程式碼樣式指南

HIDL 程式碼樣式與 Android 架構中的 C++ 程式碼相似,有 4 個空格的縮排和大小寫混合檔案名稱。套件宣告、匯入和 docstring 與 Java 中的類似,但有輕微修改。

以下 IFoo.haltypes.hal 範例說明 HIDL 程式碼樣式,並提供各樣式的詳細資料快速連結 (IFooClientCallback.halIBar.halIBaz.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 是:
    • android.hardware:核心 HIDL 套件 (對應至 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 (如果存在) (請勿明確匯入)。

完整名稱 (FQDN)

只有在必要時,才使用完整的名稱匯入使用者定義的類型。如果匯入類型位於相同套件中,請省略 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);

Struct 和 Union 欄位名稱

如要使用 struct 或 union 欄位名稱,請使用 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 中的 AOSP Apache 授權。請記得更新年份,並使用 /* */ 樣式的多行註解,如上文所述。

您可以選擇在授權聲明後方加上空白行,接著是變更記錄/版本資訊。請按照上述說明使用 /* */ 樣式的多行註解,並在變更記錄後方放置空白行,然後接著是套件宣告。

TODO 註解

TODO 應包含全大寫的字串 TODO,後面接一個冒號。例子:

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

TODO 註解僅可在開發期間使用,不得出現在已發布的介面中。

介面和函式註解 (docstring)

使用 /** */ 處理多行和單行 docstring。請勿將 // 用於 docstring。

介面說明文字應描述介面的一般機制、設計原理、用途等。函式說明文字應專屬於函式 (套件層級說明文件會放在套件目錄中的 README 檔案中)。

/**
 * 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。後面應接著參數名稱和字串說明。
  • 必須為每個傳回值新增 @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;