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<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<!--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<ITestAbility>**. 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<ITestAbility>**. 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. 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**. 200 201 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. 202 203 ```ts 204 // If the FA model is used, import featureAbility from @kit.AbilityKit. 205 // import { featureAbility } from "@kit.AbilityKit"; 206 import { Want, common } from '@kit.AbilityKit'; 207 import { rpc } from '@kit.IPCKit'; 208 import { hilog } from '@kit.PerformanceAnalysisKit'; 209 import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 210 import { BusinessError } from '@kit.BasicServicesKit'; 211 212 let dmInstance: distributedDeviceManager.DeviceManager | undefined; 213 let proxy: rpc.IRemoteObject | undefined; 214 let connectId: number; 215 216 // Bind an ability on a single device. 217 let want: Want = { 218 // Enter the bundle name and ability name. 219 bundleName: "ohos.rpc.test.server", 220 abilityName: "ohos.rpc.test.server.ServiceAbility", 221 }; 222 let connect: common.ConnectOptions = { 223 onConnect: (elementName, remoteProxy) => { 224 hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called'); 225 proxy = remoteProxy; 226 }, 227 onDisconnect: (elementName) => { 228 hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect'); 229 }, 230 onFailed: () => { 231 hilog.info(0x0000, 'testTag', 'RpcClient: onFailed'); 232 } 233 }; 234 // Use this method to connect to the ability in the FA model. 235 // connectId = featureAbility.connectAbility(want, connect); 236 237 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext 238 // Save the connection ID, which will be used when the ability is disconnected. 239 connectId = context.connectServiceExtensionAbility(want,connect); 240 241 // Bind an ability across devices. 242 try{ 243 dmInstance = distributedDeviceManager.createDeviceManager("ohos.rpc.test"); 244 } catch(error) { 245 let err: BusinessError = error as BusinessError; 246 hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message); 247 } 248 249 // Use distributedDeviceManager to obtain the network ID of the target device. 250 if (dmInstance != undefined) { 251 let deviceList = dmInstance.getAvailableDeviceListSync(); 252 let networkId = deviceList[0].networkId; 253 let want: Want = { 254 bundleName: "ohos.rpc.test.server", 255 abilityName: "ohos.rpc.test.service.ServiceAbility", 256 deviceId: networkId, 257 flags: 256 258 }; 259 // Save the connection ID, which will be used when the ability is disconnected. 260 // Use this method to connect to the ability in the FA model. 261 // connectId = featureAbility.connectAbility(want, connect); 262 263 // 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. 264 connectId = context.connectServiceExtensionAbility(want,connect); 265 } 266 ``` 267 2683. Process service requests sent from the client. 269 270 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. 271 272 ```ts 273 import { rpc } from '@kit.IPCKit'; 274 import { Want } from '@kit.AbilityKit'; 275 class Stub extends rpc.RemoteObject { 276 constructor(descriptor: string) { 277 super(descriptor); 278 } 279 onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> { 280 // Process requests sent from the client based on the code. 281 return true; 282 } 283 284 onConnect(want: Want) { 285 const robj: rpc.RemoteObject = new Stub("rpcTestAbility"); 286 return robj; 287 } 288 } 289 ``` 290 2914. Process responses sent from the server. 292 293 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). 294 295 ```ts 296 import { rpc } from '@kit.IPCKit'; 297 import { hilog } from '@kit.PerformanceAnalysisKit'; 298 299 // Use a promise. 300 let option = new rpc.MessageOption(); 301 let data = rpc.MessageSequence.create(); 302 let reply = rpc.MessageSequence.create(); 303 // Write parameters to data. 304 let proxy: rpc.IRemoteObject | undefined; 305 if (proxy != undefined) { 306 proxy.sendMessageRequest(1, data, reply, option) 307 .then((result: rpc.RequestResult) => { 308 if (result.errCode != 0) { 309 hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode); 310 return; 311 } 312 // Read the result from result.reply. 313 }) 314 .catch((e: Error) => { 315 hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e); 316 }) 317 .finally(() => { 318 data.reclaim(); 319 reply.reclaim(); 320 }) 321 } 322 323 // Use a callback. 324 function sendRequestCallback(err: Error, result: rpc.RequestResult) { 325 try { 326 if (result.errCode != 0) { 327 hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode); 328 return; 329 } 330 // Read the result from result.reply. 331 } finally { 332 result.data.reclaim(); 333 result.reply.reclaim(); 334 } 335 } 336 let options = new rpc.MessageOption(); 337 let datas = rpc.MessageSequence.create(); 338 let replys = rpc.MessageSequence.create(); 339 // Write parameters to data. 340 if (proxy != undefined) { 341 proxy.sendMessageRequest(1, datas, replys, options, sendRequestCallback); 342 } 343 ``` 344 3455. Tear down the connection. 346 347 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. 348 349 ```ts 350 // If the FA model is used, import featureAbility from @kit.AbilityKit. 351 // import { featureAbility } from "@kit.AbilityKit"; 352 import { Want, common } from '@kit.AbilityKit'; 353 import { rpc } from '@kit.IPCKit'; 354 import { hilog } from '@kit.PerformanceAnalysisKit'; 355 356 function disconnectCallback() { 357 hilog.info(0x0000, 'testTag', 'disconnect ability done'); 358 } 359 // Use this method to disconnect from the ability in the FA model. 360 // featureAbility.disconnectAbility(connectId, disconnectCallback); 361 362 let proxy: rpc.IRemoteObject | undefined; 363 let connectId: number; 364 365 // Connect to an ability on a single device. 366 let want: Want = { 367 // Enter the bundle name and ability name. 368 bundleName: "ohos.rpc.test.server", 369 abilityName: "ohos.rpc.test.server.ServiceAbility", 370 }; 371 let connect: common.ConnectOptions = { 372 onConnect: (elementName, remote) => { 373 proxy = remote; 374 }, 375 onDisconnect: (elementName) => { 376 }, 377 onFailed: () => { 378 proxy; 379 } 380 }; 381 // Use this method to connect to the ability in the FA model. 382 // connectId = featureAbility.connectAbility(want, connect); 383 384 connectId = this.context.connectServiceExtensionAbility(want,connect); 385 386 this.context.disconnectServiceExtensionAbility(connectId); 387 ``` 388 389