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