• 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| API                              | Description                                                            |
13| ------------------------------------ | ---------------------------------------------------------------- |
14| 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| 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   In addition, the refbase implementation on which IPC/RPC depends is stored in **//utils**. Add the dependency on the Utils source code.
41
42   ```
43   external_deps = [
44     "c_utils:utils",
45   ]
46   ```
47
482. Define the IPC interface **ITestAbility**.
49
50   **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.
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 service provider **TestAbilityStub**.
69
70   This class is related to the IPC framework and needs to inherit **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 **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 the 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. Connect to 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**. Then, construct the **connect** variable, and specify the callbacks to be invoked when the ability is connected, disconnect, and the connection fails. If you use the FA model, call the API provided by **featureAbility** to connect to an ability. If you use the stage model, obtain a service instance through **Context**, and then call the API provided by **featureAbility** to connect to 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    // Connect to 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    // Connect to 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    // Connect to 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