1# OpenHarmony IDL Specifications and User Guide 2 3## IDL Overview 4To ensure successful communications between the client and server, interfaces recognized by both parties must be defined. The OpenHarmony Interface Definition Language (IDL) is a tool for defining such interfaces. OpenHarmony IDL decomposes objects to be transferred into primitives that can be understood by the operating system and encapsulates cross-boundary objects based on developers' requirements. 5 6 **Figure 1** IDL interface description 7 8 9 10**IDL provides the following functions:** 11 12- Declares interfaces provided by system services for external systems, and based on the interface declaration, generates C, C++, JS, or TS code for inter-process communication (IPC) or remote procedure call (RPC) proxies and stubs during compilation. 13 14- Declares interfaces provided by abilities for external systems, and based on the interface declaration, generates C, C++, JS, or TS code for IPC or RPC proxies and stubs during compilation. 15 16**Figure 2** IPC/RPC communication model 17 18 19 20**IDL has the following advantages:** 21 22- Services are defined in the form of interfaces in IDL. Therefore, you do not need to focus on implementation details. 23 24- Interfaces defined by IDL can be used in IPC or RPC scenarios. The information or code generated based on the definitions in IDL simplifies IPC or RPC implementation. 25 26## IDL File Structure 27 28### Data Types 29 30#### Primitive Type 31| IDL Primitive Type| C++ Primitive Type| TS Primitive Type| 32| -------- | -------- | -------- | 33|void | void | void | 34|boolean | bool | boolean | 35|byte | int8_t | number | 36|short | int16_t | number | 37|int | int32_t | number | 38|long | int64_t | number | 39|float | float | number | 40|double | double | number | 41|String | std::string | string | 42 43The preceding table lists the primitive types supported by IDL and the mappings to the C++ and TS primitive types. 44 45#### sequenceable Type 46The sequenceable type is declared using the keyword **sequenceable**. This type can be passed during IPC or RPC through **Parcel** objects. The declaration mode of the sequenceable type in C++ is different from that in TS. 47 48In C++, the declaration is placed in the file header in the format of **sequenceable includedir..namespace.typename**. It can be in any of the following forms: 49 50```cpp 51sequenceable includedir..namespace.typename 52sequenceable includedir...typename 53sequenceable namespace.typename 54``` 55 56In the preceding information, **includedir** indicates the directory where the header file of the type is located, and the dot (.) is used as the separator. **namespace** indicates the namespace where the type is located, and the dot (.) is used as the separator. **typename** indicates the data type, which can contain only English characters. **includedir** and **namespace** are separated by two dots (..). If the declaration statement does not contain two dots, all characters except the last typename will be parsed as a namespace. Example: 57 58```cpp 59sequenceable a.b..C.D 60``` 61 62The preceding statement is parsed into the following code in the C++ header file: 63 64```cpp 65#include "a/b/d.h" 66using C::D; 67``` 68 69In TS, the declaration is placed in the file header in the format of **sequenceable namespace.typename;**. It can be in the following form: 70 71```ts 72sequenceable idl.MySequenceable 73``` 74 75In the preceding information, **namespace** indicates the namespace to which the data type belongs, **typename** indicates the data type name, and **MySequenceable** indicates that data can be passed during IPC using **Parcel** objects. The sequenceable type is not defined in the IDL file, but in the .ts file. Therefore, IDL adds the following statement to the generated .ts file based on the declaration: 76 77```ts 78import MySequenceable from "./my_sequenceable" 79``` 80 81Note that IDL does not implement code for this type. It only imports the header file in the specified format or imports the specified module and uses the type. Therefore, you must ensure that the imported directory, namespace, and type are correct. 82 83#### Interface Type 84The interface type refers to interfaces defined in IDL files. The interfaces defined in an IDL file can be directly used as the parameter type or return value type of a method declared in the file. If an IDL file attempts to use interfaces defined in other IDL files, forward declaration must be contained in the header of that IDL file. 85 86The declaration form in C++ is similar to that of the sequenceable type. The declaration form is as follows: 87 88```cpp 89interface includedir..namespace.typename 90``` 91 92In TS, the declaration form is as follows: 93 94```ts 95interface namespace.interfacename 96``` 97 98In the preceding information, **namespace** indicates the namespace to which the interface belongs, and **interfacename** indicates the name of the interface. For example, **interface OHOS.IIdlTestObserver;** declares the **IIdlTestObserver** interface defined in another IDL file. This interface can be used as the parameter type or return value type of a method in the current file. IDL adds the following statement to the generated .ts file based on the statement: 99 100```ts 101import IIdlTestObserver from "./i_idl_test_observer" 102``` 103 104#### Array Type 105The array type is represented by T[], where **T** can be the primitive, sequenceable, interface, or array type. In C++, this type is generated as **std::vector<T>**. 106The table below lists the mappings between the IDL array type and TS and C++ data types. 107 108|IDL Data Type | C++ Data Type | TS Data Type | 109| ------- | -------- | -------- | 110|T[] | std::vector<T> | T[] | 111 112#### Container Type 113IDL supports two container types: List and Map. The List container is represented in the format of **List<T>**. The Map container is represented in the format of **Map<KT,VT>**, where **T**, **KT**, and **VT** can be of the primitive, sequenceable, interface, array, or container type. 114 115In C++, the List container type is generated as **std::list**, and the Map container type is generated as **std::map**. 116 117In TS, the List container type is not supported, and the Map container type is generated as **Map**. 118 119The table below lists the mappings between the IDL container type and TS and C++ data types. 120 121|IDL Data Type | C++ Data Type | TS Data Type | 122| -------- | -------- | ------- | 123|List<T> | std::list | Not supported | 124|Map<KT,VT> | std::map | Map | 125 126 127### Specifications for Compiling IDL Files 128Only one interface type can be defined in an IDL file, and the interface name must be the same as the file name. The interface definition of the IDL file is described in Backus-Naur form (BNF). The basic definition format is as follows: 129 130``` 131[<*interface_attr_declaration*>]interface<*interface_name_with_namespace*>{<*method_declaration*>} 132``` 133 134In the preceding information, <*interface_attr_declaration*> declares interface attributes. Currently, only the **oneway** attribute is supported, indicating that all methods in the interface are unidirectional. Such a method returns value without waiting for the execution to complete. This attribute is optional. If this attribute is not set, synchronous call is used. The interface name must contain the complete interface header file directory, namespace, and method declaration. Empty interfaces are not allowed. 135The method declaration format in the interface is as follows: 136 137``` 138[<*method_attr_declaration*>]<*result_type*><*method_declaration*> 139``` 140 141In the preceding information, <*method_attr_declaration*> describes the interface attributes. Currently, only the **oneway** attribute is supported, indicating that the method is unidirectional. Such a method returns value without waiting for the execution to complete. This attribute is optional. If this attribute is not set, synchronous call is used. <*result_type*> indicates the type of the return value, and <*method_declaration*> indicates the method name and parameter declaration. 142The parameter declaration format is as follows: 143 144``` 145[<*formal_param_attr*>]<*type*><*identifier*> 146``` 147 148The value of <*formal_param_attr*> can be **in**, **out**, or **inout**, indicating that the parameter is an input parameter, an output parameter, or both an input and an output parameter, respectively. A **oneway** method does not allow **output** or **inout** parameters or return values. 149 150## How to Develop 151 152### Obtaining IDL 153On DevEco Studio, choose **Tools > SDK Manager** to view the local installation path of the OpenHarmony SDK. The following figure uses DevEco Studio 3.0.0.993 as an example. 154 155 156 157Go to the local installation path, choose **toolchains > 3.x.x.x** (the folder named after the version number), and check whether the executable file of IDL exists. 158 159> **NOTE**: Use the SDK of the latest version. The use of an earlier version may cause errors in some statements. 160 161If the executable file does not exist, download the SDK package from the mirror as instructed in the [Release Notes](../../release-notes). The following uses the [3.2 Beta5]((../../release-notes/OpenHarmony-v3.2-beta5.md#acquiring-source-code-from-mirrors) as an example. 162 163For details about how to replace the SDK package, see [Guide to Switching to Full SDK](../quick-start/full-sdk-switch-guide.md). 164 165After obtaining the executable file, perform subsequent development steps based on your scenario. 166 167### Development Using TS 168 169#### Creating an IDL File 170 171You can use TS to create IDL files. 172 173 For example, create a file named **IIdlTestService.idl** with the following content: 174 175```cpp 176 interface OHOS.IIdlTestService { 177 int TestIntTransaction([in] int data); 178 void TestStringTransaction([in] String data); 179 } 180``` 181 182Run the **idl -gen-ts -d *dir* -c dir/IIdlTestService.idl** command in the folder where the executable file is located. 183 184-*dir* next to **d** is the target output folder. For example, if the target output folder is **IIdlTestServiceTs**, run the **idl -gen-ts -d IIdlTestServiceTs -c IIdlTestServiceTs/IIdlTestService.idl** command in the folder where the executable file is located. The interface file, stub file, and proxy file are generated in the *dir* directory (**IIdlTestServiceTs** directory in this example) in the execution environment. 185 186> **NOTE**: The generated interface class file name must be the same as that of the .idl file. Otherwise, an error occurs during code generation. 187 188For example, for an .idl file named **IIdlTestService.idl** and target output directory named **IIdlTestServiceTs**, the directory structure is similar to the following: 189 190``` 191├── IIdlTestServiceTs # IDL code output folder 192│ ├── i_idl_test_service.ts # File generated 193│ ├── idl_test_service_proxy.ts # File generated 194│ ├── idl_test_service_stub.ts # File generated 195│ └── IIdlTestService.idl # Constructed .idl file 196└── idl.exe # Executable file of IDL 197``` 198 199#### Exposing Interfaces on the Server 200 201The stub class generated by IDL is an abstract implementation of the interface class and declares all methods in the IDL file. 202 203```ts 204import {testIntTransactionCallback} from "./i_idl_test_service"; 205import {testStringTransactionCallback} from "./i_idl_test_service"; 206import IIdlTestService from "./i_idl_test_service"; 207import rpc from "@ohos.rpc"; 208 209export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdlTestService { 210 constructor(des: string) { 211 super(des); 212 } 213 214 async onRemoteRequestEx(code: number, data, reply, option): Promise<boolean> { 215 console.log("onRemoteRequestEx called, code = " + code); 216 switch(code) { 217 case IdlTestServiceStub.COMMAND_TEST_INT_TRANSACTION: { 218 let _data = data.readInt(); 219 this.testIntTransaction(_data, (errCode, returnValue) => { 220 reply.writeInt(errCode); 221 if (errCode == 0) { 222 reply.writeInt(returnValue); 223 } 224 }); 225 return true; 226 } 227 case IdlTestServiceStub.COMMAND_TEST_STRING_TRANSACTION: { 228 let _data = data.readString(); 229 this.testStringTransaction(_data, (errCode) => { 230 reply.writeInt(errCode); 231 }); 232 return true; 233 } 234 default: { 235 console.log("invalid request code" + code); 236 break; 237 } 238 } 239 return false; 240 } 241 242 testIntTransaction(data: number, callback: testIntTransactionCallback): void{} 243 testStringTransaction(data: string, callback: testStringTransactionCallback): void{} 244 245 static readonly COMMAND_TEST_INT_TRANSACTION = 1; 246 static readonly COMMAND_TEST_STRING_TRANSACTION = 2; 247} 248``` 249 250You need to inherit the interface class defined in the IDL file and implement the methods in the class. The following code snippet shows how to inherit the **IdlTestServiceStub** interface class and implement the **testIntTransaction** and **testStringTransaction** methods. 251 252```ts 253import {testIntTransactionCallback} from "./i_idl_test_service" 254import {testStringTransactionCallback} from "./i_idl_test_service" 255import IdlTestServiceStub from "./idl_test_service_stub" 256 257 258class IdlTestImp extends IdlTestServiceStub { 259 260 testIntTransaction(data: number, callback: testIntTransactionCallback): void 261 { 262 callback(0, data + 1); 263 } 264 testStringTransaction(data: string, callback: testStringTransactionCallback): void 265 { 266 callback(0); 267 } 268} 269``` 270 271After the service implements the interface, the interface needs to be exposed to the client for connection. If your service needs to expose this interface, extend **Ability** and implement **onConnect()** to return **IRemoteObject** so that the client can interact with the service process. The following code snippet shows how to expose the **IRemoteAbility** interface to the client: 272 273```ts 274export default { 275 onStart() { 276 console.info('ServiceAbility onStart'); 277 }, 278 onStop() { 279 console.info('ServiceAbility onStop'); 280 }, 281 onCommand(want, startId) { 282 console.info('ServiceAbility onCommand'); 283 }, 284 onConnect(want) { 285 console.info('ServiceAbility onConnect'); 286 try { 287 console.log('ServiceAbility want:' + typeof(want)); 288 console.log('ServiceAbility want:' + JSON.stringify(want)); 289 console.log('ServiceAbility want name:' + want.bundleName) 290 } catch(err) { 291 console.log('ServiceAbility error:' + err) 292 } 293 console.info('ServiceAbility onConnect end'); 294 return new IdlTestImp('connect'); 295 }, 296 onDisconnect(want) { 297 console.info('ServiceAbility onDisconnect'); 298 console.info('ServiceAbility want:' + JSON.stringify(want)); 299 } 300}; 301``` 302 303#### Calling Methods from the Client for IPC 304 305When the client calls **connectAbility()** to connect to a Service ability, the **onConnect** callback in **onAbilityConnectDone** of the client receives the **IRemoteObject** instance returned by the **onConnect()** method of the Service ability. The client and Service ability are in different applications. Therefore, the directory of the client application must contain a copy of the .idl file (the SDK automatically generates the proxy class). The **onConnect** callback then uses the **IRemoteObject** instance to create the **testProxy** instance of the **IdlTestServiceProxy** class and calls the related IPC method. The sample code is as follows: 306 307```ts 308import IdlTestServiceProxy from './idl_test_service_proxy' 309import featureAbility from '@ohos.ability.featureAbility'; 310 311function callbackTestIntTransaction(result: number, ret: number): void { 312 if (result == 0 && ret == 124) { 313 console.log('case 1 success'); 314 } 315} 316 317function callbackTestStringTransaction(result: number): void { 318 if (result == 0) { 319 console.log('case 2 success'); 320 } 321} 322 323var onAbilityConnectDone = { 324 onConnect:function (elementName, proxy) { 325 let testProxy = new IdlTestServiceProxy(proxy); 326 testProxy.testIntTransaction(123, callbackTestIntTransaction); 327 testProxy.testStringTransaction('hello', callbackTestStringTransaction); 328 }, 329 onDisconnect:function (elementName) { 330 console.log('onDisconnectService onDisconnect'); 331 }, 332 onFailed:function (code) { 333 console.log('onDisconnectService onFailed'); 334 } 335}; 336 337function connectAbility: void { 338 let want = { 339 bundleName: 'com.example.myapplicationidl', 340 abilityName: 'com.example.myapplicationidl.ServiceAbility' 341 }; 342 let connectionId = -1; 343 connectionId = featureAbility.connectAbility(want, onAbilityConnectDone); 344} 345 346 347``` 348 349#### Transferring a sequenceable Object During IPC 350 351You can send a class from one process to another through IPC interfaces. However, you must ensure that the peer can use the code of this class and this class supports the **marshalling** and **unmarshalling** methods. OpenHarmony uses **marshalling** and **unmarshalling** to serialize and deserialize objects into objects that can be identified by each process. 352 353**To create a class that supports the sequenceable type, perform the following operations:** 354 3551. Implement the **marshalling** method, which obtains the current state of the object and serializes the object into a **Parcel** object. 3562. Implement the **unmarshalling** method, which deserializes the object from a **Parcel** object. 357 358The following is an example of the **MySequenceable** class code: 359 360```ts 361import rpc from '@ohos.rpc'; 362export default class MySequenceable { 363 constructor(num: number, str: string) { 364 this.num = num; 365 this.str = str; 366 } 367 getNum() : number { 368 return this.num; 369 } 370 getString() : string { 371 return this.str; 372 } 373 marshalling(messageParcel) { 374 messageParcel.writeInt(this.num); 375 messageParcel.writeString(this.str); 376 return true; 377 } 378 unmarshalling(messageParcel) { 379 this.num = messageParcel.readInt(); 380 this.str = messageParcel.readString(); 381 return true; 382 } 383 private num; 384 private str; 385} 386``` 387