• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# IPC and RPC Development (C/C++)
2<!--Kit: IPC Kit-->
3<!--Subsystem: Communication-->
4<!--Owner: @xdx19211@luodonghui0157-->
5<!--SE: @zhaopeng_gitee-->
6<!--TSE: @maxiaorong2-->
7
8
9## When to Use
10
11
12Inter-process communication (IPC) allows the proxy and stub running in different processes to communicate with each other using C/C++ APIs.
13The IPC C APIs do not provide the capability of obtaining the communication proxy object. This feature depends on [Ability Kit](../application-models/abilitykit-overview.md).
14
15![](./figures/_i_p_c_architecture_diagram.png)
16
17For details about how to establish IPC channels between processes, see [Native Child Process Development (C/C++)](../application-models/capi_nativechildprocess_development_guideline.md). This document describes how to use the IPC C APIs.
18
19## Available APIs
20
21**Table 1** Key APIs
22
23| API                              | Description                                                            |
24| ------------------------------------ | ---------------------------------------------------------------- |
25|typedef int (\*OH_OnRemoteRequestCallback)<br>(uint32_t code, const OHIPCParcel \*data, OHIPCParcel \*reply,<br> void \*userData);|Called to process the peer request at the stub.|
26| OHIPCRemoteStub\* OH_IPCRemoteStub_Create<br>(const char \*descriptor, OH_OnRemoteRequestCallback requestCallback,<br>OH_OnRemoteDestroyCallback destroyCallback, void \*userData); | Creates an **OHIPCRemoteStub** object.|
27|int OH_IPCRemoteProxy_SendRequest(const OHIPCRemoteProxy \*proxy,<br> uint32_t code, const OHIPCParcel \*data, OHIPCParcel \*reply,<br> const OH_IPC_MessageOption \*option);|Sends an IPC message.|
28|struct OHIPCRemoteProxy;|Defines an **OHIPCRemoteProxy** object, which is used to send requests to the peer end. The **OHIPCRemoteProxy** object is returned by an ability API.|
29|OHIPCDeathRecipient\* OH_IPCDeathRecipient_Create<br>(OH_OnDeathRecipientCallback deathRecipientCallback,<br> OH_OnDeathRecipientDestroyCallback destroyCallback,<br>void \*userData);|Creates an **OHIPCRemoteStub** object, which triggers a notification when the **OHIPCDeathRecipient** object dies unexpectedly.|
30|int OH_IPCRemoteProxy_AddDeathRecipient(OHIPCRemoteProxy \*proxy,<br>OHIPCDeathRecipient \*recipient);|Subscribes to the death of an **OHIPCRemoteStub** object for an **OHIPCRemoteProxy** object.|
31
32For details about the APIs, see [IPC Kit](../reference/apis-ipc-kit/capi-ipckit.md).
33
34
35## How to Develop
36
37Create a stub object on the server, obtain the proxy object of the client through ability APIs, and use the proxy object to communicate with the stub object on the server through IPC. Then, register the death notification callback of the remote object to detect the death status of the process hosting the remote stub object.
38
39### Adding Dynamic Link Libraries
40
41Add the following libraries to **CMakeLists.txt**.
42
43```txt
44# ipc capi
45libipc_capi.so
46# Ability C/C++ APIs
47libchild_process.so
48```
49
50### Adding Header Files
51
52```c++
53// IPC C/C++ APIs
54#include <IPCKit/ipc_kit.h>
55// Ability C/C++ APIs
56#include <AbilityKit/native_child_process.h>
57```
58
59### Implementing IPC
60#### Common Data and Functions
61
62```c++
63#include <string>
64#include <thread>
65#include <mutex>
66#include <chrono>
67#include <condition_variable>
68#include <IPCKit/ipc_kit.h>
69#include <AbilityKit/native_child_process.h>
70#include <hilog/log.h>
71#undef LOG_DOMAIN
72#undef LOG_TAG
73#define LOG_DOMAIN 0x0201
74#define LOG_TAG "IPCCApiSample"
75
76enum RequestCode {
77    ASYNC_ADD_CODE = 1,
78    REQUEST_EXIT_CODE = 2,
79    OTHER_CODE
80};
81static constexpr int MAX_MEMORY_SIZE = 204800;
82static const std::string INTERFACE_DESCRIPTOR = "INTERFACE_DESCRIPTOR";
83static const std::string NATIVE_REMOTE_STUB_TEST_TOKEN = "native.remote.stub";
84static const std::string NATIVE_REMOTE_STUB_ASYNC_CALL_TEST_TOKEN = "native.remote.stub.async.call";
85
86// Define the memory allocation function.
87static void* LocalMemoryAllocator(int32_t len) {
88    if (len < 0 || len > MAX_MEMORY_SIZE ) {
89        return nullptr;
90    }
91    void *buffer = malloc(len);
92    if (buffer == nullptr) {
93        return nullptr;
94    }
95    memset(buffer, 0, len);
96    return buffer;
97}
98```
99#### Server Object IpcCApiStubTest
100
101```c++
102class IpcCApiStubTest {
103public:
104    explicit IpcCApiStubTest();
105    ~IpcCApiStubTest();
106    void MainProc();
107    OHIPCRemoteStub* GetRemoteStub();
108    static int OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData);
109private:
110    int AsyncAdd(const OHIPCParcel *data);
111    int RequestExitChildProcess();
112private:
113    OHIPCRemoteStub *stub_{ nullptr };
114    std::mutex childMutex_;
115    std::condition_variable childCondVar_;
116};
117
118IpcCApiStubTest::IpcCApiStubTest() {
119    // Create a stub object.
120    stub_ = OH_IPCRemoteStub_Create(INTERFACE_DESCRIPTOR.c_str(), &IpcCApiStubTest::OnRemoteRequest,
121        nullptr, this);
122}
123
124IpcCApiStubTest::~IpcCApiStubTest() {
125    if (stub_ != nullptr) {
126        // Destroy the stub object when it is no longer used.
127        OH_IPCRemoteStub_Destroy(stub_);
128    }
129}
130
131void IpcCApiStubTest::MainProc() {
132    std::unique_lock<std::mutex> autoLock(childMutex_);
133    childCondVar_.wait(autoLock);
134}
135
136OHIPCRemoteStub* IpcCApiStubTest::GetRemoteStub() {
137    return stub_;
138}
139
140// Request processing function of the server. Requests sent by the client are processed in this function.
141int IpcCApiStubTest::OnRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData) {
142    int readLen = 0;
143    char *token = nullptr;
144    // Verify the current communication based on interfaceToken sent by the client.
145    if (OH_IPCParcel_ReadInterfaceToken(data, &token, &readLen, LocalMemoryAllocator) != OH_IPC_SUCCESS
146        || NATIVE_REMOTE_STUB_TEST_TOKEN != token) {
147        if (token != nullptr) {
148            OH_LOG_ERROR(LOG_APP, "check InterfaceToken failed");
149            free(token);
150        }
151        return OH_IPC_PARCEL_WRITE_ERROR;
152    }
153    free(token);
154    auto *stubTest = reinterpret_cast<IpcCApiStubTest *>(userData);
155    if (stubTest == nullptr) {
156        return OH_IPC_CHECK_PARAM_ERROR;
157    }
158    auto rqCode = RequestCode(code);
159    switch (rqCode) {
160        case ASYNC_ADD_CODE: {
161            return stubTest->AsyncAdd(data);
162        }
163        case REQUEST_EXIT_CODE: {
164            return stubTest->RequestExitChildProcess();
165        }
166        default:
167            break;
168    }
169    return OH_IPC_SUCCESS;
170}
171
172int IpcCApiStubTest::AsyncAdd(const OHIPCParcel *data) {
173    int a = 0;
174    int b = 0;
175    OH_LOG_INFO(LOG_APP, "start async add a=%d,b=%d", a, b);
176    if ((OH_IPCParcel_ReadInt32(data, &a) != OH_IPC_SUCCESS)
177        || (OH_IPCParcel_ReadInt32(data, &b) != OH_IPC_SUCCESS)) {
178        return OH_IPC_PARCEL_READ_ERROR;
179    }
180    // Obtain the proxy object for IPC.
181    auto proxyCallBack = OH_IPCParcel_ReadRemoteProxy(data);
182    if (proxyCallBack == nullptr) {
183        return OH_IPC_PARCEL_READ_ERROR;
184    }
185    OH_LOG_INFO(LOG_APP, "start create sendCallBack thread!");
186    // Define asynchronous thread processing and use proxyCallBack to respond to asynchronous results. To implement synchronous calls, use replyData to write the response result.
187    std::thread th([proxyCallBack, a, b] {
188        auto data = OH_IPCParcel_Create();
189        if (data == nullptr) {
190            // If the parcel fails to be created, destroy the obtained proxyCallBack object.
191            OH_IPCRemoteProxy_Destroy(proxyCallBack);
192            return;
193        }
194        auto reply = OH_IPCParcel_Create();
195        if (reply == nullptr) {
196            OH_IPCParcel_Destroy(data);
197            OH_IPCRemoteProxy_Destroy(proxyCallBack);
198            return;
199        }
200        if (OH_IPCParcel_WriteInt32(data, a + b) != OH_IPC_SUCCESS) {
201            OH_IPCParcel_Destroy(data);
202            OH_IPCParcel_Destroy(reply);
203            OH_IPCRemoteProxy_Destroy(proxyCallBack);
204            return;
205        }
206        // The asynchronous thread processing result is returned to the service requester in IPC synchronous calls.
207        OH_IPC_MessageOption option = { OH_IPC_REQUEST_MODE_SYNC, 0 };
208        OH_LOG_INFO(LOG_APP, "thread start sendCallBack!");
209        // Send an IPC request.
210        int ret = OH_IPCRemoteProxy_SendRequest(proxyCallBack, ASYNC_ADD_CODE, data, reply, &option);
211        OH_LOG_INFO(LOG_APP, "thread sendCallBack ret = %d", ret);
212        if (ret != OH_IPC_SUCCESS) {
213            OH_IPCParcel_Destroy(data);
214            OH_IPCParcel_Destroy(reply);
215            OH_IPCRemoteProxy_Destroy(proxyCallBack);
216            return;
217        }
218        OH_IPCRemoteProxy_Destroy(proxyCallBack);
219        OH_IPCParcel_Destroy(data);
220        OH_IPCParcel_Destroy(reply);
221    });
222    th.detach();
223    return OH_IPC_SUCCESS;
224}
225
226int IpcCApiStubTest::RequestExitChildProcess() {
227    std::unique_lock<std::mutex> autoLock(childMutex_);
228    childCondVar_.notify_all();
229    return OH_IPC_SUCCESS;
230}
231```
232
233#### Client Proxy Object IpcCApiProxyTest
234
235```cpp
236// Customize error codes.
237static constexpr int OH_IPC_CREATE_OBJECT_ERROR = OH_IPC_USER_ERROR_CODE_MIN + 1;
238
239class IpcCApiProxyTest {
240public:
241    explicit IpcCApiProxyTest(OHIPCRemoteProxy *proxy);
242    ~IpcCApiProxyTest();
243public:
244    int AsyncAdd(int a, int b, int &result);
245    int RequestExitChildProcess();
246    void ClearResource();
247private:
248    void SendAsyncReply(int &replyValue);
249    int WaitForAsyncReply(int timeOut);
250    // Note: OnRemoteRequest is the callback function used by the stub object to process IPC requests. This function does not need to be implemented on the proxy side.
251    // OnRemoteRequest is the callback function used by the asynchronous callback object (replyStub_) to process IPC requests.
252    static int OnRemoteRequest(uint32_t code, const OHIPCParcel *data,
253        OHIPCParcel *reply, void *userData);
254    static void OnDeathRecipientCB(void *userData);
255private:
256    int asyncReply_{};
257    std::mutex mutex_;
258    std::condition_variable cv_;
259    OHIPCRemoteProxy *proxy_{ nullptr };
260    OHIPCRemoteStub *replyStub_{ nullptr };
261    OHIPCDeathRecipient *deathRecipient_{ nullptr };
262};
263
264IpcCApiProxyTest::IpcCApiProxyTest(OHIPCRemoteProxy *proxy) {
265    if (proxy == nullptr) {
266        OH_LOG_ERROR(LOG_APP, "proxy is nullptr");
267        return;
268    }
269    proxy_ = proxy;
270    replyStub_ = OH_IPCRemoteStub_Create(NATIVE_REMOTE_STUB_ASYNC_CALL_TEST_TOKEN.c_str(), OnRemoteRequest,
271        nullptr, this);
272    if (replyStub_ == nullptr) {
273        OH_LOG_ERROR(LOG_APP, "crete reply stub failed!");
274        return;
275    }
276    // Create a death callback object.
277    deathRecipient_ = OH_IPCDeathRecipient_Create(OnDeathRecipientCB, nullptr, this);
278    if (deathRecipient_ == nullptr) {
279        OH_LOG_ERROR(LOG_APP, "OH_IPCDeathRecipient_Create failed!");
280        return;
281    }
282    // Register the death callback object with the proxy to detect the death status of the stub object on the server.
283    OH_IPCRemoteProxy_AddDeathRecipient(proxy_, deathRecipient_);
284}
285
286IpcCApiProxyTest::~IpcCApiProxyTest() {
287    if (proxy_ != nullptr) {
288        OH_IPCRemoteProxy_Destroy(proxy_);
289    }
290    if (deathRecipient_ != nullptr) {
291        OH_IPCDeathRecipient_Destroy(deathRecipient_);
292    }
293    if (replyStub_ != nullptr) {
294        OH_IPCRemoteStub_Destroy(replyStub_);
295    }
296}
297
298int IpcCApiProxyTest::AsyncAdd(int a, int b, int &result) {
299    OH_LOG_INFO(LOG_APP, "start %d + %d", a, b);
300    auto data = OH_IPCParcel_Create();
301    if (data == nullptr) {
302        return OH_IPC_CREATE_OBJECT_ERROR;
303    }
304    // Write the interface token for verification.
305    if (OH_IPCParcel_WriteInterfaceToken(data, NATIVE_REMOTE_STUB_TEST_TOKEN.c_str()) != OH_IPC_SUCCESS) {
306        OH_LOG_ERROR(LOG_APP, "OH_IPCParcel_WriteInterfaceToken failed!");
307        OH_IPCParcel_Destroy(data);
308        return OH_IPC_PARCEL_WRITE_ERROR;
309    }
310    if (OH_IPCParcel_WriteInt32(data, a) != OH_IPC_SUCCESS
311        || OH_IPCParcel_WriteInt32(data, b) != OH_IPC_SUCCESS
312        || OH_IPCParcel_WriteRemoteStub(data, replyStub_) != OH_IPC_SUCCESS) {
313        OH_IPCParcel_Destroy(data);
314        return OH_IPC_PARCEL_WRITE_ERROR;
315    }
316    // Use replyStub_ to receive responses in asynchronous transmission. In asynchronous processing, the OHIPCRemoteStub object for receiving the result needs to be written.
317    OH_IPC_MessageOption option = { OH_IPC_REQUEST_MODE_ASYNC, 0 };
318    int ret = OH_IPCRemoteProxy_SendRequest(proxy_, RequestCode::ASYNC_ADD_CODE, data, nullptr, &option);
319    if (ret != OH_IPC_SUCCESS) {
320        OH_IPCParcel_Destroy(data);
321        OH_LOG_ERROR(LOG_APP, "OH_IPCRemoteProxy_SendRequest failed!");
322        return ret;
323    }
324    static constexpr int TIMEOUT = 3;
325    WaitForAsyncReply(TIMEOUT);
326    OH_LOG_INFO(LOG_APP, "asyncReply_:%d", asyncReply_);
327    result = asyncReply_;
328    OH_IPCParcel_Destroy(data);
329    return OH_IPC_SUCCESS;
330}
331
332int IpcCApiProxyTest::RequestExitChildProcess() {
333    auto data = OH_IPCParcel_Create();
334    if (data == nullptr) {
335        return OH_IPC_CREATE_OBJECT_ERROR;
336    }
337    auto reply = OH_IPCParcel_Create();
338    if (reply == nullptr) {
339        OH_IPCParcel_Destroy(data);
340        return OH_IPC_CREATE_OBJECT_ERROR;
341    }
342    if (OH_IPCParcel_WriteInterfaceToken(data, NATIVE_REMOTE_STUB_TEST_TOKEN.c_str()) != OH_IPC_SUCCESS) {
343        OH_LOG_ERROR(LOG_APP, "OH_IPCParcel_WriteInterfaceToken failed!");
344        OH_IPCParcel_Destroy(data);
345        OH_IPCParcel_Destroy(reply);
346        return OH_IPC_PARCEL_WRITE_ERROR;
347    }
348    OH_IPC_MessageOption option = { OH_IPC_REQUEST_MODE_SYNC, 0 };
349    int ret = OH_IPCRemoteProxy_SendRequest(proxy_, RequestCode::REQUEST_EXIT_CODE, data, reply, &option);
350    if (ret != OH_IPC_SUCCESS) {
351        OH_IPCParcel_Destroy(data);
352        OH_IPCParcel_Destroy(reply);
353        OH_LOG_ERROR(LOG_APP, "OH_IPCRemoteProxy_SendRequest failed!");
354        return ret;
355    }
356    OH_IPCParcel_Destroy(data);
357    OH_IPCParcel_Destroy(reply);
358    return OH_IPC_SUCCESS;
359}
360
361void IpcCApiProxyTest::SendAsyncReply(int &replyValue) {
362    std::unique_lock<std::mutex> lck(mutex_);
363    asyncReply_ = replyValue;
364    cv_.notify_all();
365}
366
367int IpcCApiProxyTest::WaitForAsyncReply(int timeOut) {
368    asyncReply_ = 0;
369    std::unique_lock<std::mutex> lck(mutex_);
370    cv_.wait_for(lck, std::chrono::seconds(timeOut), [&] {
371        return asyncReply_ != 0;
372    });
373    return asyncReply_;
374}
375
376int IpcCApiProxyTest::OnRemoteRequest(uint32_t code, const OHIPCParcel *data,
377        OHIPCParcel *reply, void *userData) {
378    OH_LOG_INFO(LOG_APP, "start %u", code);
379    auto *proxyTest = reinterpret_cast<IpcCApiProxyTest *>(userData);
380    if (proxyTest == nullptr || code != static_cast<uint32_t>(RequestCode::ASYNC_ADD_CODE)) {
381        OH_LOG_ERROR(LOG_APP, "check param failed!");
382        return OH_IPC_CHECK_PARAM_ERROR;
383    }
384    int32_t val = -1;
385    if (OH_IPCParcel_ReadInt32(data, &val) != OH_IPC_SUCCESS) {
386        OH_LOG_ERROR(LOG_APP, "OH_IPCParcel_ReadInt32 failed!");
387        return OH_IPC_PARCEL_READ_ERROR;
388    }
389    proxyTest->SendAsyncReply(val);
390    return OH_IPC_SUCCESS;
391}
392
393void IpcCApiProxyTest::ClearResource() {
394    // clear resource;
395}
396
397void IpcCApiProxyTest::OnDeathRecipientCB(void *userData) {
398    auto *proxyTest = reinterpret_cast<IpcCApiProxyTest *>(userData);
399    if (proxyTest != nullptr) {
400        proxyTest->ClearResource();
401    }
402    OH_LOG_INFO(LOG_APP, "the stub is dead!");
403}
404```
405#### libipcCapiDemo.so for Server Calls
406
407```C++
408IpcCApiStubTest g_ipcStubObj;
409
410#ifdef __cplusplus
411extern "C" {
412
413// The following functions need to be implemented for the server. For details, see the Ability APIs.
414OHIPCRemoteStub* NativeChildProcess_OnConnect() {
415    OH_LOG_INFO(LOG_APP, "NativeChildProcess_OnConnect");
416    return g_ipcStubObj.GetRemoteStub();
417}
418
419void NativeChildProcess_MainProc() {
420    OH_LOG_INFO(LOG_APP, "NativeChildProcess_MainProc");
421    g_ipcStubObj.MainProc();
422    OH_LOG_INFO(LOG_APP, "NativeChildProcess_MainProc End");
423}
424
425}
426#endif
427```
428
429#### Client Invocation Entry
430
431```c++
432IpcCApiProxyTest *g_ipcProxy = nullptr;
433
434// Callback to be invoked when the IPC channel is set up via the ability.
435void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy) {
436    OH_LOG_INFO(LOG_APP, "OnNativeChildProcessStarted proxy=%{public}p err=%{public}d", remoteProxy, errCode);
437    if (remoteProxy == nullptr) {
438        return;
439    }
440    g_ipcProxy = new (std::nothrow) IpcCApiProxyTest(remoteProxy);
441    if (g_ipcProxy == nullptr) {
442        OH_IPCRemoteProxy_Destroy(remoteProxy);
443        OH_LOG_ERROR(LOG_APP, "Alloc IpcCApiProxyTest object failed");
444        return;
445    }
446}
447
448int main(int argc, char *argv[]) {
449    // Call the ability API to create a child process and load the libipcCapiDemo.so file specified in the parameter. The process startup result is asynchronously notified through the callback parameter OnNativeChildProcessStarted. The proxy object is obtained from the callback function.
450    int32_t ret = OH_Ability_CreateNativeChildProcess("libipcCapiDemo.so", OnNativeChildProcessStarted);
451    if (ret != 0) {
452        return -1;
453    }
454    if (g_ipcProxy == nullptr) {
455        return -1;
456    }
457    int a = 2;
458    int b = 3;
459    int result = 0;
460    ret = g_ipcProxy->AsyncAdd(a, b, result);
461    OH_LOG_INFO(LOG_APP, "AsyncAdd: %d + %d = %d, ret=%d", a, b, result, ret);
462
463    // Trigger the stub process to exit.
464    ret = g_ipcProxy->RequestExitChildProcess();
465    // The death notification callback function (IpcCApiProxyTest::OnDeathRecipientCB) is automatically executed.
466    if (g_ipcProxy != nullptr) {
467        delete g_ipcProxy;
468        g_ipcProxy = nullptr;
469    }
470    return 0;
471}
472```
473