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

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

Các ví dụ sau cho IFoo.haltypes.hal minh hoạ các kiểu mã HIDL và cung cấp các đườ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 mang tính mô tả; tránh viết tắt quá mức. Coi từ viết tắt là từ ngữ (ví dụ: sử dụng INfc / 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 hơn một 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 chính.
    • 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 một 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 được lồng. Có thể có nhiều cấp độ lồng nhau.
  • VERSION phải là cùng một phiên bản (major.minor) như mô tả trong Phiên bản.
  • IINTERFACE_X phải là tên giao diện với UpperCamelCase/PascalCase (ví dụ: INfc) như được mô tả trong 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ả các tệp đều phải có tệp không thể thực thi quyền (trong Git).

Tên gói

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

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

Trong trường hợp:

  • PACKAGE là gói liên kết với ROOT-DIRECTORY. Cụ thể, PACKAGE là:
    • android.hardware cho các gói HIDL chính (ánh xạ tới hardware/interfaces).
    • vendor.VENDOR.hardware 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 (liên kết đến vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION có tên thư mục giống hệt nhau trong cấu trúc được mô tả trong Cấu trúc thư mục.
  • Tên gói phải viết thường. Nếu chúng dài hơn một từ, các từ này nên được sử 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 khai báo gói.

Phiên bản

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

MAJOR.MINOR

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

Nhập

Dữ liệu 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 giá trị nhập type 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 Tên gói. của gói hiện tại types.hal (nếu có) sẽ được nhập tự động (không nhập) một cách rõ ràng).

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

Chỉ sử dụng tên đủ điều kiện cho việc nhập loại do người dùng xác định chỉ khi cần thiết. Bỏ qua PACKAGE-NAME nếu loại nhập giống nhau . FQN 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 khảo bên trên giao diện dưới dạng INfcClientCallback. Nếu không, chỉ sử dụng tên đủ điều kiện.

Nhóm và sắp xếp thứ tự nhập

Sử dụng một dòng trống sau khi khai báo gói (trước khi nhập). Mỗi lần nhập phải chiếm một dòng duy nhất và không được thụt lề. Nhóm các lệnh nhập trong 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 các gói đủ điều kiện tên).
    • Mỗi nhà cung cấp phải là một nhóm.
    • Đặt hàng 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).

Sử 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 dữ liệu 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à UpperCamelCase/PascalCase tên. Giao diện có tên Bạn phải xác định IFoo trong tệp IFoo.hal. Tệp này chỉ có thể chứa đị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 kết hợp và cấu trúc

Đố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 định nghĩa cấu trúc hoặc liên kết, định nghĩa loại enum và typedef giây. Đối với các 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 chuyển giá trị enum dưới dạng đối số hàm và trả về các giá trị đó dưới dạng hàm trả về, 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 một loại enum là đượ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 những tên đủ điều kiện cho các giá trị enum, hãy 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

Tên không được có dấu cách trong một tên đủ điều kiện. Hãy sử dụng thông tin chi tiết đủ điều kiện khi cần 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 nhận xét một dòng, //, /* *//** */ đều ổn.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Dùng /* */ để nhận xét. Mặc dù HIDL hỗ trợ // cho nhận xét, chúng không được khuyến khích vì chúng không xuất hiện trong đầu ra được tạo.
  • Sử dụng /** */ cho tài liệu đã tạo. Bạn có thể áp dụng các đề xuất này chỉ để khai báo giá trị loại, phương thức, trường và giá trị enum. 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 với /** 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, căn chỉnh các dấu hoa thị. Ví dụ:
    /**
     * My multi-line
     * comment
     */
    
  • Nhật ký thay đổi và thông báo cấp phép sẽ bắt đầu một dòng mới với /* (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

Khởi động mỗi tệp bằng thông báo cấp phép thích hợp. Đối với HAL (Lớp trừu tượng phần cứng) cốt lõi, phải là giấy phép AOSP Apache trong development/docs/copyright-templates/c.txt. Hãy nhớ cập nhật năm và sử dụng nhận xét nhiều dòng theo 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, sau đó thông tin nhật ký thay đổi/thông tin tạo phiên bản. Sử dụng kiểu /* */ ghi chú nhiều dòng như đã giải thích ở trên, hãy đặt dòng trống sau dòng nhật ký thay đổi, sau đó làm theo nội dung khai báo gói.

