1# IPC & RPC Development Guidelines 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 8## Available APIs 9 10Table 1 Native IPC APIs 11 12| Class| API| Description| 13| -------- | -------- | -------- | 14| [IRemoteBroker](../reference/apis/js-apis-rpc.md#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 18 19## How to Develop 20 21### **Using Native APIs** 22 231. Add dependencies. 24 25 SDK dependency: 26 27 ``` 28 #IPC scenario 29 external_deps = [ 30 "ipc:ipc_single", 31 ] 32 33 #RPC scenario 34 external_deps = [ 35 "ipc:ipc_core", 36 ] 37 ``` 38 39 In addition, the refbase implementation on which IPC/RPC depends is stored in **//utils**. Add the dependency on the Utils source code. 40 41 ``` 42 external_deps = [ 43 "c_utils:utils", 44 ] 45 ``` 46 472. Define the IPC interface **ITestAbility**. 48 49 **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. 50 51 ```c++ 52 #include "iremote_broker.h" 53 54 // Define message codes. 55 const int TRANS_ID_PING_ABILITY = 5; 56 57 const std::string DESCRIPTOR = "test.ITestAbility"; 58 59 class ITestAbility : public IRemoteBroker { 60 public: 61 // DECLARE_INTERFACE_DESCRIPTOR is mandatory, and the input parameter is std::u16string. 62 DECLARE_INTERFACE_DESCRIPTOR(to_utf16(DESCRIPTOR)); 63 virtual int TestPingAbility(const std::u16string &dummy) = 0; // Define functions. 64 }; 65 ``` 66 673. Define and implement service provider **TestAbilityStub**. 68 69 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. 70 71 ```c++ 72 #include "iability_test.h" 73 #include "iremote_stub.h" 74 75 class TestAbilityStub : public IRemoteStub<ITestAbility> { 76 public: 77 virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; 78 int TestPingAbility(const std::u16string &dummy) override; 79 }; 80 81 int TestAbilityStub::OnRemoteRequest(uint32_t code, 82 MessageParcel &data, MessageParcel &reply, MessageOption &option) 83 { 84 switch (code) { 85 case TRANS_ID_PING_ABILITY: { 86 std::u16string dummy = data.ReadString16(); 87 int result = TestPingAbility(dummy); 88 reply.WriteInt32(result); 89 return 0; 90 } 91 default: 92 return IPCObjectStub::OnRemoteRequest(code, data, reply, option); 93 } 94 } 95 ``` 96 974. Define the **TestAbility** class that implements functions for the stub. 98 99 ```c++ 100 #include "iability_server_test.h" 101 102 class TestAbility : public TestAbilityStub { 103 public: 104 int TestPingAbility(const std::u16string &dummy); 105 } 106 107 int TestAbility::TestPingAbility(const std::u16string &dummy) { 108 return 0; 109 } 110 ``` 111 1125. Define and implement **TestAbilityProxy**. 113 114 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. 115 116 ```c++ 117 #include "iability_test.h" 118 #include "iremote_proxy.h" 119 #include "iremote_object.h" 120 121 class TestAbilityProxy : public IRemoteProxy<ITestAbility> { 122 public: 123 explicit TestAbilityProxy(const sptr<IRemoteObject> &impl); 124 int TestPingAbility(const std::u16string &dummy) override; 125 private: 126 static inline BrokerDelegator<TestAbilityProxy> delegator_; // For use of the iface_cast macro at a later time 127 } 128 129 TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl) 130 : IRemoteProxy<ITestAbility>(impl) 131 { 132 } 133 134 int TestAbilityProxy::TestPingAbility(const std::u16string &dummy){ 135 MessageOption option; 136 MessageParcel dataParcel, replyParcel; 137 dataParcel.WriteString16(dummy); 138 int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option); 139 int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1; 140 return result; 141 } 142 ``` 143 1446. Register and start an SA. 145 146 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. 147 148 ```c++ 149 // Register the TestAbilityStub instance with the SystemAbilityManager on the same device as the SA. 150 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 151 samgr->AddSystemAbility(saId, new TestAbility()); 152 153 // Register the TestAbilityStub instance with the SystemAbilityManager on a different device. 154 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 155 ISystemAbilityManager::SAExtraProp saExtra; 156 saExtra.isDistributed = true; // Set a distributed SA. 157 int result = samgr->AddSystemAbility(saId, new TestAbility(), saExtra); 158 ``` 159 1607. Obtain the SA. 161 162 Call the **GetSystemAbility** function of the **SystemAbilityManager** class to obtain the **IRemoteObject** for the SA, and create a **TestAbilityProxy** instance. 163 164 ```c++ 165 // Obtain the proxy of the SA registered on the local device. 166 sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 167 sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId); 168 sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // Use the iface_cast macro to convert the proxy to a specific type. 169 170 // Obtain the proxy of the SA registered with any other devices. 171 sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 172 173 // networkId is the device identifier and can be obtained through GetLocalNodeDeviceInfo. 174 sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(saId, networkId); 175 sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // Construct a proxy. 176 ``` 177 178### **Using JS APIs** 179 1801. Add dependencies. 181 182 ```ts 183 import rpc from '@ohos.rpc'; 184 // Import @ohos.ability.featureAbility only for the application developed based on the FA model. 185 // import featureAbility from "@ohos.ability.featureAbility"; 186 ``` 187 188 If you use the stage model, you need to obtain the context. The sample code is as follows: 189 190 ```ts 191 import UIAbility from '@ohos.app.ability.UIAbility'; 192 import Want from '@ohos.app.ability.Want'; 193 import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 194 import window from '@ohos.window'; 195 196 export default class MainAbility extends UIAbility { 197 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 198 console.log("[Demo] MainAbility onCreate"); 199 let context = this.context; 200 } 201 onDestroy() { 202 console.log("[Demo] MainAbility onDestroy"); 203 } 204 onWindowStageCreate(windowStage: window.WindowStage) { 205 // Main window is created, set main page for this ability 206 console.log("[Demo] MainAbility onWindowStageCreate"); 207 } 208 onWindowStageDestroy() { 209 // Main window is destroyed, release UI related resources 210 console.log("[Demo] MainAbility onWindowStageDestroy"); 211 } 212 onForeground() { 213 // Ability has brought to foreground 214 console.log("[Demo] MainAbility onForeground"); 215 } 216 onBackground() { 217 // Ability has back to background 218 console.log("[Demo] MainAbility onBackground"); 219 } 220 } 221 ``` 222 2232. Bind the desired ability. 224 225 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 **deviceManager**. Then, construct the **connect** variable, and specify the callback that is called 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. 226 227 ```ts 228 // Import @ohos.ability.featureAbility only for the application developed based on the FA model. 229 // import featureAbility from "@ohos.ability.featureAbility"; 230 import rpc from '@ohos.rpc'; 231 import Want from '@ohos.app.ability.Want'; 232 import common from '@ohos.app.ability.common'; 233 import deviceManager from '@ohos.distributedHardware.deviceManager'; 234 import { BusinessError } from '@ohos.base'; 235 236 let dmInstance: deviceManager.DeviceManager | undefined; 237 let proxy: rpc.IRemoteObject | undefined = undefined; 238 let connectId: number; 239 240 // Bind the ability on a single device. 241 let want: Want = { 242 // Enter the bundle name and ability name. 243 bundleName: "ohos.rpc.test.server", 244 abilityName: "ohos.rpc.test.server.ServiceAbility", 245 }; 246 let connect: common.ConnectOptions = { 247 onConnect: (elementName, remote) => { 248 proxy = remote; 249 }, 250 onDisconnect: (elementName) => { 251 }, 252 onFailed: () => { 253 proxy; 254 } 255 }; 256 // Use this method to connect to the ability in the FA model. 257 // connectId = featureAbility.connectAbility(want, connect); 258 259 connectId = this.context.connectServiceExtensionAbility(want,connect); 260 261 // Cross-device binding 262 let deviceManagerCallback = (err: BusinessError, data: deviceManager.DeviceManager) => { 263 if (err) { 264 console.error("createDeviceManager errCode:" + err.code + ",errMessage:" + err.message); 265 return; 266 } 267 console.info("createDeviceManager success"); 268 dmInstance = data; 269 } 270 try{ 271 deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback); 272 } catch(error) { 273 let err: BusinessError = error as BusinessError; 274 console.error("createDeviceManager errCode:" + err.code + ",errMessage:" + err.message); 275 } 276 277 // Use deviceManager to obtain the network ID of the target device. 278 if (dmInstance != undefined) { 279 let deviceList: Array<deviceManager.DeviceInfo> = dmInstance.getTrustedDeviceListSync(); 280 let networkId: string = deviceList[0].networkId; 281 let want: Want = { 282 bundleName: "ohos.rpc.test.server", 283 abilityName: "ohos.rpc.test.service.ServiceAbility", 284 deviceId: networkId, 285 flags: 256 286 }; 287 // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection. 288 // Use this method to connect to the ability in the FA model. 289 // connectId = featureAbility.connectAbility(want, connect); 290 291 // The first parameter specifies the bundle name of the application, and the second parameter specifies the callback used to return the device ID obtained by using DeviceManager. 292 connectId = this.context.connectServiceExtensionAbility(want,connect); 293 } 294 ``` 295 2963. Process requests sent from the client. 297 298 Call the **onConnect** API to return a proxy object inherited from **rpc.RemoteObject** after the ability is successfully bound. Implement the **onRemoteMessageRequest** API for the proxy object to process requests sent from the client. 299 300 ```ts 301 import rpc from '@ohos.rpc'; 302 import Want from '@ohos.app.ability.Want'; 303 class Stub extends rpc.RemoteObject { 304 constructor(descriptor: string) { 305 super(descriptor); 306 } 307 onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> { 308 // Process requests sent from the client based on the code. 309 return true; 310 } 311 312 onConnect(want: Want) { 313 const robj: rpc.RemoteObject = new Stub("rpcTestAbility"); 314 return robj; 315 } 316 } 317 ``` 318 3194. Process responses sent from the server. 320 321 Obtain the proxy object from the **onConnect** callback, call **sendRequest** 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). 322 323 ```ts 324 import rpc from '@ohos.rpc'; 325 // Use a promise. 326 let option = new rpc.MessageOption(); 327 let data = rpc.MessageParcel.create(); 328 let reply = rpc.MessageParcel.create(); 329 // Write parameters to data. 330 let proxy: rpc.IRemoteObject | undefined = undefined; 331 proxy.sendRequest(1, data, reply, option) 332 .then((result: rpc.SendRequestResult) => { 333 if (result.errCode != 0) { 334 console.error("send request failed, errCode: " + result.errCode); 335 return; 336 } 337 // Read the result from result.reply. 338 }) 339 .catch((e: Error) => { 340 console.error("send request got exception: " + e); 341 }) 342 .finally(() => { 343 data.reclaim(); 344 reply.reclaim(); 345 }) 346 347 // Use a callback. 348 function sendRequestCallback(result: rpc.SendRequestResult) { 349 try { 350 if (result.errCode != 0) { 351 console.error("send request failed, errCode: " + result.errCode); 352 return; 353 } 354 // Read the result from result.reply. 355 } finally { 356 result.data.reclaim(); 357 result.reply.reclaim(); 358 } 359 } 360 let options = new rpc.MessageOption(); 361 let datas = rpc.MessageParcel.create(); 362 let replys = rpc.MessageParcel.create(); 363 // Write parameters to data. 364 proxy.sendRequest(1, datas, replys, options, sendRequestCallback); 365 ``` 366 3675. Tear down the connection. 368 369 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. 370 371 ```ts 372 import rpc from "@ohos.rpc"; 373 import Want from '@ohos.app.ability.Want'; 374 import common from '@ohos.app.ability.common'; 375 // Import @ohos.ability.featureAbility only for the application developed based on the FA model. 376 // import featureAbility from "@ohos.ability.featureAbility"; 377 378 function disconnectCallback() { 379 console.info("disconnect ability done"); 380 } 381 // Use this method to disconnect from the ability in the FA model. 382 // featureAbility.disconnectAbility(connectId, disconnectCallback); 383 384 let proxy: rpc.IRemoteObject | undefined = undefined; 385 let connectId: number; 386 387 // Bind the ability on a single device. 388 let want: Want = { 389 // Enter the bundle name and ability name. 390 bundleName: "ohos.rpc.test.server", 391 abilityName: "ohos.rpc.test.server.ServiceAbility", 392 }; 393 let connect: common.ConnectOptions = { 394 onConnect: (elementName, remote) => { 395 proxy = remote; 396 }, 397 onDisconnect: (elementName) => { 398 }, 399 onFailed: () => { 400 proxy; 401 } 402 }; 403 // Use this method to connect to the ability in the FA model. 404 // connectId = featureAbility.connectAbility(want, connect); 405 406 connectId = this.context.connectServiceExtensionAbility(want,connect); 407 408 this.context.disconnectServiceExtensionAbility(connectId); 409 ``` 410