程式碼樣式指南

HIDL 程式碼樣式與 Android 架構中的 C++ 程式碼類似,且有 4 個空格 縮排和混合大小寫的檔案名稱。套件宣告、匯入項目和 docstring 與 Java 中的映像檔類似,只是稍做修改。

以下是 IFoo.haltypes.hal 的範例 說明 HIDL 程式碼樣式,並提供快速連結,方便您查看每種樣式的詳情 (IFooClientCallback.halIBar.hal和 已省略 IBaz.hal)。

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;
};

命名慣例

請使用描述性字詞名稱、變數名稱和檔案名稱;避開 。將縮寫詞視為字詞 (例如,改用 INfc (共 INFC 個)。

目錄結構和檔案命名

目錄結構應如下所示:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (選用,可以是多個值) 等級)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (非必要)

地點:

  • ROOT-DIRECTORY 是:
    • hardware/interfaces:適用於核心 HIDL 套件。
    • 針對供應商套裝方案為 vendor/VENDOR/interfaces, 其中 VENDOR 是指 SoC 供應商或 原始設備製造商 (OEM)/ODM。
  • MODULE 應為以下描述的一個小寫字詞: 子系統 (例如 nfc)。如需輸入多個字詞,請使用 巢狀 SUBMODULE。可以使用多個等級 建立巢狀結構
  • VERSION 應完全相同 (major.minor),詳情請參閱「版本」。
  • IINTERFACE_X 應為含有 UpperCamelCase/PascalCase (例如 INfc) ,如同「介面名稱」中所述。

例子:

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

注意:所有檔案都必須不是執行檔 權限 (位於 Git 中)

套件名稱

套件名稱必須使用以下完整名稱 (FQN) 格式 (稱為 PACKAGE-NAME):

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

地點:

  • PACKAGE 是對應至 ROOT-DIRECTORY。我們要用 PACKAGE 是:
    • 核心 HIDL 套件的 android.hardware (對應至以下項目: hardware/interfaces)。
    • vendor.VENDOR.hardware 適用於供應商套件,其中 VENDOR 是指 SoC 供應商或原始設備製造商 (OEM)/ODM (對應) 至 vendor/VENDOR/interfaces)。
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION 是跟先前在語句中所述 目錄結構
  • 套件名稱應使用小寫。如果長度超過一個字 字詞應做為子模組或以 snake_case 編寫。
  • 不得包含空格。

套件宣告一律使用 FQN。

版本

版本必須採用以下格式:

MAJOR.MINOR

MAJORMINOR 版本應為單一版本 整數值。HIDL 使用語意 版本管理規則。

匯入

匯入作業有下列三種格式之一:

  • 整個套件匯入作業:import PACKAGE-NAME;
  • 部分匯入:import PACKAGE-NAME::UDT; (如果已匯入 屬於同一套件、import UDT;
  • 僅限類型匯入:import PACKAGE-NAME::types;

PACKAGE-NAME 會採用以下格式: 套件名稱。目前套件的 types.hal (如果有的話) 會自動匯入 (請勿匯入) )。

完整名稱 (FQN)

請只在必要時,才使用完整名稱匯入使用者定義的類型。 如果匯入類型相同,請省略 PACKAGE-NAME 套件。FQN 不得包含空格。完整名稱範例:

android.hardware.nfc@1.0::INfcClientCallback

android.hardware.nfc@1.0 的另一個檔案中,參照 視為 INfcClientCallback否則,請僅使用 完整名稱。

將匯入分組和排序

請在套件宣告之後 (匯入前) 使用空白行。每次匯入 應佔據單行,且不應縮排。將群組匯入 以下順序:

  1. 其他 android.hardware 套件 (使用完整名稱)。
  2. 其他 vendor.VENDOR 套件 (使用完整合格套件 名稱)。
    • 每個供應商都應該為一個群組。
    • 按照字母順序訂購供應商。
  3. 從同一套件中的其他介面匯入 (使用簡單名稱)。

群組之間請使用空白行。在每個群組中,排序匯入項目 。例子:

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;

介面名稱

介面名稱的開頭必須是 I,後接 UpperCamelCase/PascalCase名稱。具有名稱的介面 必須在 IFoo.hal 檔案中定義「IFoo」。這個檔案 只能包含 IFoo 介面 (介面) 的定義 INAME 應為 INAME.hal )。

函式

針對函式名稱、引數和傳回變數名稱,請採用 lowerCamelCase。例子:

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

結構和聯集欄位名稱

如果是結構體或聯集欄位名稱,請使用 lowerCamelCase。例子:

struct FooReply {
    vec<uint8_t> replyData;
}

類型名稱

型別名稱是指結構體或聯集定義、列舉類型定義,以及 typedef 秒。這類名稱請使用 UpperCamelCase/PascalCase。範例:

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

列舉值

列舉值應為 UPPER_CASE_WITH_UNDERSCORES。通過 以函式引數的形式傳回列舉值,然後傳回這些引數做為函式傳回值,請使用 實際列舉類型 (而非基礎整數類型)。例子:

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
};

注意:列舉類型的基礎類型是 。由於不屬於編譯器,方法是使用 實際的列舉類型較為清晰

如果是列舉值的完整名稱,系統會使用半形冒號 是否在列舉類型名稱與列舉值名稱之間:

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

完整名稱中不得使用空格。使用完整的 並省略不必要的部分。例子:

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

留言

