• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# IPC & RPC Development Guidelines
2
3## When to Use
4
5IPC/RPC enables a proxy and a stub that run on different processes to communicate with each other, regardless of whether they run on the same or different devices.
6
7
8## Available APIs
9
10Table 1 Native IPC APIs
11
12| Class| API| Description|
13| -------- | -------- | -------- |
14| [IRemoteBroker](../reference/apis/js-apis-rpc.md#iremotebroker) | sptr<IRemoteObject> AsObject() | Obtains the holder of a remote proxy object. If you call this API on the stub, the **RemoteObject** is returned; if you call this API on the proxy, the proxy object is returned.|
15| IRemoteStub | virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) | Called to process a request from the proxy and return the result. Derived classes need to override this API.|
16| IRemoteProxy | Remote()->SendRequest(code, data, reply, option)             | Sends a request to the peer end. Service proxy classes are derived from the **IRemoteProxy** class.|
17
18
19## How to Develop
20
21### **Using Native APIs**
22
231. Add dependencies.
24
25   SDK dependency:
26
27   ```
28   #IPC scenario
29   external_deps = [
30     "ipc:ipc_single",
31   ]
32
33   #RPC scenario
34   external_deps = [
35     "ipc:ipc_core",
36   ]
37   ```
38
39   In addition, the refbase implementation on which IPC/RPC depends is stored in **//utils**. Add the dependency on the Utils source code.
40
41   ```
42   external_deps = [
43     "c_utils:utils",
44   ]
45   ```
46
472. Define the IPC interface **ITestAbility**.
48
49   **ITestAbility** inherits the IPC base class **IRemoteBroker** and defines descriptors, functions, and message code. The functions need to be implemented on both the proxy and stub.
50
51   ```c++
52   #include "iremote_broker.h"
53
54   // Define message codes.
55   const int TRANS_ID_PING_ABILITY = 5;
56
57   const std::string DESCRIPTOR = "test.ITestAbility";
58
59   class ITestAbility : public IRemoteBroker {
60   public:
61       // DECLARE_INTERFACE_DESCRIPTOR is mandatory, and the input parameter is std::u16string.
62       DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR));
63       virtual int TestPingAbility(const std::u16string &dummy) = 0; // Define functions.
64   };
65   ```
66
673. Define and implement service provider **TestAbilityStub**.
68
69   This class is related to the IPC framework and needs to inherit **IRemoteStub<ITestAbility>**. You need to override **OnRemoteRequest** on the stub to receive requests from the proxy.
70
71   ```c++
72   #include "iability_test.h"
73   #include "iremote_stub.h"
74
75   class TestAbilityStub : public IRemoteStub<ITestAbility> {
76   public:
77       virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
78       int TestPingAbility(const std::u16string &dummy) override;
79    };
80
81   int TestAbilityStub::OnRemoteRequest(uint32_t code,
82       MessageParcel &data, MessageParcel &reply, MessageOption &option)
83   {
84       switch (code) {
85           case TRANS_ID_PING_ABILITY: {
86               std::u16string dummy = data.ReadString16();
87               int result = TestPingAbility(dummy);
88               reply.WriteInt32(result);
89               return 0;
90           }
91           default:
92               return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
93       }
94   }
95   ```
96
974. Define the **TestAbility** class that implements functions for the stub.
98
99   ```c++
100   #include "iability_server_test.h"
101
102   class TestAbility : public TestAbilityStub {
103   public:
104       int TestPingAbility(const std::u16string &dummy);
105   }
106
107   int TestAbility::TestPingAbility(const std::u16string &dummy) {
108       return 0;
109   }
110   ```
111
1125. Define and implement **TestAbilityProxy**.
113
114   This class is implemented on the proxy and inherits **IRemoteProxy&lt;ITestAbility&gt;**. You can call **SendRequest** to send a request to the stub and expose the capabilities provided by the stub.
115
116   ```c++
117   #include "iability_test.h"
118   #include "iremote_proxy.h"
119   #include "iremote_object.h"
120
121   class TestAbilityProxy : public IRemoteProxy<ITestAbility> {
122   public:
123       explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);
124       int TestPingAbility(const std::u16string &dummy) override;
125   private:
126       static inline BrokerDelegator<TestAbilityProxy> delegator_; // For use of the iface_cast macro at a later time
127   }
128
129   TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl)
130       : IRemoteProxy<ITestAbility>(impl)
131   {
132   }
133
134   int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){
135       MessageOption option;
136       MessageParcel dataParcel, replyParcel;
137       dataParcel.WriteString16(dummy);
138       int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option);
139       int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1;
140       return result;
141   }
142   ```
143
1446. Register and start an SA.
145
146   Call **AddSystemAbility** to register the **TestAbilityStub** instance of the SA with **SystemAbilityManager**. The registration parameters vary depending on whether the **SystemAbilityManager** resides on the same device as the SA.
147
148   ```c++
149   // Register the TestAbilityStub instance with the SystemAbilityManager on the same device as the SA.
150   auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
151   samgr->AddSystemAbility(saId, new TestAbility());
152
153   // Register the TestAbilityStub instance with the SystemAbilityManager on a different device.
154   auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
155   ISystemAbilityManager::SAExtraProp saExtra;
156   saExtra.isDistributed = true; // Set a distributed SA.
157   int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra);
158   ```
159
1607. Obtain the SA.
161
162   Call the **GetSystemAbility** function of the **SystemAbilityManager** class to obtain the **IRemoteObject** for the SA, and create a **TestAbilityProxy** instance.
163
164   ```c++
165   // Obtain the proxy of the SA registered on the local device.
166   sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
167   sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId);
168   sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // Use the iface_cast macro to convert the proxy to a specific type.
169
170   // Obtain the proxy of the SA registered with any other devices.
171   sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
172
173   // networkId is the device identifier and can be obtained through GetLocalNodeDeviceInfo.
174   sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId, networkId);
175   sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // Construct a proxy.
176   ```
177
178### **Using JS APIs**
179
1801. Add dependencies.
181
182   ```ts
183   import rpc from '@ohos.rpc';
184   // Import @ohos.ability.featureAbility only for the application developed based on the FA model.
185   // import featureAbility from "@ohos.ability.featureAbility";
186   ```
187
188   If you use the stage model, you need to obtain the context. The sample code is as follows:
189
190   ```ts
191   import UIAbility from '@ohos.app.ability.UIAbility';
192   import Want from '@ohos.app.ability.Want';
193   import AbilityConstant from '@ohos.app.ability.AbilityConstant';
194   import window from '@ohos.window';
195
196   export default class MainAbility extends UIAbility {
197       onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
198           console.log("[Demo] MainAbility onCreate");
199           let context = this.context;
200       }
201       onDestroy() {
202           console.log("[Demo] MainAbility onDestroy");
203       }
204       onWindowStageCreate(windowStage: window.WindowStage) {
205           // Main window is created, set main page for this ability
206           console.log("[Demo] MainAbility onWindowStageCreate");
207       }
208       onWindowStageDestroy() {
209           // Main window is destroyed, release UI related resources
210           console.log("[Demo] MainAbility onWindowStageDestroy");
211       }
212       onForeground() {
213           // Ability has brought to foreground
214           console.log("[Demo] MainAbility onForeground");
215       }
216       onBackground() {
217           // Ability has back to background
218           console.log("[Demo] MainAbility onBackground");
219       }
220   }
221   ```
222
2232. Bind the desired ability.
224
225   Construct the **want** variable, and specify the bundle name and component name of the application where the ability is located. If cross-device communication is involved, also specify the network ID of the target device, which can be obtained through **deviceManager**. Then, construct the **connect** variable, and specify the callback that is called when the binding is successful, the binding fails, or the ability is disconnected. If you use the FA model, call the API provided by **featureAbility** to bind an ability. If you use the stage model, obtain a service instance through **Context**, and then call the API provided by **featureAbility** to bind an ability.
226
227   ```ts
228   // Import @ohos.ability.featureAbility only for the application developed based on the FA model.
229   // import featureAbility from "@ohos.ability.featureAbility";
230   import rpc from '@ohos.rpc';
231   import Want from '@ohos.app.ability.Want';
232   import common from '@ohos.app.ability.common';
233   import deviceManager from '@ohos.distributedHardware.deviceManager';
234   import { BusinessError } from '@ohos.base';
235
236   let dmInstance: deviceManager.DeviceManager | undefined;
237   let proxy: rpc.IRemoteObject | undefined = undefined;
238   let connectId: number;
239
240   // Bind the ability on a single device.
241   let want: Want = {
242       // Enter the bundle name and ability name.
243       bundleName: "ohos.rpc.test.server",
244       abilityName: "ohos.rpc.test.server.ServiceAbility",
245   };
246   let connect: common.ConnectOptions = {
247       onConnect: (elementName, remote) => {
248           proxy = remote;
249       },
250       onDisconnect: (elementName) => {
251       },
252       onFailed: () => {
253           proxy;
254       }
255   };
256   // Use this method to connect to the ability in the FA model.
257   // connectId = featureAbility.connectAbility(want, connect);
258
259   connectId = this.context.connectServiceExtensionAbility(want,connect);
260
261   // Cross-device binding
262   let deviceManagerCallback = (err: BusinessError, data: deviceManager.DeviceManager) => {
263       if (err) {
264           console.error("createDeviceManager errCode:" + err.code + ",errMessage:" + err.message);
265           return;
266       }
267       console.info("createDeviceManager success");
268       dmInstance = data;
269   }
270   try{
271       deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback);
272   } catch(error) {
273       let err: BusinessError = error as BusinessError;
274       console.error("createDeviceManager errCode:" + err.code + ",errMessage:" + err.message);
275   }
276
277   // Use deviceManager to obtain the network ID of the target device.
278   if (dmInstance != undefined) {
279       let deviceList: Array<deviceManager.DeviceInfo> = dmInstance.getTrustedDeviceListSync();
280       let networkId: string = deviceList[0].networkId;
281       let want: Want = {
282           bundleName: "ohos.rpc.test.server",
283           abilityName: "ohos.rpc.test.service.ServiceAbility",
284           deviceId: networkId,
285           flags: 256
286       };
287       // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection.
288       // Use this method to connect to the ability in the FA model.
289       // connectId = featureAbility.connectAbility(want, connect);
290
291       // The first parameter specifies the bundle name of the application, and the second parameter specifies the callback used to return the device ID obtained by using DeviceManager.
292       connectId = this.context.connectServiceExtensionAbility(want,connect);
293   }
294   ```
295
2963. Process requests sent from the client.
297
298   Call the **onConnect** API to return a proxy object inherited from **rpc.RemoteObject** after the ability is successfully bound. Implement the **onRemoteMessageRequest** API for the proxy object to process requests sent from the client.
299
300   ```ts
301    import rpc from '@ohos.rpc';
302    import Want from '@ohos.app.ability.Want';
303    class Stub extends rpc.RemoteObject {
304       constructor(descriptor: string) {
305           super(descriptor);
306       }
307       onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> {
308           // Process requests sent from the client based on the code.
309           return true;
310       }
311
312       onConnect(want: Want) {
313           const robj: rpc.RemoteObject = new Stub("rpcTestAbility");
314           return robj;
315       }
316    }
317   ```
318
3194. Process responses sent from the server.
320
321   Obtain the proxy object from the **onConnect** callback, call **sendRequest** to send a request, and receive the response using a callback or a promise (an object representing the eventual completion or failure of an asynchronous operation and its result value).
322
323   ```ts
324   import rpc from '@ohos.rpc';
325   // Use a promise.
326   let option = new rpc.MessageOption();
327   let data = rpc.MessageParcel.create();
328   let reply = rpc.MessageParcel.create();
329   // Write parameters to data.
330   let proxy: rpc.IRemoteObject | undefined = undefined;
331   proxy.sendRequest(1, data, reply, option)
332       .then((result: rpc.SendRequestResult) => {
333           if (result.errCode != 0) {
334               console.error("send request failed, errCode: " + result.errCode);
335               return;
336           }
337           // Read the result from result.reply.
338       })
339       .catch((e: Error) => {
340           console.error("send request got exception: " + e);
341       })
342       .finally(() => {
343           data.reclaim();
344           reply.reclaim();
345       })
346
347   // Use a callback.
348   function sendRequestCallback(result: rpc.SendRequestResult) {
349       try {
350           if (result.errCode != 0) {
351               console.error("send request failed, errCode: " + result.errCode);
352               return;
353           }
354           // Read the result from result.reply.
355       } finally {
356           result.data.reclaim();
357           result.reply.reclaim();
358       }
359   }
360   let options = new rpc.MessageOption();
361   let datas = rpc.MessageParcel.create();
362   let replys = rpc.MessageParcel.create();
363   // Write parameters to data.
364   proxy.sendRequest(1, datas, replys, options, sendRequestCallback);
365   ```
366
3675. Tear down the connection.
368
369   If you use the FA model, call the API provided by **featureAbility** to tear down the connection when the communication is over. If you use the stage model, obtain a service instance through **Context**, and then call the API provided by **featureAbility** to tear down the connection.
370
371   ```ts
372   import rpc from "@ohos.rpc";
373   import Want from '@ohos.app.ability.Want';
374   import common from '@ohos.app.ability.common';
375   // Import @ohos.ability.featureAbility only for the application developed based on the FA model.
376   // import featureAbility from "@ohos.ability.featureAbility";
377
378   function disconnectCallback() {
379     console.info("disconnect ability done");
380   }
381   // Use this method to disconnect from the ability in the FA model.
382   // featureAbility.disconnectAbility(connectId, disconnectCallback);
383
384   let proxy: rpc.IRemoteObject | undefined = undefined;
385   let connectId: number;
386
387   // Bind the ability on a single device.
388   let want: Want = {
389     // Enter the bundle name and ability name.
390     bundleName: "ohos.rpc.test.server",
391     abilityName: "ohos.rpc.test.server.ServiceAbility",
392   };
393   let connect: common.ConnectOptions = {
394     onConnect: (elementName, remote) => {
395       proxy = remote;
396     },
397     onDisconnect: (elementName) => {
398     },
399     onFailed: () => {
400       proxy;
401     }
402   };
403   // Use this method to connect to the ability in the FA model.
404   // connectId = featureAbility.connectAbility(want, connect);
405
406   connectId = this.context.connectServiceExtensionAbility(want,connect);
407
408   this.context.disconnectServiceExtensionAbility(connectId);
409   ```
410