• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# IPC and RPC Development
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<!--Del-->
8## Available APIs
9
10**Table 1** Native IPC APIs
11
12| Class/Interface| API                              | Description                                                            |
13|----------|  ------------------------------------ | ---------------------------------------------------------------- |
14| IRemoteBroker | sptr&lt;IRemoteObject&gt; 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 &amp;data, MessageParcel &amp;reply, MessageOption &amp;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<!--DelEnd-->
18
19## How to Develop
20
21<!--Del-->
22### **Using Native APIs**
23
241. Add dependencies.
25
26   SDK dependency:
27
28   ```
29   #IPC scenario
30   external_deps = [
31     "ipc:ipc_single",
32   ]
33
34   #RPC scenario
35   external_deps = [
36     "ipc:ipc_core",
37   ]
38   ```
39
40   The refbase implementation on which IPC/RPC depends is in **//utils**. Add the dependency on Utils.
41
42   ```
43   external_deps = [
44     "c_utils:utils",
45   ]
46   ```
47
482. Define the IPC interface **ITestAbility**.
49
50   **ITestAbility** inherits from the IPC base class **IRemoteBroker** and defines descriptors, functions, and message code. The functions need to be implemented on both the proxy and stub.
51
52   ```c++
53   #include "iremote_broker.h"
54
55   // Define message codes.
56   const int TRANS_ID_PING_ABILITY = 5;
57
58   const std::string DESCRIPTOR = "test.ITestAbility";
59
60   class ITestAbility : public IRemoteBroker {
61   public:
62       // DECLARE_INTERFACE_DESCRIPTOR is mandatory, and the input parameter is std::u16string.
63       DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR));
64       virtual int TestPingAbility(const std::u16string &dummy) = 0; // Define functions.
65   };
66   ```
67
683. Define and implement the service provider **TestAbilityStub**.
69
70   This class is related to the IPC framework and inherits from **IRemoteStub&lt;ITestAbility&gt;**. You need to override **OnRemoteRequest** on the stub to receive requests from the proxy.
71
72   ```c++
73   #include "iability_test.h"
74   #include "iremote_stub.h"
75
76   class TestAbilityStub : public IRemoteStub<ITestAbility> {
77   public:
78       virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
79       int TestPingAbility(const std::u16string &dummy) override;
80    };
81
82   int TestAbilityStub::OnRemoteRequest(uint32_t code,
83       MessageParcel &data, MessageParcel &reply, MessageOption &option)
84   {
85       switch (code) {
86           case TRANS_ID_PING_ABILITY: {
87               std::u16string dummy = data.ReadString16();
88               int result = TestPingAbility(dummy);
89               reply.WriteInt32(result);
90               return 0;
91           }
92           default:
93               return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
94       }
95   }
96   ```
97
984. Define the **TestAbility** class that implements functions for the stub.
99
100   ```c++
101   #include "iability_server_test.h"
102
103   class TestAbility : public TestAbilityStub {
104   public:
105       int TestPingAbility(const std::u16string &dummy);
106   }
107
108   int TestAbility::TestPingAbility(const std::u16string &dummy) {
109       return 0;
110   }
111   ```
112
1135. Define and implement **TestAbilityProxy**.
114
115   This class is implemented on the proxy and inherits from **IRemoteProxy&lt;ITestAbility&gt;**. You can call **SendRequest** to send a request to the stub and expose the capabilities provided by the stub.
116
117   ```c++
118   #include "iability_test.h"
119   #include "iremote_proxy.h"
120   #include "iremote_object.h"
121
122   class TestAbilityProxy : public IRemoteProxy<ITestAbility> {
123   public:
124       explicit TestAbilityProxy(const sptr<IRemoteObject> &impl);
125       int TestPingAbility(const std::u16string &dummy) override;
126   private:
127       static inline BrokerDelegator<TestAbilityProxy> delegator_; // For use of the iface_cast macro at a later time
128   }
129
130   TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl)
131       : IRemoteProxy<ITestAbility>(impl)
132   {
133   }
134
135   int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){
136       MessageOption option;
137       MessageParcel dataParcel, replyParcel;
138       dataParcel.WriteString16(dummy);
139       int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option);
140       int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1;
141       return result;
142   }
143   ```
144
1456. Register and start an SA.
146
147   Call **AddSystemAbility** to register the **TestAbilityStub** instance of an SA with **SystemAbilityManager**. The registration parameters vary depending on whether the **SystemAbilityManager** resides on the same device as the SA.
148
149   ```c++
150   // Register the TestAbilityStub instance with the SystemAbilityManager on the same device as the SA.
151   auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
152   samgr->AddSystemAbility(saId, new TestAbility());
153
154   // Register the TestAbilityStub instance with the SystemAbilityManager on a different device.
155   auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
156   ISystemAbilityManager::SAExtraProp saExtra;
157   saExtra.isDistributed = true; // Set a distributed SA.
158   int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra);
159   ```
160
1617. Obtain the SA.
162
163   Call the **GetSystemAbility** function of the **SystemAbilityManager** class to obtain the **IRemoteObject** for the SA, and create a **TestAbilityProxy** instance.
164
165   ```c++
166   // Obtain the proxy of the SA registered on the local device.
167   sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
168   sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId);
169   sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // Use the iface_cast macro to convert the proxy to a specific type.
170
171   // Obtain the proxy of the SA registered with any other devices.
172   sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
173
174   // networkId is the device identifier and can be obtained through GetLocalNodeDeviceInfo.
175   sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId, networkId);
176   sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // Construct a proxy.
177   ```
178<!--DelEnd-->
179### **Using ArkTS APIs**
180
181> **NOTE**
182>
183> - The sample code in this topic implements communication between system applications across processes.
184>
185> - Currently, third-party applications cannot implement ServiceExtensionAbility. The UIAbility components of a third-party application can connect to the ServiceExtensionAbility provided by the system via **Context**.
186>
187> - The development applies only to the scenario, in which the client is a third-party application and the server is a system application.
188
1891. Add dependencies.
190
191   ```ts
192    // If the FA model is used, import featureAbility from @kit.AbilityKit.
193    // import { featureAbility } from '@kit.AbilityKit';
194    import { rpc } from '@kit.IPCKit';
195    ```
196
1972. Bind the desired ability.
198
199   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 **distributedDeviceManager**. <br>Then, construct the **connect** variable, and specify the callback to be invoked 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.
200
201   ```ts
202    // If the FA model is used, import featureAbility from @kit.AbilityKit.
203    // import { featureAbility } from "@kit.AbilityKit";
204    import { Want, common } from '@kit.AbilityKit';
205    import { rpc } from '@kit.IPCKit';
206    import { hilog } from '@kit.PerformanceAnalysisKit';
207    import { distributedDeviceManager } from '@kit.DistributedServiceKit';
208    import { BusinessError } from '@kit.BasicServicesKit';
209
210    let dmInstance: distributedDeviceManager.DeviceManager | undefined;
211    let proxy: rpc.IRemoteObject | undefined;
212    let connectId: number;
213
214    // Bind an ability on a single device.
215    let want: Want = {
216      // Enter the bundle name and ability name.
217      bundleName: "ohos.rpc.test.server",
218      abilityName: "ohos.rpc.test.server.ServiceAbility",
219    };
220    let connect: common.ConnectOptions = {
221      onConnect: (elementName, remoteProxy) => {
222        hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called');
223        proxy = remoteProxy;
224      },
225      onDisconnect: (elementName) => {
226        hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect');
227      },
228      onFailed: () => {
229        hilog.info(0x0000, 'testTag', 'RpcClient: onFailed');
230      }
231    };
232    // Use this method to connect to the ability in the FA model.
233    // connectId = featureAbility.connectAbility(want, connect);
234
235    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
236    // Save the connection ID, which will be used when the ability is disconnected.
237    connectId = context.connectServiceExtensionAbility(want,connect);
238
239    // Bind an ability across devices.
240    try{
241      dmInstance = distributedDeviceManager.createDeviceManager("ohos.rpc.test");
242    } catch(error) {
243      let err: BusinessError = error as BusinessError;
244      hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message);
245    }
246
247    // Use distributedDeviceManager to obtain the network ID of the target device.
248    if (dmInstance != undefined) {
249      let deviceList = dmInstance.getAvailableDeviceListSync();
250      let networkId = deviceList[0].networkId;
251      let want: Want = {
252        bundleName: "ohos.rpc.test.server",
253        abilityName: "ohos.rpc.test.service.ServiceAbility",
254        deviceId: networkId,
255        flags: 256
256      };
257      // Save the connection ID, which will be used when the ability is disconnected.
258      // Use this method to connect to the ability in the FA model.
259      // connectId = featureAbility.connectAbility(want, connect);
260
261      // The first parameter specifies the bundle name of the application, and the second parameter specifies the callback used to return the network ID obtained by using distributedDeviceManager.
262      connectId = context.connectServiceExtensionAbility(want,connect);
263    }
264   ```
265
2663. Process requests sent from the client.
267
268   Call **onConnect()** to return a proxy object inherited from [rpc.RemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#remoteobject) after the ability is successfully connected. Implement [onRemoteMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#onremotemessagerequest9) for the proxy object to process requests sent from the client.
269
270   ```ts
271    import { rpc } from '@kit.IPCKit';
272    import { Want } from '@kit.AbilityKit';
273    class Stub extends rpc.RemoteObject {
274      constructor(descriptor: string) {
275        super(descriptor);
276      }
277      onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> {
278        // Process requests sent from the client based on the code.
279        return true;
280      }
281
282      onConnect(want: Want) {
283        const robj: rpc.RemoteObject = new Stub("rpcTestAbility");
284        return robj;
285      }
286    }
287   ```
288
2894. Process responses sent from the server.
290
291   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).
292
293   ```ts
294    import { rpc } from '@kit.IPCKit';
295    import { hilog } from '@kit.PerformanceAnalysisKit';
296
297    // Use a promise.
298    let option = new rpc.MessageOption();
299    let data = rpc.MessageSequence.create();
300    let reply = rpc.MessageSequence.create();
301    // Write parameters to data.
302    let proxy: rpc.IRemoteObject | undefined;
303    if (proxy != undefined) {
304      proxy.sendMessageRequest(1, data, reply, option)
305        .then((result: rpc.RequestResult) => {
306          if (result.errCode != 0) {
307            hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);
308            return;
309          }
310          // Read the result from result.reply.
311        })
312        .catch((e: Error) => {
313          hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e);
314        })
315        .finally(() => {
316          data.reclaim();
317          reply.reclaim();
318        })
319    }
320
321    // Use a callback.
322    function sendRequestCallback(err: Error, result: rpc.RequestResult) {
323      try {
324        if (result.errCode != 0) {
325          hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);
326          return;
327        }
328        // Read the result from result.reply.
329      } finally {
330          result.data.reclaim();
331          result.reply.reclaim();
332      }
333    }
334    let options = new rpc.MessageOption();
335    let datas = rpc.MessageSequence.create();
336    let replys = rpc.MessageSequence.create();
337    // Write parameters to data.
338    if (proxy != undefined) {
339      proxy.sendMessageRequest(1, datas, replys, options, sendRequestCallback);
340    }
341   ```
342
3435. Tear down the connection.
344
345   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.
346
347   ```ts
348    // If the FA model is used, import featureAbility from @kit.AbilityKit.
349    // import { featureAbility } from "@kit.AbilityKit";
350    import { Want, common } from '@kit.AbilityKit';
351    import { rpc } from '@kit.IPCKit';
352    import { hilog } from '@kit.PerformanceAnalysisKit';
353
354    function disconnectCallback() {
355      hilog.info(0x0000, 'testTag', 'disconnect ability done');
356    }
357    // Use this method to disconnect from the ability in the FA model.
358    // featureAbility.disconnectAbility(connectId, disconnectCallback);
359
360    let proxy: rpc.IRemoteObject | undefined;
361    let connectId: number;
362
363    // Bind an ability on a single device.
364    let want: Want = {
365      // Enter the bundle name and ability name.
366      bundleName: "ohos.rpc.test.server",
367      abilityName: "ohos.rpc.test.server.ServiceAbility",
368    };
369    let connect: common.ConnectOptions = {
370      onConnect: (elementName, remote) => {
371        proxy = remote;
372      },
373      onDisconnect: (elementName) => {
374      },
375      onFailed: () => {
376        proxy;
377      }
378    };
379    // Use this method to connect to the ability in the FA model.
380    // connectId = featureAbility.connectAbility(want, connect);
381
382    connectId = this.context.connectServiceExtensionAbility(want,connect);
383
384    this.context.disconnectServiceExtensionAbility(connectId);
385   ```
386
387##
388
389
390
391-
392