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

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

Các ví dụ sau đây cho IFoo.haltypes.hal minh họa các kiểu mã HIDL và cung cấp các liên kết nhanh đến 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á nhiều. Hãy coi các từ viết tắt là từ (ví dụ: sử dụng INfc thay vì INFC ).

Cấu trúc thư mục và đặt tên file

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

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (tùy chọn, có thể nhiều hơn một cấp độ)
        • VERSION
          • Android.mk
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_N .hal
          • types.hal (tùy chọn)

Ở đâu:

  • ROOT-DIRECTORY là:
    • hardware/interfaces cho các gói HIDL lõi.
    • vendor/ VENDOR /interfaces cho các gói của nhà cung cấp, trong đó VENDOR đề cập đến 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 hơn một từ, hãy sử dụng SUBMODULE lồng nhau. Có thể có nhiều hơn một cấp độ lồng nhau.
  • VERSION phải giống hệt phiên bản (major.minor) như được mô tả trong Versions .
  • I INTERFACE_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 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 (được gọi là PACKAGE-NAME ):

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

Ở đâu:

  • PACKAGE là gói ánh xạ tới ROOT-DIRECTORY . Đặc biệt, PACKAGE là:
    • android.hardware cho các gói HIDL cốt lõi (ánh xạ tới hardware/interfaces ).
    • vendor. VENDOR .hardware dành cho các gói của nhà cung cấp, trong đó VENDOR đề cập đến nhà cung cấp SoC hoặc OEM/ODM (ánh xạ tới 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 Cấu trúc thư mục .
  • Tên gói phải là chữ thường. Nếu chúng dài hơn một từ thì các từ đó phải được sử dụng dưới dạng mô-đun con hoặc được viết bằng snake_case .
  • Không có khoảng trống được cho phép.

FQN luôn được sử dụng trong khai báo gói.

Phiên bản

Các 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 số nguyên duy nhất. HIDL sử dụng các quy tắc phiên bản ngữ nghĩa .

Nhập khẩu

Quá trình nhập có một trong ba định dạng sau:

  • Nhập khẩu trọn 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 các loại: import PACKAGE-NAME ::types;

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

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

Chỉ sử dụng tên đủ điều kiện để nhập loại do người dùng xác định khi cần thiết. Bỏ qua PACKAGE-NAME nếu loại nhập nằm trong cùng một gói. 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 bên dưới android.hardware.nfc@1.0 , hãy tham khảo giao diện trên là INfcClientCallback . Nếu không, chỉ sử dụng tên đủ điều kiện.

Phân nhóm và đặt hàng nhập khẩu

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ậ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. vendor. VENDOR Gói vendor. VENDOR (sử dụng tên đầy đủ).
    • Mỗi nhà cung cấp nên 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 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. Trong mỗi nhóm, 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 chữ I , theo sau là tên UpperCamelCase / PascalCase . Giao diện có tên IFoo phải được xác định 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 I NAME phải ở I NAME .hal ).

Chức năng

Đố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/công đoàn

Đối với tên trường struct/union, hãy sử dụng lowerCamelCase . Ví dụ:

struct FooReply {
    vec<uint8_t> replyData;
}

Nhập tên

Tên loại tham chiếu đến định nghĩa struct/union, định nghĩa loại enum và typedef s. Đố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ị liệt kê