Nhận xét về TODO

VIỆC CẦN LÀM phải bao gồm chuỗi TODO bằng tất cả chữ viết hoa theo sau là dấu hai chấm. Ví dụ:

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

Chỉ cho phép nhận xét về TODO trong quá trình phát triển; họ phải không tồn tại trong giao diện đã xuất bản.

Nhận xét về giao diện và chức năng (chuỗi tài liệu)

Sử dụng /** */ cho các chuỗi tài liệu nhiều dòng và một dòng. Không sử dụng // cho chuỗi tài liệu.

Chuỗi tài liệu cho giao diện phải mô tả cơ chế chung của giao diện, lý do thiết kế, mục đích, v.v. Tài liệu cho các hàm nên là cụ thể 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 loại tham số/giá trị trả về:

  • Bạn phải thêm @param cho mỗi tham số. Phải theo sau là tên của tham số rồi đến chuỗi tài liệu.
  • Bạn phải thêm @return cho mỗi giá trị trả về. Nó phải theo sau là tên của giá trị trả về, rồi đến chuỗi tài liệu.

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

Quy tắc định dạng chung bao gồm:

  • Độ dài dòng. Mỗi dòng văn bản chỉ được dài tối đa Dài 100 cột.
  • Khoảng trắng. Không có khoảng trắng ở cuối các dòng; dòng trống không được chứa khoảng trắng.
  • Phím cách và thẻ. Chỉ sử dụng dấu cách.
  • Tăng kích thước thụt lề. Dùng 4 dấu cách cho các khối và 8 dấu cách để xuống dòng
  • Mang. Ngoại trừ chú thích , thì dấu ngoặc nhọn open nằm trên cùng một dòng với dấu ngoặc nhọn đứng trước nhưng dấu ngoặc nhọn close và dấu chấm phẩy sau đây 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 ở đầu tệp sau khi cấp phép chú ý, nên chiếm toàn bộ dòng và không nên thụt lề. Các gói là được khai báo bằng định dạng sau (để định dạng tên, hãy xem 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 trên cùng một dòng nếu chúng phù hợp. Ví dụ:

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

Nếu chúng không vừa trên cùng một dòng, hãy thử đặt các tham số và trả về các giá trị ở cùng mức thụt lề và phân biệt generate để giúp người đọc sẽ nhanh chóng thấy các thông 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 một dòng với tên hàm.
  • Không có khoảng trắng 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à các tham số, trừ trường hợp có là những nguồn cấp dữ liệu dòng giữa chúng.
  • Nếu generates nằm trên cùng một dòng với lệnh đóng trước đó dấu ngoặc đơn, hãy sử dụng dấu cách phía trước. Nếu generates ở trên cùng một làm dấu ngoặc đơn mở tiếp theo, theo sau là một dấu cách.
  • Căn chỉnh tất cả tham số và trả về giá trị (nếu có thể).
  • Mức thụt lề mặc định là 4 dấu cách.
  • Tham số được gói được căn chỉnh với các tham số đầu tiên trên dòng trước đó, nếu không, chúng sẽ có 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ú giải chiếm toàn bộ dòng. Ví dụ:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Nếu chú thích không thể vừa trên cùng một dòng, hãy thụt lề bằng 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 trong cùng một dòng, hãy 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. Đóng địa điểm dấu ngoặc đơn ngay sau giá trị cuối cùng. Không đặt dấu ngoặc nhọn nếu có một giá trị duy nhất.

Nếu toàn bộ mảng giá trị có thể nằm vừa trong cùng một dòng, thì đừng sử dụng dấu cách sau dấu ngoặc nhọn mở và trước khi đóng dấu ngoặc nhọn, đồ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ú giải và hàm khai báo. Ví dụ:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Khai báo enum

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

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

Khai báo cấu trúc

Sử dụng các quy tắc sau để 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 trong types.hal thay vì nhúng bên trong một giao diện.
  • Sử 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 để dấu cách giữa các dòng 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à đóng dấu ngoặc vuông.
  • Đóng dấu ngoặc vuông và dấu ngoặc vuông mở tiếp theo nếu có nhiều dấu ngoặc vuông .

Ví dụ:

/* Good */
int32_t[5] array;

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

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

Vectơ

Đừng để dấu cách giữa các dòng 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 góc đó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;