如果是單行註解,則 ///* *//** */ 沒關係。

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • 使用 /* */ 留言。雖然 HIDL 支援使用 // 註解, 並不會出現在生成的輸出內容中,因此不建議使用。
  • 使用 /** */ 處理產生的文件。可套用 只有型別、方法、欄位和列舉值宣告。範例:
    /** 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,
        ...
    }
    
  • 使用 /** 開始另行註解。 請在每行開頭使用 *。 以 */ 做為註解換行,並對齊星號。 範例:
    /**
     * My multi-line
     * comment
     */
    
  • 授權聲明和變更記錄應以新的一行為 /* 開頭 (單一星號),請在每行開頭使用 *,然後將 最後一行的 */ 本身要保留 (星號必須對齊)。 範例:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */
    

檔案中的註解

請在每個檔案的開頭加上適當的授權通知。以核心 HAL 來說 應具備的 development/docs/copyright-templates/c.txt。 記得更新年份並使用 /* */ 樣式的多行註解 如上所述。

您可以選擇在授權通知後方加上空白行,後面再行 並依照變更記錄/版本資訊傳回使用 /* */ 樣式 在多行註解中如上所述,在 變更記錄,然後依照套件宣告內容進行處理。

TODO 註解

TODO 應全部以大寫輸入 TODO 字串,後接 冒號。例子:

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

TODO 留言只能在開發期間獲得。他們必須 並不存在發布的介面中。

介面和函式註解 (文件字串)

使用 /** */ 處理多行和單行 docstring。不使用 // 代表 docstring。

介面的 Docstring 應說明 介面、設計理由、用途等。函式的 Docstring 應該 專屬於該函式 (套件層級的說明文件會在 套件目錄)。

/**
 * 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();
};

您必須為各個屬性新增 @param@return 參數/回傳值:

  • 您必須為每個參數新增 @param。這應該 後面加上參數的名稱,然後是 docstring。
  • 您必須為每個傳回值新增 @return。這項服務 後面應為傳回值的名稱,然後是 docstring。

例子:

/**
 * 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);

格式設定規則

一般格式設定規則包括:

  • 線條長度:每一行文字不得超過 長度為 100 欄。
  • 空白字元。行上沒有結尾空格;空白行 不得包含空白字元。
  • 聊天室與分頁:只能使用空格,
  • 縮排大小。使用 4 個空格做為封鎖和 8 個換行空格
  • 搞怪備註除外 值大括號與前一行 但「右」大括號和以下半形分號 整個行。範例:
    interface INfc {
        close();
    };
    

套件宣告

授權後,套件宣告應位於檔案頂端 注意,應佔滿整行,而不應縮排套裝內容為 宣告的方式,(如需名稱格式,請參閱 套件名稱):

package PACKAGE-NAME;

例子:

package android.hardware.nfc@1.0;

函式宣告

函式名稱、參數、generates 和傳回值應 可以在同一線了。例子:

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

如果兩者無法在同一行中,請嘗試加入參數,並傳回 值相同,並且區分 generate,以協助您 讀取器可快速查看參數和傳回值例子:

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);
}

其他詳細資料:

  • 左括號一律與函式名稱位於同一行。
  • 函式名稱與左括號之間沒有空格。
  • 括號與參數之間不得有空格,但「除外」 就是兩者之間的換行
  • 如果 generates 與前一個關閉時間相同 請使用前面的空格做為括號。如果 generates 相同 做為下一個左括號的後方,後接空格。
  • 所有參數並保持一致 (如果可以的話)。
  • 預設的縮排是 4 個空格。
  • 已包裝參數會與上一行的第一個參數對齊。 否則會以 8 空格縮排

註解

註解格式如下:

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

依字母順序排列註解,並在等號前後使用空格。 例子:

@callflow(key = value)
@entry
@exit

確保註解佔滿整行。例如:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

如果註解無法容納在同一行,請使用 8 個空格縮排。 例子:

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

如果整個值陣列無法在同一行中,請在後面加上換行符號 左大括號 {,在陣列中每個半形逗號之後。地點已打烊 在最後一個值後面,加上括號。請勿使用大括號 只需要一個值

如果整個值陣列可以在同一行中,請勿在後方使用空格 左大括號和右大括號,且每個逗號後方各有一個空格。 例如:

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

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

註解和函式之間不得有空白行 宣告內容例如:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

列舉宣告

請使用以下列舉宣告規則:

  • 如果列舉宣告與其他套件共用,請加入宣告 而不要嵌入介面內。types.hal
  • 冒號前後各須有空格,且基礎類型後方應有空格 。
  • 最後一個列舉值可能不包含額外的半形逗號。

結構宣告

請使用以下結構體宣告規則:

  • 如果與其他套件共用結構體宣告,請放入宣告 而不要嵌入介面內。types.hal
  • 在結構體類型名稱之後,請在左大括號前加上空格。
  • 對齊欄位名稱 (選用)。範例:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }
    

陣列宣告

請勿在下列項目之間加入空格:

  • 元素類型和左方括號。
  • 左方括號和陣列大小。
  • 陣列大小和右方括號。
  • 關閉方括號和下一個左方括號 (如有多個) 維度已存在。

例如:

/* Good */
int32_t[5] array;

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

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

向量

請勿在下列項目之間加入空格:

  • vec 和左角括號。
  • 左角括號和元素類型 (例外狀況:元素類型也是 vec)。
  • 元素類型和封閉角括號 (例外狀況:元素類型也是 vec)

例如:

/* 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;