Giá trị enum phải là UPPER_CASE_WITH_UNDERSCORES . Khi truyền các giá trị enum làm đối số hàm và trả về chúng 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 ý: Kiểu cơ bản của kiểu enum được khai báo rõ ràng sau dấu hai chấm. Vì nó không phụ thuộc vào trình biên dịch nên việc sử dụng kiểu 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, dấu hai chấm được sử dụng 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ó khoảng trắng bên trong một tên đủ điều kiện. Chỉ sử dụng tên đủ điều kiện khi cần thiết và bỏ qua những 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, // , /* *//** */ đều ổn.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Sử dụng /* */ để nhận xét. Mặc dù HIDL hỗ trợ // cho các nhận xét nhưng 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 được tạo. Chúng chỉ có thể được áp dụng cho các khai báo kiểu, 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 bằng /** trên một dòng riêng biệt. Sử dụng * ở đầu mỗi dòng. Kết thúc nhận xét 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
     */
    
  • 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 (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 tệp

Bắt đầu mỗi tệp với 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 AOSP Apache trong development/docs/copyright-templates/c.txt . Hãy nhớ cập nhật năm và sử dụng /* */ tạo kiểu nhận xét nhiều dòng như đã giải thích ở trên.

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

bình luận TODO

TODO phải bao gồm chuỗi TODO viết hoa tất cả các chữ cái theo sau là dấu hai chấm. Ví dụ:

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

Nhận xét TODO chỉ được phép trong quá trình phát triển; chúng không được tồn tại trong các giao diện được xuất bản.

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

Sử dụng /** */ cho 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á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 các chức năng phải dành riêng cho chức năng (tài liệu cấp gói có 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 s và @return s cho mỗi tham số/giá trị trả về:

  • @param phải được thêm vào cho mỗi tham số. Theo sau nó là tên của tham số, sau đó là chuỗi tài liệu.
  • @return phải được thêm vào cho mỗi giá trị trả về. Theo sau nó phải là tên của giá trị trả về, sau đó là 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);

Định dạng

Các quy tắc định dạng chung bao gồm:

  • Chiều 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; dòng trống không được chứa khoảng trắng.
  • Dấu cách và tab . Chỉ sử dụng khoảng trắng.
  • Kích thước thụt lề . Sử dụng 4 khoảng trắng cho các khối và 8 khoảng trắng để ngắt dòng
  • 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 một dòng với mã trước đó nhưng dấu ngoặc nhọn đóng và dấu chấm phẩy sau chiếm toàn bộ dòng. Ví dụ:
    interface INfc {
        close();
    };
    

Khai báo gói hàng

Khai báo gói phải ở đầu tệp sau thông báo giấy 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 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 chúng khớ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 cố gắng đặt các tham số và giá trị trả về ở cùng mức thụt lề và generate phân biệt để giúp người đọc nhanh chóng nhìn thấy 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);
}

Chi tiết bổ sung:

  • Dấu ngoặc đơn mở luôn ở cùng dòng với tên hàm.
  • Không có khoảng cách giữa tên hàm và dấu ngoặc đơn mở.
  • Không có khoảng cách giữa dấu ngoặc đơn và tham số trừ khi có dòng cấp dữ liệu giữa chúng.
  • 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 khoảng trắng trước đó. Nếu generates nằm trên cùng dòng với dấu ngoặc đơn mở tiếp theo, hãy thêm dấu cách vào sau.
  • Căn chỉnh tất cả các tham số và giá trị trả về (nếu có thể).
  • Thụt lề mặc định là 4 dấu cách.
  • Các tham số được gói được căn chỉnh theo các tham số đầu tiên trên dòng trước đó, nếu không chúng sẽ 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 các chú thích theo thứ tự bảng chữ cái và sử dụng khoảng trắng xung quanh các 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ú 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ể vừa trong 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. Khô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 trong cùng một dòng, khô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 và 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à 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 để khai báo enum:

  • Nếu các khai báo enum được chia sẻ với một gói khác, hãy đặt các khai báo trong types.hal thay vì nhúng vào trong một giao diện.
  • Sử dụng khoảng trắng trước và sau dấu hai chấm và khoảng trắng sau loại cơ bản trước dấu ngoặc nhọn.
  • Giá trị enum cuối cùng có thể có hoặc 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 các khai báo cấu trúc được chia sẻ với một gói khác, hãy đặt các khai báo trong types.hal thay vì nhúng vào bên trong một giao diện.
  • Sử dụng khoảng trắng sau tên loại cấu trúc trước dấu ngoặc nhọn.
  • Căn chỉnh tên trường (tùy chọn). Ví dụ:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }
    

Khai báo mảng

Không đặt dấu cách giữa các mục 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.
  • Đóng dấu ngoặc vuông và dấu ngoặc vuông mở tiếp theo, nếu có nhiều hơn một chiều.

Ví dụ:

/* Good */
int32_t[5] array;

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

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

Vectơ

Không đặt dấu cách giữa các mục sau:

  • vec và khung góc mở.
  • Dấu ngoặc nhọn 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 ( 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;