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. 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