• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![IDL-interface-description](./figures/IDL-interface-description.png)
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![IPC-RPC-communication-model](./figures/IPC-RPC-communication-model.png)
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&lt;T&gt;**. 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&lt;T&gt;           | 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![SDKpath](./figures/SDKpath.png)
155![SDKpath](./figures/SDKpath2.png)
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**
160>
161> Use the SDK of the latest version. The use of an earlier version may cause errors in some statements.
162
163If the executable file does not exist, download the SDK package from the mirror as instructed in the [Release Notes](../../release-notes). The following uses [3.2 Beta5](../../release-notes/OpenHarmony-v3.2-beta5.md#acquiring-source-code-from-mirrors) as an example.
164
165For details about how to replace the SDK package, see [Guide to Switching to Full SDK](../quick-start/full-sdk-switch-guide.md).
166
167After obtaining the executable file, perform subsequent development steps based on your scenario.
168
169### Development Using TS
170
171#### Creating an IDL File
172
173You can use TS to create IDL files.
174
175 For example, create a file named **IIdlTestService.idl** with the following content:
176
177```cpp
178  interface OHOS.IIdlTestService {
179      int TestIntTransaction([in] int data);
180      void TestStringTransaction([in] String data);
181      void TestMapTransaction([in] Map<int, int> data);
182      int TestArrayTransaction([in] String[] data);
183  }
184```
185
186Run the **idl -gen-ts -d *dir* -c dir/IIdlTestService.idl** command in the folder where the executable file is located.
187
188-*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.
189
190> **NOTE**
191>
192> The generated interface class file name must be the same as that of the .idl file. Otherwise, an error occurs during code generation.
193
194For example, for an .idl file named **IIdlTestService.idl** and target output directory named **IIdlTestServiceTs**, the directory structure is similar to the following:
195
196```
197├── IIdlTestServiceTs  # IDL code output folder
198│   ├── i_idl_test_service.ts  # File generated
199│   ├── idl_test_service_proxy.ts  # File generated
200│   ├── idl_test_service_stub.ts  # File generated
201│   └── IIdlTestService.idl  # Constructed .idl file
202└── idl.exe  # Executable file of IDL
203```
204
205#### Exposing Interfaces on the Server
206
207The stub class generated by IDL is an abstract implementation of the interface class and declares all methods in the IDL file.
208
209```ts
210import {testIntTransactionCallback} from "./i_idl_test_service";
211import {testStringTransactionCallback} from "./i_idl_test_service";
212import {testMapTransactionCallback} from "./i_idl_test_service";
213import {testArrayTransactionCallback} from "./i_idl_test_service";
214import IIdlTestService from "./i_idl_test_service";
215import rpc from "@ohos.rpc";
216
217export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdlTestService {
218    constructor(des: string) {
219        super(des);
220    }
221
222    async onRemoteMessageRequest(code: number, data, reply, option): Promise<boolean> {
223        console.log("onRemoteMessageRequest called, code = " + code);
224        switch(code) {
225            case IdlTestServiceStub.COMMAND_TEST_INT_TRANSACTION: {
226                let _data = data.readInt();
227                this.testIntTransaction(_data, (errCode, returnValue) => {
228                    reply.writeInt(errCode);
229                    if (errCode == 0) {
230                        reply.writeInt(returnValue);
231                    }
232                });
233                return true;
234            }
235            case IdlTestServiceStub.COMMAND_TEST_STRING_TRANSACTION: {
236                let _data = data.readString();
237                this.testStringTransaction(_data, (errCode) => {
238                    reply.writeInt(errCode);
239                });
240                return true;
241            }
242            case IdlTestServiceStub.COMMAND_TEST_MAP_TRANSACTION: {
243                let _data = new Map();
244                let _dataSize = data.readInt();
245                for (let i = 0; i < _dataSize; ++i) {
246                    let key = data.readInt();
247                    let value = data.readInt();
248                    _data.set(key, value);
249                }
250                this.testMapTransaction(_data, (errCode) => {
251                    reply.writeInt(errCode);
252                });
253                return true;
254            }
255            case IdlTestServiceStub.COMMAND_TEST_ARRAY_TRANSACTION: {
256                let _data = data.readStringArray();
257                this.testArrayTransaction(_data, (errCode, returnValue) => {
258                    reply.writeInt(errCode);
259                    if (errCode == 0) {
260                        reply.writeInt(returnValue);
261                    }
262                });
263                return true;
264            }
265            default: {
266                console.log("invalid request code" + code);
267                break;
268            }
269        }
270        return false;
271    }
272
273    testIntTransaction(data: number, callback: testIntTransactionCallback): void{}
274    testStringTransaction(data: string, callback: testStringTransactionCallback): void{}
275    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void{}
276    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void{}
277
278    static readonly COMMAND_TEST_INT_TRANSACTION = 1;
279    static readonly COMMAND_TEST_STRING_TRANSACTION = 2;
280    static readonly COMMAND_TEST_MAP_TRANSACTION = 3;
281    static readonly COMMAND_TEST_ARRAY_TRANSACTION = 4;
282}
283```
284
285You 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**, **testStringTransaction**, **testMapTransaction**, and **testArrayTransaction** methods.
286
287```ts
288import {testIntTransactionCallback} from "./i_idl_test_service"
289import {testStringTransactionCallback} from "./i_idl_test_service"
290import {testMapTransactionCallback} from "./i_idl_test_service";
291import {testArrayTransactionCallback} from "./i_idl_test_service";
292import IdlTestServiceStub from "./idl_test_service_stub"
293
294
295class IdlTestImp extends IdlTestServiceStub {
296
297    testIntTransaction(data: number, callback: testIntTransactionCallback): void
298    {
299        callback(0, data + 1);
300    }
301    testStringTransaction(data: string, callback: testStringTransactionCallback): void
302    {
303        callback(0);
304    }
305    testMapTransaction(data: Map<number, number>, callback: testMapTransactionCallback): void
306    {
307        callback(0);
308    }
309    testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void
310    {
311        callback(0, 1);
312    }
313}
314```
315
316After 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:
317
318```ts
319export default {
320    onStart() {
321        console.info('ServiceAbility onStart');
322    },
323    onStop() {
324        console.info('ServiceAbility onStop');
325    },
326    onCommand(want, startId) {
327        console.info('ServiceAbility onCommand');
328    },
329    onConnect(want) {
330        console.info('ServiceAbility onConnect');
331        try {
332            console.log('ServiceAbility want:' + typeof(want));
333            console.log('ServiceAbility want:' + JSON.stringify(want));
334            console.log('ServiceAbility want name:' + want.bundleName)
335        } catch(err) {
336            console.log('ServiceAbility error:' + err)
337        }
338        console.info('ServiceAbility onConnect end');
339        return new IdlTestImp('connect');
340    },
341    onDisconnect(want) {
342        console.info('ServiceAbility onDisconnect');
343        console.info('ServiceAbility want:' + JSON.stringify(want));
344    }
345};
346```
347
348#### Calling Methods from the Client for IPC
349
350When 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:
351
352```ts
353import IdlTestServiceProxy from './idl_test_service_proxy'
354import featureAbility from '@ohos.ability.featureAbility';
355
356function callbackTestIntTransaction(result: number, ret: number): void {
357  if (result == 0 && ret == 124) {
358    console.log('case 1 success');
359  }
360}
361
362function callbackTestStringTransaction(result: number): void {
363  if (result == 0) {
364    console.log('case 2 success');
365  }
366}
367
368function callbackTestMapTransaction(result: number): void {
369  if (result == 0) {
370    console.log('case 3 success');
371  }
372}
373
374function callbackTestArrayTransaction(result: number, ret: number): void {
375  if (result == 0 && ret == 124) {
376    console.log('case 4 success');
377  }
378}
379
380var onAbilityConnectDone = {
381  onConnect:function (elementName, proxy) {
382    let testProxy = new IdlTestServiceProxy(proxy);
383    let testMap = new Map();
384    testMap.set(1, 1);
385    testMap.set(1, 2);
386    testProxy.testIntTransaction(123, callbackTestIntTransaction);
387    testProxy.testStringTransaction('hello', callbackTestStringTransaction);
388    testProxy.testMapTransaction(testMap, callbackTestMapTransaction);
389    testProxy.testArrayTransaction(['1','2'], callbackTestMapTransaction);
390  },
391  onDisconnect:function (elementName) {
392    console.log('onDisconnectService onDisconnect');
393  },
394  onFailed:function (code) {
395    console.log('onDisconnectService onFailed');
396  }
397};
398
399function connectAbility: void {
400    let want = {
401        bundleName: 'com.example.myapplicationidl',
402        abilityName: 'com.example.myapplicationidl.ServiceAbility'
403    };
404    let connectionId = -1;
405    connectionId = featureAbility.connectAbility(want, onAbilityConnectDone);
406}
407
408
409```
410
411#### Transferring a sequenceable Object During IPC
412
413You 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.
414
415**To create a class that supports the sequenceable type, perform the following operations:**
416
4171. Implement the **marshalling** method, which obtains the current state of the object and serializes the object into a **Parcel** object.
4182. Implement the **unmarshalling** method, which deserializes the object from a **Parcel** object.
419
420The following is an example of the **MySequenceable** class code:
421
422```ts
423import rpc from '@ohos.rpc';
424export default class MySequenceable {
425    constructor(num: number, str: string) {
426        this.num = num;
427        this.str = str;
428    }
429    getNum() : number {
430        return this.num;
431    }
432    getString() : string {
433        return this.str;
434    }
435    marshalling(messageParcel) {
436        messageParcel.writeInt(this.num);
437        messageParcel.writeString(this.str);
438        return true;
439    }
440    unmarshalling(messageParcel) {
441        this.num = messageParcel.readInt();
442        this.str = messageParcel.readString();
443        return true;
444    }
445    private num;
446    private str;
447}
448```
449