Hướng dẫn về kiểu mã

Kiểu mã HIDL giống với mã C++ trong khung Android, với dấu thụt lề 4 dấu cách và tên tệp viết hoa chữ cái đầu. Nội dung khai báo gói, nhập và docstring tương tự như trong Java, với một số sửa đổi nhỏ.

Các ví dụ sau đây về IFoo.haltypes.hal minh hoạ các kiểu mã HIDL và cung cấp đường liên kết nhanh đến thông tin chi tiết về từng kiểu (IFooClientCallback.hal, IBar.halIBaz.hal đã bị bỏ qua).

hardware/interfaces/foo/1.0/IFoo.hal
/*
 * (License Notice)
 */

package android.hardware.foo@1.0;

import android.hardware.bar@1.0::IBar;

import IBaz;
import IFooClientCallback;

/**
 * IFoo is an interface that*/
interface IFoo {

    /**
     * This is a multiline docstring.
     *
     * @return result 0 if successful, nonzero otherwise.
     */
     foo() generates (FooStatus result);

    /**
     * Restart controller by power cycle.
     *
     * @param bar callback interface that* @return result 0 if successful, nonzero otherwise.
     */
    powerCycle(IBar bar) generates (FooStatus result);

    /** Single line docstring. */
    baz();


    /**
     * The bar function.
     *
     * @param clientCallback callback after function is called
     * @param baz related baz object
     * @param data input data blob
     */
    bar(IFooClientCallback clientCallback,
        IBaz baz,
        FooData data);

};
hardware/interfaces/foo/1.0/types.hal
/*
 * (License Notice)
 */

package android.hardware.foo@1.0;

/** Replied status. */
enum Status : int32_t {
    OK,
    /* invalid arguments */
    ERR_ARG,
    /* note, no transport related errors */
    ERR_UNKNOWN = -1,
};

struct ArgData {
    int32_t[20]  someArray;
    vec<uint8_t> data;
};

Quy ước đặt tên

Tên hàm, tên biến và tên tệp phải mô tả; tránh viết tắt quá mức. Xem từ viết tắt như từ (ví dụ: sử dụng INfc thay vì INFC).

Cấu trúc thư mục và cách đặt tên tệp

Cấu trúc thư mục sẽ xuất hiện như sau:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (không bắt buộc, có thể có nhiều cấp)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (không bắt buộc)

Trong trường hợp:

  • ROOT-DIRECTORY là:
    • hardware/interfaces cho các gói HIDL cốt lõi.
    • vendor/VENDOR/interfaces cho các gói của nhà cung cấp, trong đó VENDOR đề cập đến một nhà cung cấp SoC hoặc OEM/ODM.
  • MODULE phải là một từ viết thường mô tả hệ thống con (ví dụ: nfc). Nếu cần nhiều từ, hãy sử dụng SUBMODULE lồng nhau. Có thể có nhiều cấp độ lồng ghép.
  • VERSION phải là phiên bản giống hệt nhau (chính.phụ) như mô tả trong phần Phiên bản.
  • IINTERFACE_X phải là tên giao diện với UpperCamelCase/PascalCase (ví dụ: INfc) như mô tả trong phần Tên giao diện.

Ví dụ:

  • hardware/interfaces
    • nfc
      • 1.0
        • Android.mk
        • INfc.hal
        • INfcClientCallback.hal
        • types.hal

Lưu ý: Tất cả tệp phải có quyền không thể thực thi (trong Git).

Tên gói

Tên gói phải sử dụng định dạng tên đủ điều kiện (FQN) sau đây (gọi là PACKAGE-NAME):

PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[]]]@VERSION

