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| 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 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 ArkTS 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 hilog from '@ohos.hilog'; 194 import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 195 import window from '@ohos.window'; 196 197 export default class MainAbility extends UIAbility { 198 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 199 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onCreate'); 200 let context = this.context; 201 } 202 onDestroy() { 203 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onDestroy'); 204 } 205 onWindowStageCreate(windowStage: window.WindowStage) { 206 // Main window is created, set main page for this ability 207 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageCreate'); 208 } 209 onWindowStageDestroy() { 210 // Main window is destroyed, release UI related resources 211 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onWindowStageDestroy'); 212 } 213 onForeground() { 214 // Ability has brought to foreground 215 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onForeground'); 216 } 217 onBackground() { 218 // Ability has back to background 219 hilog.info(0x0000, 'testTag', '%{public}s', 'UIAbility onBackground'); 220 } 221 } 222 ``` 223 2242. Bind the desired ability. 225 226 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. 227 228 ```ts 229 // Import @ohos.ability.featureAbility only for the application developed based on the FA model. 230 // import featureAbility from "@ohos.ability.featureAbility"; 231 import rpc from '@ohos.rpc'; 232 import Want from '@ohos.app.ability.Want'; 233 import common from '@ohos.app.ability.common'; 234 import hilog from '@ohos.hilog'; 235 import deviceManager from '@ohos.distributedDeviceManager'; 236 import { BusinessError } from '@ohos.base'; 237 238 let dmInstance: deviceManager.DeviceManager | undefined; 239 let proxy: rpc.IRemoteObject | undefined; 240 let connectId: number; 241 242 // Bind the ability on a single device. 243 let want: Want = { 244 // Enter the bundle name and ability name. 245 bundleName: "ohos.rpc.test.server", 246 abilityName: "ohos.rpc.test.server.ServiceAbility", 247 }; 248 let connect: common.ConnectOptions = { 249 onConnect: (elementName, remoteProxy) => { 250 hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called'); 251 proxy = remoteProxy; 252 }, 253 onDisconnect: (elementName) => { 254 hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect'); 255 }, 256 onFailed: () => { 257 hilog.info(0x0000, 'testTag', 'RpcClient: onFailed'); 258 } 259 }; 260 // Use this method to connect to the ability in the FA model. 261 // connectId = featureAbility.connectAbility(want, connect); 262 263 connectId = this.context.connectServiceExtensionAbility(want,connect); 264 265 // Cross-device binding 266 try{ 267 dmInstance = deviceManager.createDeviceManager("ohos.rpc.test"); 268 } catch(error) { 269 let err: BusinessError = error as BusinessError; 270 hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message); 271 } 272 273 // Use deviceManager to obtain the network ID of the target device. 274 if (dmInstance != undefined) { 275 let deviceList = dmInstance.getAvailableDeviceListSync(); 276 let networkId = deviceList[0].networkId; 277 let want: Want = { 278 bundleName: "ohos.rpc.test.server", 279 abilityName: "ohos.rpc.test.service.ServiceAbility", 280 deviceId: networkId, 281 flags: 256 282 }; 283 // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection. 284 // Use this method to connect to the ability in the FA model. 285 // connectId = featureAbility.connectAbility(want, connect); 286 287 // 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. 288 connectId = this.context.connectServiceExtensionAbility(want,connect); 289 } 290 ``` 291 2923. Process requests sent from the client. 293 294 Call the **onConnect** API to return a proxy object inherited from [rpc.RemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#remoteobject) after the ability is successfully bound. Implement the [onRemoteMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#onremotemessagerequest9) API for the proxy object to process requests sent from the client. 295 296 ```ts 297 import rpc from '@ohos.rpc'; 298 import Want from '@ohos.app.ability.Want'; 299 class Stub extends rpc.RemoteObject { 300 constructor(descriptor: string) { 301 super(descriptor); 302 } 303 onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> { 304 // Process requests sent from the client based on the code. 305 return true; 306 } 307 308 onConnect(want: Want) { 309 const robj: rpc.RemoteObject = new Stub("rpcTestAbility"); 310 return robj; 311 } 312 } 313 ``` 314 3154. Process responses sent from the server. 316 317 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). 318 319 ```ts 320 import rpc from '@ohos.rpc'; 321 import hilog from '@ohos.hilog'; 322 323 // Use a promise. 324 let option = new rpc.MessageOption(); 325 let data = rpc.MessageSequence.create(); 326 let reply = rpc.MessageSequence.create(); 327 // Write parameters to data. 328 let proxy: rpc.IRemoteObject | undefined; 329 if (proxy != undefined) { 330 proxy.sendMessageRequest(1, data, reply, option) 331 .then((result: rpc.RequestResult) => { 332 if (result.errCode != 0) { 333 hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode); 334 return; 335 } 336 // Read the result from result.reply. 337 }) 338 .catch((e: Error) => { 339 hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e); 340 }) 341 .finally(() => { 342 data.reclaim(); 343 reply.reclaim(); 344 }) 345 } 346 347 // Use a callback. 348 function sendRequestCallback(err: Error, result: rpc.RequestResult) { 349 try { 350 if (result.errCode != 0) { 351 hilog.error(0x0000, 'testTag', 'sendMessageRequest 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.MessageSequence.create(); 362 let replys = rpc.MessageSequence.create(); 363 // Write parameters to data. 364 if (proxy != undefined) { 365 proxy.sendMessageRequest(1, datas, replys, options, sendRequestCallback); 366 } 367 ``` 368 3695. Tear down the connection. 370 371 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. 372 373 ```ts 374 import rpc from '@ohos.rpc'; 375 import Want from '@ohos.app.ability.Want'; 376 import hilog from '@ohos.hilog'; 377 import common from '@ohos.app.ability.common'; 378 // Import @ohos.ability.featureAbility only for the application developed based on the FA model. 379 // import featureAbility from "@ohos.ability.featureAbility"; 380 381 function disconnectCallback() { 382 hilog.info(0x0000, 'testTag', 'disconnect ability done'); 383 } 384 // Use this method to disconnect from the ability in the FA model. 385 // featureAbility.disconnectAbility(connectId, disconnectCallback); 386 387 let proxy: rpc.IRemoteObject | undefined; 388 let connectId: number; 389 390 // Bind the ability on a single device. 391 let want: Want = { 392 // Enter the bundle name and ability name. 393 bundleName: "ohos.rpc.test.server", 394 abilityName: "ohos.rpc.test.server.ServiceAbility", 395 }; 396 let connect: common.ConnectOptions = { 397 onConnect: (elementName, remote) => { 398 proxy = remote; 399 }, 400 onDisconnect: (elementName) => { 401 }, 402 onFailed: () => { 403 proxy; 404 } 405 }; 406 // Use this method to connect to the ability in the FA model. 407 // connectId = featureAbility.connectAbility(want, connect); 408 409 connectId = this.context.connectServiceExtensionAbility(want,connect); 410 411 this.context.disconnectServiceExtensionAbility(connectId); 412 ``` 413 414<!--no_check-->