1# IPC & 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 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() | 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) | Callback used to process a request from the proxy and return the result. Derived classes need to override this API.| 16| IRemoteProxy | | Service proxy class, which is 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_; // Use the iface_cast macro. 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 featureAbility from "@ohos.ability.featureAbility" 185 ``` 186 187 188 1892. Bind the desired ability. 190 191 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. After that, call the API provided by **featureAbility** to bind an ability. 192 193 ```ts 194 import rpc from "@ohos.rpc" 195 import featureAbility from "@ohos.ability.featureAbility" 196 197 let proxy = null 198 let connectId = null 199 200 // Bind the ability on a single device. 201 let want = { 202 // Enter the bundle name and ability name. 203 "bundleName": "ohos.rpc.test.server", 204 "abilityName": "ohos.rpc.test.server.ServiceAbility", 205 } 206 let connect = { 207 onConnect:function(elementName, remote) { 208 proxy = remote 209 }, 210 onDisconnect:function(elementName) { 211 }, 212 onFailed:function() { 213 proxy = null 214 } 215 } 216 connectId = featureAbility.connectAbility(want, connect) 217 218 // If you're binding the ability across devices, use deviceManager to obtain the network ID of the target device. 219 import deviceManager from '@ohos.distributedHardware.deviceManager' 220 function deviceManagerCallback(deviceManager) { 221 let deviceList = deviceManager.getTrustedDeviceListSync() 222 let networkId = deviceList[0].networkId 223 let want = { 224 "bundleName": "ohos.rpc.test.server", 225 "abilityName": "ohos.rpc.test.service.ServiceAbility", 226 "networkId": networkId, 227 "flags": 256 228 } 229 connectId = featureAbility.connectAbility(want, connect) 230 } 231 // 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. 232 deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback) 233 ``` 234 235 236 2373. Process requests sent from the client. 238 239 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. 240 241 ```ts 242 onConnect(want: Want) { 243 var robj:rpc.RemoteObject = new Stub("rpcTestAbility") 244 return robj 245 } 246 class Stub extends rpc.RemoteObject { 247 constructor(descriptor) { 248 super(descriptor) 249 } 250 onRemoteMessageRequest(code, data, reply, option) { 251 // Process requests sent from the client based on the code. 252 return true 253 } 254 } 255 ``` 256 257 258 2594. Process responses sent from the server. 260 261 Obtain the proxy object from the **onConnect** callback, call **sendRequestAsync** 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). 262 263 ```ts 264 // Use a promise. 265 let option = new rpc.MessageOption() 266 let data = rpc.MessageParcel.create() 267 let reply = rpc.MessageParcel.create() 268 // Write parameters to data. 269 proxy.sendRequestAsync(1, data, reply, option) 270 .then(function(result) { 271 if (result.errCode != 0) { 272 console.error("send request failed, errCode: " + result.errCode) 273 return 274 } 275 // Read the result from result.reply. 276 }) 277 .catch(function(e) { 278 console.error("send request got exception: " + e) 279 } 280 .finally(() => { 281 data.reclaim() 282 reply.reclaim() 283 }) 284 285 // Use a callback. 286 function sendRequestCallback(result) { 287 try { 288 if (result.errCode != 0) { 289 console.error("send request failed, errCode: " + result.errCode) 290 return 291 } 292 // Read the result from result.reply. 293 } finally { 294 result.data.reclaim() 295 result.reply.reclaim() 296 } 297 } 298 let option = new rpc.MessageOption() 299 let data = rpc.MessageParcel.create() 300 let reply = rpc.MessageParcel.create() 301 // Write parameters to data. 302 proxy.sendRequest(1, data, reply, option, sendRequestCallback) 303 ``` 304 3055. Tear down the connection. 306 307 Use the API provided by **featureAbility** to tear down the connection when the communication is over. 308 309 ```ts 310 import rpc from "@ohos.rpc" 311 import featureAbility from "@ohos.ability.featureAbility" 312 function disconnectCallback() { 313 console.info("disconnect ability done") 314 } 315 featureAbility.disconnectAbility(connectId, disconnectCallback) 316 ``` 317