• 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| API                              | Description                                                            |
13| ------------------------------------ | ---------------------------------------------------------------- |
14| 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| 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 ArkTS 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 hilog from '@ohos.hilog';
194    import AbilityConstant from '@ohos.app.ability.AbilityConstant';
195    import window from '@ohos.window';
196
197    export default class MainAbility extends UIAbility {
198      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
199        hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onCreate');
200        let context = this.context;
201      }
202      onDestroy() {
203        hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onDestroy');
204      }
205      onWindowStageCreate(windowStage: window.WindowStage) {
206        // Main window is created, set main page for this ability
207	  	hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageCreate');
208      }
209      onWindowStageDestroy() {
210        // Main window is destroyed, release UI related resources
211	  	hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageDestroy');
212      }
213      onForeground() {
214        // Ability has brought to foreground
215        hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onForeground');
216      }
217      onBackground() {
218        // Ability has back to background
219        hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onBackground');
220      }
221    }
222   ```
223
2242. Bind the desired ability.
225
226   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.
227
228   ```ts
229    // Import @ohos.ability.featureAbility only for the application developed based on the FA model.
230    // import featureAbility from "@ohos.ability.featureAbility";
231    import rpc from '@ohos.rpc';
232    import Want from '@ohos.app.ability.Want';
233    import common from '@ohos.app.ability.common';
234    import hilog from '@ohos.hilog';
235    import deviceManager from '@ohos.distributedDeviceManager';
236    import { BusinessError } from '@ohos.base';
237
238    let dmInstance: deviceManager.DeviceManager | undefined;
239    let proxy: rpc.IRemoteObject | undefined;
240    let connectId: number;
241
242    // Bind the ability on a single device.
243    let want: Want = {
244      // Enter the bundle name and ability name.
245      bundleName: "ohos.rpc.test.server",
246      abilityName: "ohos.rpc.test.server.ServiceAbility",
247    };
248    let connect: common.ConnectOptions = {
249      onConnect: (elementName, remoteProxy) => {
250        hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called');
251        proxy = remoteProxy;
252      },
253      onDisconnect: (elementName) => {
254        hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect');
255      },
256      onFailed: () => {
257        hilog.info(0x0000, 'testTag', 'RpcClient: onFailed');
258      }
259    };
260    // Use this method to connect to the ability in the FA model.
261    // connectId = featureAbility.connectAbility(want, connect);
262
263    connectId = this.context.connectServiceExtensionAbility(want,connect);
264
265    // Cross-device binding
266    try{
267      dmInstance = deviceManager.createDeviceManager("ohos.rpc.test");
268    } catch(error) {
269      let err: BusinessError = error as BusinessError;
270      hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message);
271    }
272
273    // Use deviceManager to obtain the network ID of the target device.
274    if (dmInstance != undefined) {
275      let deviceList = dmInstance.getAvailableDeviceListSync();
276      let networkId = deviceList[0].networkId;
277      let want: Want = {
278        bundleName: "ohos.rpc.test.server",
279        abilityName: "ohos.rpc.test.service.ServiceAbility",
280        deviceId: networkId,
281        flags: 256
282      };
283      // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection.
284      // Use this method to connect to the ability in the FA model.
285      // connectId = featureAbility.connectAbility(want, connect);
286
287      // 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.
288      connectId = this.context.connectServiceExtensionAbility(want,connect);
289    }
290   ```
291
2923. Process requests sent from the client.
293
294   Call the **onConnect** API to return a proxy object inherited from [rpc.RemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#remoteobject) after the ability is successfully bound. Implement the [onRemoteMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#onremotemessagerequest9) API for the proxy object to process requests sent from the client.
295
296   ```ts
297    import rpc from '@ohos.rpc';
298    import Want from '@ohos.app.ability.Want';
299    class Stub extends rpc.RemoteObject {
300      constructor(descriptor: string) {
301        super(descriptor);
302      }
303      onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> {
304        // Process requests sent from the client based on the code.
305        return true;
306      }
307
308      onConnect(want: Want) {
309        const robj: rpc.RemoteObject = new Stub("rpcTestAbility");
310        return robj;
311      }
312    }
313   ```
314
3154. Process responses sent from the server.
316
317   Receive the proxy object in the **onConnect** callback, call [sendMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#sendmessagerequest9-2) 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).
318
319   ```ts
320    import rpc from '@ohos.rpc';
321    import hilog from '@ohos.hilog';
322
323    // Use a promise.
324    let option = new rpc.MessageOption();
325    let data = rpc.MessageSequence.create();
326    let reply = rpc.MessageSequence.create();
327    // Write parameters to data.
328    let proxy: rpc.IRemoteObject | undefined;
329    if (proxy != undefined) {
330      proxy.sendMessageRequest(1, data, reply, option)
331        .then((result: rpc.RequestResult) => {
332          if (result.errCode != 0) {
333            hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);
334            return;
335          }
336          // Read the result from result.reply.
337        })
338        .catch((e: Error) => {
339          hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e);
340        })
341        .finally(() => {
342          data.reclaim();
343          reply.reclaim();
344        })
345    }
346
347    // Use a callback.
348    function sendRequestCallback(err: Error, result: rpc.RequestResult) {
349      try {
350        if (result.errCode != 0) {
351          hilog.error(0x0000, 'testTag', 'sendMessageRequest 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.MessageSequence.create();
362    let replys = rpc.MessageSequence.create();
363    // Write parameters to data.
364    if (proxy != undefined) {
365      proxy.sendMessageRequest(1, datas, replys, options, sendRequestCallback);
366    }
367   ```
368
3695. Tear down the connection.
370
371   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.
372
373   ```ts
374    import rpc from '@ohos.rpc';
375    import Want from '@ohos.app.ability.Want';
376    import hilog from '@ohos.hilog';
377    import common from '@ohos.app.ability.common';
378    // Import @ohos.ability.featureAbility only for the application developed based on the FA model.
379    // import featureAbility from "@ohos.ability.featureAbility";
380
381    function disconnectCallback() {
382      hilog.info(0x0000, 'testTag', 'disconnect ability done');
383    }
384    // Use this method to disconnect from the ability in the FA model.
385    // featureAbility.disconnectAbility(connectId, disconnectCallback);
386
387    let proxy: rpc.IRemoteObject | undefined;
388    let connectId: number;
389
390    // Bind the ability on a single device.
391    let want: Want = {
392      // Enter the bundle name and ability name.
393      bundleName: "ohos.rpc.test.server",
394      abilityName: "ohos.rpc.test.server.ServiceAbility",
395    };
396    let connect: common.ConnectOptions = {
397      onConnect: (elementName, remote) => {
398        proxy = remote;
399      },
400      onDisconnect: (elementName) => {
401      },
402      onFailed: () => {
403        proxy;
404      }
405    };
406    // Use this method to connect to the ability in the FA model.
407    // connectId = featureAbility.connectAbility(want, connect);
408
409    connectId = this.context.connectServiceExtensionAbility(want,connect);
410
411    this.context.disconnectServiceExtensionAbility(connectId);
412   ```
413
414<!--no_check-->