Trong trường hợp:

  • PACKAGE là gói ánh xạ đến ROOT-DIRECTORY. Cụ thể, PACKAGE là:
    • android.hardware cho các gói HIDL cốt lõi (liên kết với hardware/interfaces).
    • vendor.VENDOR.hardware cho các gói của nhà cung cấp, trong đó VENDOR là nhà cung cấp SoC hoặc OEM/ODM (ánh xạ đến vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION là tên thư mục giống hệt nhau trong cấu trúc được mô tả trong phần Cấu trúc thư mục.
  • Tên gói phải viết bằng chữ thường. Nếu có nhiều từ, các từ đó phải được dùng làm mô-đun con hoặc được viết trong snake_case.
  • Không được dùng dấu cách.

FQN luôn được dùng trong phần khai báo gói.

Phiên bản

Phiên bản phải có định dạng như sau:

MAJOR.MINOR

Cả phiên bản MAJORMINOR đều phải là một số nguyên duy nhất. HIDL sử dụng các quy tắc phiên bản ngữ nghĩa.

Nhập

Tệp nhập có một trong ba định dạng sau:

  • Nhập toàn bộ gói: import PACKAGE-NAME;
  • Nhập một phần: import PACKAGE-NAME::UDT; (hoặc nếu loại đã nhập nằm trong cùng một gói,import UDT;
  • Chỉ nhập loại: import PACKAGE-NAME::types;

PACKAGE-NAME tuân theo định dạng trong phần Tên gói. types.hal của gói hiện tại (nếu có) sẽ tự động được nhập (không nhập rõ ràng).

Tên đủ điều kiện (FQN)

Chỉ sử dụng tên đủ điều kiện cho một loại nhập do người dùng xác định khi cần. Bỏ qua PACKAGE-NAME nếu loại nhập nằm trong cùng một gói. Tên đầy đủ không được chứa dấu cách. Ví dụ về tên đủ điều kiện:

android.hardware.nfc@1.0::INfcClientCallback

Trong một tệp khác trong android.hardware.nfc@1.0, hãy tham chiếu đến giao diện ở trên là INfcClientCallback. Nếu không, hãy chỉ sử dụng tên đủ điều kiện.

Nhóm và sắp xếp các lệnh nhập

Sử dụng một dòng trống sau phần khai báo gói (trước phần nhập). Mỗi lệnh nhập phải chiếm một dòng và không được thụt lề. Nhập nhóm theo thứ tự sau:

  1. Các gói android.hardware khác (sử dụng tên đủ điều kiện).
  2. Các gói vendor.VENDOR khác (sử dụng tên đủ điều kiện).
    • Mỗi nhà cung cấp phải là một nhóm.
    • Sắp xếp nhà cung cấp theo thứ tự bảng chữ cái.
  3. Nhập từ các giao diện khác trong cùng một gói (sử dụng tên đơn giản).

Dùng một dòng trống giữa các nhóm. Bên trong mỗi nhóm, hãy sắp xếp các tệp nhập theo thứ tự bảng chữ cái. Ví dụ:

import android.hardware.nfc@1.0::INfc;
import android.hardware.nfc@1.0::INfcClientCallback;

/* Importing the whole module. */
import vendor.barvendor.bar@3.1;

import vendor.foovendor.foo@2.2::IFooBar;
import vendor.foovendor.foo@2.2::IFooFoo;

import IBar;
import IFoo;

Tên giao diện

Tên giao diện phải bắt đầu bằng I, theo sau là tên UpperCamelCase/PascalCase. Bạn phải xác định một giao diện có tên IFoo trong tệp IFoo.hal. Tệp này chỉ có thể chứa các định nghĩa cho giao diện IFoo (giao diện INAME phải nằm trong INAME.hal).

Hàm

Đối với tên hàm, đối số và tên biến trả về, hãy sử dụng lowerCamelCase. Ví dụ:

open(INfcClientCallback clientCallback) generates (int32_t retVal);
oneway pingAlive(IFooCallback cb);

Tên trường cấu trúc và liên kết

Đối với tên trường cấu trúc hoặc liên kết, hãy sử dụng lowerCamelCase. Ví dụ:

struct FooReply {
    vec<uint8_t> replyData;
}

Nhập tên

Tên loại đề cập đến các định nghĩa về cấu trúc hoặc liên kết, định nghĩa loại enum và typedef. Đối với những tên này, hãy sử dụng UpperCamelCase/PascalCase. Ví dụ:

enum NfcStatus : int32_t {
    /*...*/
};
struct NfcData {
    /*...*/
};

Giá trị enum

Giá trị enum phải là UPPER_CASE_WITH_UNDERSCORES. Khi truyền giá trị enum dưới dạng đối số hàm và trả về dưới dạng giá trị trả về của hàm, hãy sử dụng loại enum thực tế (không phải loại số nguyên cơ bản). Ví dụ:

enum NfcStatus : int32_t {
    HAL_NFC_STATUS_OK               = 0,
    HAL_NFC_STATUS_FAILED           = 1,
    HAL_NFC_STATUS_ERR_TRANSPORT    = 2,
    HAL_NFC_STATUS_ERR_CMD_TIMEOUT  = 3,
    HAL_NFC_STATUS_REFUSED          = 4
};

Lưu ý: Loại cơ bản của loại enum được khai báo rõ ràng sau dấu hai chấm. Vì không phụ thuộc vào trình biên dịch, nên việc sử dụng loại enum thực tế sẽ rõ ràng hơn.

Đối với tên đủ điều kiện cho các giá trị enum, bạn có thể sử dụng dấu hai chấm giữa tên loại enum và tên giá trị enum:

PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME

Không được có dấu cách bên trong tên đủ điều kiện. Chỉ sử dụng tên đủ điều kiện khi cần thiết và bỏ qua các phần không cần thiết. Ví dụ:

android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK

Bình luận

Đối với một nhận xét một dòng, bạn có thể sử dụng //, /* *//** */.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Sử dụng /* */ cho các nhận xét. Mặc dù HIDL hỗ trợ // cho các nhận xét, nhưng bạn không nên sử dụng vì các nhận xét này không xuất hiện trong kết quả được tạo.
  • Sử dụng /** */ cho tài liệu được tạo. Bạn chỉ có thể áp dụng các loại này cho nội dung khai báo giá trị enum, phương thức, trường và loại. Ví dụ:
    /** Replied status */
    enum TeleportStatus {
        /** Object entirely teleported. */
        OK              = 0,
        /** Methods return this if teleportation is not completed. */
        ERROR_TELEPORT  = 1,
        /**
         * Teleportation could not be completed due to an object
         * obstructing the path.
         */
        ERROR_OBJECT    = 2,
        ...
    }
  • Bắt đầu nhận xét nhiều dòng bằng /** trên một dòng riêng. Sử dụng * ở đầu mỗi dòng. Kết thúc ghi chú bằng */ trên một dòng riêng biệt, căn chỉnh các dấu hoa thị. Ví dụ:
    /**
     * My multi-line
     * comment
     */
  • Thông báo cấp phép và nhật ký thay đổi phải bắt đầu một dòng mới bằng /* (một dấu hoa thị), sử dụng * ở đầu mỗi dòng và đặt */ trên dòng cuối cùng (các dấu hoa thị phải căn chỉnh). Ví dụ:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

Nhận xét về tệp

Bắt đầu mỗi tệp bằng thông báo cấp phép thích hợp. Đối với các HAL cốt lõi, đây phải là giấy phép Apache AOSP trong development/docs/copyright-templates/c.txt. Hãy nhớ cập nhật năm và sử dụng ghi chú nhiều dòng kiểu /* */ như đã giải thích ở trên.

Bạn có thể tuỳ ý đặt một dòng trống sau thông báo giấy phép, theo sau là thông tin nhật ký thay đổi/phiên bản. Sử dụng chú thích nhiều dòng kiểu /* */ như đã giải thích ở trên, đặt dòng trống sau nhật ký thay đổi, sau đó tiếp theo là phần khai báo gói.

Bình luận TODO

TODO phải bao gồm chuỗi TODO viết hoa toàn bộ, theo sau là dấu hai chấm. Ví dụ:

// TODO: remove this code before foo is checked in.

Bạn chỉ được phép sử dụng nhận xét TODO trong quá trình phát triển; các nhận xét này không được xuất hiện trong giao diện đã xuất bản.

Nhận xét về giao diện và hàm (docstring)

Sử dụng /** */ cho chuỗi ký tự nhiều dòng và một dòng. Đừng sử dụng // cho chuỗi ký tự.

Chuỗi tài liệu cho giao diện phải mô tả các cơ chế chung của giao diện, lý do thiết kế, mục đích, v.v. Chuỗi tài liệu cho hàm phải dành riêng cho hàm đó (tài liệu cấp gói nằm trong tệp README trong thư mục gói).

/**
 * IFooController is the controller for foos.
 */
interface IFooController {
    /**
     * Opens the controller.
     *
     * @return status HAL_FOO_OK if successful.
     */
    open() generates (FooStatus status);

    /** Close the controller. */
    close();
};

Bạn phải thêm @param@return cho mỗi tham số/giá trị trả về:

  • Bạn phải thêm @param cho mỗi tham số. Tiếp theo là tên của tham số rồi đến docstring.
  • Bạn phải thêm @return cho mỗi giá trị trả về. Theo sau là tên của giá trị trả về, sau đó là docstring.

Ví dụ:

/**
 * Explain what foo does.
 *
 * @param arg1 explain what arg1 is
 * @param arg2 explain what arg2 is
 * @return ret1 explain what ret1 is
 * @return ret2 explain what ret2 is
 */
foo(T arg1, T arg2) generates (S ret1, S ret2);

Quy tắc định dạng

Sau đây là một số quy tắc định dạng chung:

  • Độ dài dòng. Mỗi dòng văn bản phải dài tối đa 100 cột.
  • Khoảng trắng. Không có khoảng trắng ở cuối dòng; các dòng trống không được chứa khoảng trắng.
  • Không gian so với thẻ. Chỉ sử dụng dấu cách.
  • Kích thước thụt lề. Sử dụng 4 dấu cách cho các khối và 8 dấu cách cho các dòng ngắt
  • Giằng. Ngoại trừ các giá trị chú thích, dấu ngoặc nhọn mở nằm trên cùng dòng với mã trước đó, nhưng dấu ngoặc nhọn đóng và dấu chấm phẩy theo sau chiếm toàn bộ dòng. Ví dụ:
    interface INfc {
        close();
    };

Khai báo gói

Phần khai báo gói phải nằm ở đầu tệp sau thông báo cấp phép, phải chiếm toàn bộ dòng và không được thụt lề. Các gói được khai báo bằng định dạng sau (để định dạng tên, hãy xem phần Tên gói):

package PACKAGE-NAME;

Ví dụ:

package android.hardware.nfc@1.0;

Khai báo hàm

Tên hàm, tham số, generates và giá trị trả về phải nằm trên cùng một dòng nếu vừa. Ví dụ:

interface IFoo {
    /** ... */
    easyMethod(int32_t data) generates (int32_t result);
};

Nếu các tham số và giá trị trả về không nằm trên cùng một dòng, hãy cố gắng đặt các tham số và giá trị trả về ở cùng một cấp thụt lề và phân biệt generate để giúp người đọc nhanh chóng xem các tham số và giá trị trả về. Ví dụ:

interface IFoo {
    suchALongMethodThatCannotFitInOneLine(int32_t theFirstVeryLongParameter,
                                          int32_t anotherVeryLongParameter);
    anEvenLongerMethodThatCannotFitInOneLine(int32_t theFirstLongParameter,
                                             int32_t anotherVeryLongParameter)
                                  generates (int32_t theFirstReturnValue,
                                             int32_t anotherReturnValue);
    superSuperSuperSuperSuperSuperSuperLongMethodThatYouWillHateToType(
            int32_t theFirstVeryLongParameter, // 8 spaces
            int32_t anotherVeryLongParameter
        ) generates (
            int32_t theFirstReturnValue,
            int32_t anotherReturnValue
        );
    /* method name is even shorter than 'generates' */
    foobar(AReallyReallyLongType aReallyReallyLongParameter,
           AReallyReallyLongType anotherReallyReallyLongParameter)
        generates (ASuperLongType aSuperLongReturnValue, // 4 spaces
                   ASuperLongType anotherSuperLongReturnValue);
}

Thông tin chi tiết bổ sung:

  • Dấu ngoặc đơn mở luôn nằm trên cùng dòng với tên hàm.
  • Không có dấu cách giữa tên hàm và dấu ngoặc đơn mở.
  • Không có khoảng trắng giữa dấu ngoặc đơn và tham số ngoại trừ khi có dấu xuống dòng giữa các tham số đó.
  • Nếu generates nằm trên cùng dòng với dấu ngoặc đơn đóng trước đó, hãy sử dụng dấu cách ở phía trước. Nếu generates nằm trên cùng một dòng với dấu ngoặc đơn mở tiếp theo, hãy thêm một dấu cách.
  • Căn chỉnh tất cả tham số và giá trị trả về (nếu có thể).
  • Mức thụt lề mặc định là 4 dấu cách.
  • Các tham số được gói được căn chỉnh với tham số đầu tiên trên dòng trước, nếu không, các tham số này sẽ có khoảng thụt lề 8 dấu cách.

Chú thích

Sử dụng định dạng sau cho chú thích:

@annotate(keyword = value, keyword = {value, value, value})

Sắp xếp chú thích theo thứ tự bảng chữ cái và sử dụng dấu cách xung quanh dấu bằng. Ví dụ:

@callflow(key = value)
@entry
@exit

Đảm bảo chú thích chiếm toàn bộ dòng. Ví dụ:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Nếu chú giải không nằm vừa trên cùng một dòng, hãy thụt lề 8 dấu cách. Ví dụ:

@annotate(
        keyword = value,
        keyword = {
                value,
                value
        },
        keyword = value)

Nếu toàn bộ mảng giá trị không thể nằm vừa trên cùng một dòng, hãy đặt dấu ngắt dòng sau dấu ngoặc nhọn mở { và sau mỗi dấu phẩy bên trong mảng. Đặt dấu ngoặc đơn đóng ngay sau giá trị cuối cùng. Đừng đặt dấu ngoặc nhọn nếu chỉ có một giá trị.

Nếu toàn bộ mảng giá trị có thể nằm vừa trên cùng một dòng, đừng sử dụng dấu cách sau dấu ngoặc nhọn mở và trước dấu ngoặc nhọn đóng, đồng thời sử dụng một dấu cách sau mỗi dấu phẩy. Ví dụ:

/* Good */
@callflow(key = {"val", "val"})

/* Bad */
@callflow(key = { "val","val" })

KHÔNG được có dòng trống giữa chú thích và phần khai báo hàm. Ví dụ:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Khai báo enum

Sử dụng các quy tắc sau đây để khai báo enum:

  • Nếu bạn chia sẻ nội dung khai báo enum với một gói khác, hãy đặt nội dung khai báo đó vào types.hal thay vì nhúng vào giao diện.
  • Dùng dấu cách trước và sau dấu hai chấm, cũng như dấu cách sau loại cơ bản trước dấu ngoặc nhọn mở.
  • Giá trị enum cuối cùng có thể không có dấu phẩy thừa.

Khai báo cấu trúc

Sử dụng các quy tắc sau đây để khai báo cấu trúc:

  • Nếu nội dung khai báo cấu trúc được chia sẻ với một gói khác, hãy đặt nội dung khai báo đó vào types.hal thay vì nhúng vào bên trong một giao diện.
  • Dùng dấu cách sau tên loại cấu trúc trước dấu ngoặc nhọn mở.
  • Căn chỉnh tên trường (không bắt buộc). Ví dụ:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

Khai báo mảng

Đừng đặt dấu cách giữa những nội dung sau:

  • Loại phần tử và dấu ngoặc vuông mở.
  • Mở dấu ngoặc vuông và kích thước mảng.
  • Kích thước mảng và dấu ngoặc vuông đóng.
  • Dấu ngoặc vuông đóng và dấu ngoặc vuông mở tiếp theo, nếu có nhiều kích thước.

Ví dụ:

/* Good */
int32_t[5] array;

/* Good */
int32_t[5][6] multiDimArray;

/* Bad */
int32_t [ 5 ] [ 6 ] array;

Vectơ

Đừng đặt dấu cách giữa những nội dung sau:

  • vec và dấu ngoặc góc mở.
  • Dấu ngoặc góc mở và loại phần tử (Ngoại lệ: loại phần tử cũng là vec).
  • Loại phần tử và dấu ngoặc nhọn đóng (Ngoại lệ: loại phần tử cũng là vec).

Ví dụ:

/* Good */
vec<int32_t> array;

/* Good */
vec<vec<int32_t>> array;

/* Good */
vec< vec<int32_t> > array;

/* Bad */
vec < int32_t > array;

/* Bad */
vec < vec < int32_t > > array;