Android 項目構建編譯概述

主要內容是Android AOSP源碼的管理方式,項目源碼的構建和編譯,用到比如git、repo、gerrit一些命令工具,以及使用Soong編譯系統,編寫Android.bp文件的格式樣式。

1. Android操作系統堆棧概述

Android 是一個針對多種不同設備類型打造的開放源代碼軟件堆棧。Android 的主要目的是為運營商、OEM 和開發者打造一個開放的軟件平臺,使他們能夠將創新理念變為現實,并推出能夠卓有成效地改善用戶移動體驗的真實產品。

Android 平臺的設計可確保不存在一個集中瓶頸,即沒有任何行業參與者可一手限制或控制其他參與者的創新。這樣,我們不但可以打造功能完善的高品質消費類產品,而且可以完全開放源代碼,供第三方自由定制和移植。


1.1. Android AOSP代碼管理

Google維護著多個代碼流水線,以便明確區分當前穩定版 Android 與不穩定的實驗性版本。將Android代碼流水線的開放源代碼管理和維護工作納入到了更大的產品開發周期中

  1. 在任何特定時刻,Android平臺都有一個當前最新版本。該版本通常作為樹中的一個分支
  2. 設備制造商和貢獻者會以當前最新版本為基礎來修復錯誤、發布新設備、試驗新功能等
  3. 與此同時,Google會根據產品的需求和目標,在內部開發下一版Android平臺和框架。與設備合作伙伴就旗艦設備展開合作來開發下一個Android版本,該旗艦設備的規格旨在推動Android朝著我們認為它應該選擇的方向發展
  4. 當第 n+1 版準備就緒時,它就會發布到公開源代碼樹,成為新的最新版本

1.2. 代號、標記和Build號

Android 開發版本按照英文字母的順序,采用美味甜點的名字為代號,劃分為不同的系列。

1.2.1. build ID定義

參考官方文檔

在Android 8.0.0 (Oreo)及更高版本中,每個build均采用build ID格式PVBB.YYMMDD.bbb[.Cn]進行標識,其中:

  • P 表示平臺版本代號的第一個字母,例如O表示Oreo
  • V 表示支持的類別。按照慣例,P表示主要平臺分支
  • BB 是由字母和數字組成的代碼,Google可通過該代碼識別build所屬的確切代碼分支
  • YYMMDD 表示相應版本從開發分支細分出來或與開發分支同步的日期。它并不一定是build的確切構建日期,因為Google常常會在現有build中增加細微的更改,并在新build中重復使用與現有build相同的日期代碼
  • bbb 表示具有相同日期代碼的不同版本,從001開始
  • Cn 是可選的字母數字,表示在現有PVBB.YYMMDD.bbbbuild之上構建的修補程序,從A1開始

2. 構建下載源碼

2.1. ubuntu構建環境

如果是ubuntu 18.04的版本,則下載以下軟件:

sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig


2.2. 源碼控制工具

如需使用Android代碼,需要使用Git一種開源版本控制系統)和Repo(一種對Git構成補充的Google 代碼庫管理工具)

2.2.1. Git

參考git官方使用說明文檔

Git可以處理分布在多個代碼庫上的大型項目。Android使用Git執行本地操作,例如建立本地分支、提交、對比差異、修改

2.2.2. Repo

Repo可以在必要時整合多個Git代碼庫,將相關內容上傳到Gerrit(修訂版本控制系統),并自動執行Android開發工作流程的部分環節

Repo啟動器會提供一個Python腳本,該腳本可以初始化檢出,并可以下載第二部分,即完整的Repo工具。完整的Repo工具包含在Android源代碼檢出中。該工具默認位于$SRCDIR/.repo/repo/...中,它可以從下載的Repo啟動器接收轉發的命令。

Repo不會取代Git,只是為了在Android環境中更輕松地使用Git。Repo使用清單文件(XML)將Git項目匯總到Android超級項目中。

在大多數情況下,可以僅使用Git(不必使用Repo),或結合使用Repo和Git命令以組成復雜的命令。不過,使用Repo執行基本的跨網絡操作可大大簡化您的工作


2.2.2.1. 安裝Repo
  1. 運行以下命令以使用您的 Linux 發行版中的官方軟件包:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

  sudo apt-get updatesudo apt-get install repo
  1. 查看版本號repo version

2.2.2.2. 常用repo/git命令

命令

說明

repo init

初始化代碼

repo sync

同步代碼

repo prune

安全移除已過時的主題分支

repo start

新建一個分支

repo status

顯示當前分支的狀態

repo upload

將更改上傳到審核服務器

git add

暫存文件

git commit

提交暫存的文件

git branch或repo branches

顯示當前分支

git branch [branch]

創建新的主題分支

git checkout [branch]

將 HEAD 切換到指定分支

git merge [branch]

將 [branch] 合并到當前分支

git diff

顯示未暫存更改的 diff 結果

git diff –cached

顯示已暫存更改的 diff 結果

git log

顯示當前分支的歷史記錄

git log m/[codeline]..

顯示未推送的提交

注意:如果存在錯誤,可能會導致repo sync重置本地主題分支。如果在您運行 repo sync 之后,git branch顯示*(無分支),請再次運行git checkout


2.2.2.3. repo命令
  1. repo help init:針對init獲取詳細的幫助說明
  2. repo init --help:只列出init的使用參數列表

repo sync使用說明:(repo help sync)

  • -c:僅獲取服務器中的當前清單分支
  • -d:將指定項目切換回清單修訂版本。如果項目當前屬于某個主題分支,但臨時需要清單修訂版本,則此選項會有所幫助
  • -f:即使某個項目同步失敗,也繼續同步其他項目
  • -jthreadcount:將同步操作拆分成多個線程,以更快地完成。確保不會使計算機超負荷運行-為其他任務預留一些 CPU。如需查看可用CPU的數量,請先運行:nproc --all
  • -q:通過抑制狀態消息來確保運行過程沒有干擾
  • -s:同步到當前清單中的manifest-server元素指定的一個已知良好build

2.2.3. Gerrit

Gerrit是一個基于網頁的代碼審核系統,適用于使用Git的項目。Gerrit允許所有授權用戶提交更改(如果這些更改通過代碼審核,便會自動納入到項目中),以此鼓勵他們更集中地使用 Git。此外,Gerrit會在瀏覽器中并排顯示更改,并支持代碼內注釋,從而使審核工作變得非常輕松。

2.2.4. Android Studio

一個用于開發 Android 應用的官方集成開發環境 (IDE)工具軟件

2.2.5. Android 調試橋 (adb)

可將開發工作站直接與相應 Android 設備關聯,以便安裝軟件包和評估更改


2.3. 下載源碼

2.3.1. 初始化repo客戶端
  1. 使用mkdir創建工作文件夾目錄
  2. 配置git用戶名和email

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

git config --global user.name Your Name
git config --global user.email you@example.com
  1. 運行repo init獲取最新版本的repo

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

repo init -u https://android.googlesource.com/platform/manifest# 初始化某分支
repo init -u https://android.googlesource.com/platform/manifest -b master

2.3.2. python版本依賴

如果報錯/usr/bin/env 'python' no such file or directory,則需要軟鏈接python版本:

sudo ln -s /usr/bin/python3 /usr/bin/python


2.3.3. 下載Android源代碼樹

使用repo sync下載源代碼到工作目錄

如果需要加快速度,使用-c當前分支,和-j threadcount線程數標記:repo sync -c -j8


2.4. Soong編譯系統

在Android7.0發布之前,Android僅使用GNUMake描述和執行其構建規則。Make構建系統得到了廣泛的支持和使用,但在Android層面變得緩慢、容易出錯、無法擴展且難以測試。Soong構建系統正好提供了Androidbuild所需的靈活性

2.4.1. Make和Soong比較(Android.mk和Android.bp)

Make示例:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_MODULE := libxmlrpc++
LOCAL_MODULE_HOST_OS := linuxLOCAL_RTTI_FLAG := -frtti
LOCAL_CPPFLAGS := -Wall -Werror -fexceptions
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/srcLOCAL_SRC_FILES := $(call \all-cpp-files-under,src)
include $(BUILD_SHARED_LIBRARY)

Soong示例:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

cc_library_shared {name: “libxmlrpc++”,rtti: true,cppflags: [“-Wall”,“-Werror”,“-fexceptions”,],export_include_dirs: [“src”],srcs: [“src/**/*.cpp”],target: {darwin: {enabled: false,},},
}

2.4.2. Android.bp文件格式

根據設計,Android.bp文件很簡單。它們不包含任何條件語句,也不包含控制流語句;所有復雜問題都由用Go編寫的構建邏輯處理。

2.4.2.1. 模塊

Android.bp文件中的模塊以模塊類型開頭,后跟一組name: "value", 格式的屬性:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

cc_binary {/* 每個模塊必須具有name屬性,并且值唯一(僅有兩個例外情況是命名空間和預構建模塊中的 Android.bp 屬性值,這兩個值可能會重復)*/name: "gzip",// srcs屬性以字符串列表的形式指定用于構建模塊的源文件// 可以使用模塊引用語法 ":<module-name>" 來引用生成源文件的其他模塊的輸出,如 genrule或filegroupsrcs: ["src/test/minigzip.c"],shared_libs: ["libz"],stl: "none",
}
2.4.2.2. 類型

變量和屬性是強類型,變量根據第一項賦值動態變化,屬性由模塊類型靜態設置。支持的類型為:

映射可以包含任何類型的值,包括嵌套映射。列表和映射可能在最后一個值后面有終止逗號。

  • 布爾值(true 或 false)
  • 整數 (int)
  • 字符串 (“string”)
  • 字符串列表 (["string1", "string2"])
  • 映射 ({key1: "value1", key2: ["value2"]})
2.4.2.3. 支持Glob(正則)

接受文件列表的屬性(例如 srcs)也可以采用glob模式。

glob模式可以包含普通的UNIX通配符*,例如*.java。glob模式還可以包含單個**通配符作為路徑元素,與零個或多個路徑元素匹配。例如,java/**/*.java同時匹配java/Main.java 和 java/com/android/Main.java模式

2.4.2.4. 變量

變量的作用域限定在聲明它們的文件的其余部分,以及所有子Blueprint文件。變量是不可變的,但有一個例外情況:可以使用+=賦值將變量附加到別處,但只能在引用它們之前附加

Android.bp文件可能包含頂級變量賦值:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

// 定義變量 
gzip_srcs = ["src/test/minigzip.c"],
cc_binary {name: "gzip",// 引用變量srcs: gzip_srcs,shared_libs: ["libz"],stl: "none",
}

2.4.2.5. 注釋

Android.bp因為本質上是go語言,所以文件可以包含C樣式的多行/* */注釋以及C++樣式的單行//注釋


2.4.2.6. 運算符

可以使用+運算符附加字符串、字符串列表和映射。

可以使用+運算符對整數求和。附加映射會生成兩個映射中鍵的并集,并附加在兩個映射中都存在的所有鍵的值


2.4.2.7. 條件語句

Soong不支持Android.bp文件中的條件語句。

但是,編譯規則中需要條件語句的復雜問題將在Go在這種語言中,您可以使用高級語言功能,并且可以跟蹤條件語句引入的隱式依賴項)中處理。

大多數條件語句都會轉換為映射屬性,其中選擇了映射中的某個值并將其附加到頂級屬性。

例如,要支持特定于架構的文件,請使用以下命令:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

cc_library {...srcs: ["generic.cpp"],arch: {arm: {srcs: ["arm.cpp"],},x86: {srcs: ["x86.cpp"],},},
}

2.4.2.8. [實用]格式設置工具bpfmt

Soong包含一個針對Blueprint文件的規范格式設置工具,類似于gofmt

如需以遞歸方式重新設置當前目錄中所有Android.bp文件的格式,請運行以下命令:

bpfmt -w .

PS:規范格式包括縮進四個空格、多元素列表的每個元素后面有換行符,以及列表和映射末尾有英文逗號


2.4.3. 特殊模塊
2.4.3.1. 默認模塊

默認模塊可用于在多個模塊中重復使用相同的屬性。例如:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

//默認模塊定義
cc_defaults {name: "gzip_defaults",shared_libs: ["libz"],stl: "none",
}//引用默認模塊
cc_binary {name: "gzip",defaults: ["gzip_defaults"],srcs: ["src/test/minigzip.c"],
}

2.4.3.2. 預編譯模塊

某些預構建的模塊類型允許模塊與其基于源代碼的對應模塊具有相同的名稱。

例如,如果已有同名的cc_binary,也可以將cc_prebuilt_binary命名為foo

例如可以預編譯一些腳本:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

//Android 12 AOSP源碼
//packages/modules/Gki/Android.bp
cc_prebuilt_binary {name: "com.android.gki.preinstall",product_specific: true,srcs: ["preinstall.sh"],apex_available: ["com.android.gki.*"],strip: {none: true,},
}

這讓開發者可以靈活地選擇要納入其最終產品中的版本。如果編譯配置包含兩個版本,則預編譯模塊定義中的prefer標記值會指示哪個版本具有優先級。請注意,某些預編譯模塊的名稱不能以prebuilt開頭,例如android_app_import


2.4.3.3. 命名空間模塊

在Android完全從Make轉換為Soong之前,Make產品配置必須指定PRODUCT_SOONG_NAMESPACES值。它的值應該是一個以空格分隔的列表,其中包含Soong導出到Make以使用m命令進行編譯的命名空間。

在Android完成到Soong的轉換之后,啟用命名空間的詳細信息可能會發生變化。

Soong可以讓不同目錄中的模塊指定相同的名稱,只要每個模塊都在單獨的命名空間中聲明即可。可以按如下方式聲明命名空間:

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

soong_namespace {imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
}

PS:命名空間沒有 name 屬性;其路徑會自動指定為其名稱。系統會根據每個 Soong 模塊在樹中的位置為其分配命名空間。每個 Soong 模塊都會被視為處于 Android.bp(位于當前目錄或最近的父級目錄中的 soong_namespace 文件內)定義的命名空間中。如果未找到此類 soong_namespace 模塊,則認為該模塊位于隱式根命名空間中。


2.5. 編譯Android

2.5.1. source更新環境

使用envsetup.sh腳本初始化環境:source build/envsetup.sh或者. build/envsetup.sh

PS:需要在每次運行repo sync后重新發出此命令,以獲取對該腳本所做的任何更改。請注意,將source替換為.(一個點)可以省去一些字符,這種簡寫形式在文檔中更為常用

envsetup.sh 腳本會導入若干命令,執行后能夠使用Android源代碼,其中包括一些可使用的命令

如需查看可用命令的完整列表,請運行以下命令:hmm


2.5.2. lunch選擇目標

使用lunch選擇要構建的目標

lunch product_name-build_variant會選擇product_name作為需要構建的產品,并選擇build_variant作為需要構建的變體,然后將這些選擇存儲在環境中,以便供后續對m和其他類似命令的調用讀取。

確切的配置可作為參數進行傳遞。例如,以下命令表示針對模擬器進行完整構建,并且啟用所有調試功能。

lunch aosp_arm-eng

2.5.2.1. eng/user/userdebug

參考官方文檔

所有構建目標都采用BUILD-BUILDTYPE形式,其中BUILD是表示特定功能組合的代號。BUILDTYPE是以下類型之一:

構建類型

使用情況

user

權限受限;適用于生產環境安裝帶有user標記的模塊除了帶有標記的模塊之外,還會根據產品定義文件安裝相應模塊屬性ro.secure=1屬性ro.debuggable=0adb 默認處于停用狀態

userdebug

與“user”類似,但具有 root 權限和調試功能;是進行調試時的首選編譯類型同user區別有幾點:還會安裝帶有 debug 標記的模塊屬性ro.debuggable=1adb 默認處于啟用狀態

eng

具有額外調試工具的開發配置默認變種安裝帶有eng或debug標記的模塊除了帶有標記的模塊之外,還會根據產品定義文件安裝相應模塊屬性ro.secure=0屬性ro.debuggable=1屬性ro.kernel.android.checkjni=1adb 默認處于啟用狀態


2.5.2.2. tapas

tapas命令用于配置未捆綁應用的構建流程。

它會選擇要由Android構建系統構建的各個應用。與lunch不同,tapas不會請求為設備構建映像。

使用tapas help查看幫助說明


2.5.3. 編譯代碼

使用m構建所有內容。m可以使用-jN參數處理并行任務。

如果沒有提供-j參數,構建系統會自動選擇認為最適合您系統的并行任務計數

  • m droid是正常build。此模塊目標在此處,因為默認目標需要名稱
  • m all會構建所有內容,以確保包含在樹中且包含Android.mk文件的所有元素都會構建
  • m: 從樹的頂部運行構建系統。這很有用,因為可以在子目錄中運行make。如果設置了TOP環境變量,它便會使用此變量。如果未設置此變量,它便會從當前目錄中查找相應的樹,以嘗試找到樹的頂層。可以通過運行不包含參數的m來構建整個源代碼樹,也可以通過指定相應名稱來構建特定目標
  • mma: 構建當前目錄中的所有模塊及其依賴項
  • mmma: 構建提供的目錄中的所有模塊及其依賴項
  • croot: cd 到樹頂部
  • m clean: 會刪除此配置的所有輸出和中間文件。此內容與rm -rf out/相同
  • 運行m help即可查看m提供的其他偽目標

2.5.4. 運行編譯結果

可以在模擬器上運行構建系統,也可以將其刷寫到設備上。由如果已經使用lunch選擇了構建目標,就不能在構建目標以外的目標上運行。

  1. 使用fastboot刷機
  2. 模擬Android設備:編譯流程會自動將模擬器添加到您的路徑中。如需運行模擬器,請輸入以下命令:emulator

2.6. ADB命令工具

使用adb version查看adb版本,并同時確認是否已安裝ADB

如果未安裝,可以構建Android源碼,在構建目錄下使用Android項目源碼提供的ADB工具

2.7. fastboot刷寫設備

  1. 按住響應組合按鍵或者使用adb reboot bootloader命令進入fastboot模式
  2. 運行命令fastboot flashall -w刷寫,其中-w會擦除設備的data分區數據

2.8. 添加變種product

2.8.1. 編寫product makefile步驟
  1. 創建一個目錄device/<company-name>/<device-name>,例如device/google/marlin(參考Android Q AOSP)。此目錄將包含您設備的源代碼以及構建這些代碼所需的Makefile
  2. 創建一個Makefile文件device.mk,用來聲明設備所需的文件和模塊。有關示例,參考device/google/marlin/device-marlin.mk
  3. 創建一個產品定義Makefile,以便基于設備創建具體產品。以下示例Makefile來自于device/google/marlin/aosp_marlin.mk。請注意,該產品會通過Makefile沿用device/google/marlin/device-marlin.mkvendor/google/marlin/device-vendor-marlin.mk文件中的設置,同時還會聲明產品特定信息,例如名稱、品牌和型號

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

# Inherit from the common Open Source product configuration
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk)PRODUCT_NAME := aosp_marlin
PRODUCT_DEVICE := marlin
PRODUCT_BRAND := Android
PRODUCT_MODEL := AOSP on msm8996
PRODUCT_MANUFACTURER := Google
PRODUCT_RESTRICT_VENDOR_FILES := truePRODUCT_COPY_FILES += device/google/marlin/fstab.common:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.marlin$(call inherit-product, device/google/marlin/device-marlin.mk)
$(call inherit-product-if-exists, vendor/google_devices/marlin/device-vendor-marlin.mk)PRODUCT_PACKAGES += \Launcher3QuickStep \WallpaperPicker
  1. 創建一個指向產品的Makefile的AndroidProducts.mk文件。在此示例中,僅需要產品定義Makefile。以下示例來自于device/google/marlin/AndroidProducts.mk(該文件同時包含marlin(Pixel)和sailfish(PixelXL),它們共享大部分配置)

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

PRODUCT_MAKEFILES := \$(LOCAL_DIR)/aosp_marlin.mk \$(LOCAL_DIR)/aosp_sailfish.mkCOMMON_LUNCH_CHOICES := \aosp_marlin-userdebug \aosp_sailfish-userdebug
  1. 創建一個包含主板特定配置的Makefile文件BoardConfig.mk。有關示例,請查看device/google/marlin/BoardConfig.mk

2.8.2. 設置產品定義變量

變量

說明

示例

PRODUCT_AAPT_CONFIG

創建軟件包時使用的 aapt 配置

PRODUCT_BRAND

對軟件進行自定義所針對的品牌(如果有),例如運營商

PRODUCT_CHARACTERISTICS

用于允許向軟件包中添加變體特定資源的 aapt 特性

tablet、nosdcard

PRODUCT_COPY_FILES

字詞列表,如 source_path:destination_path。在構建相應產品時,應將源路徑下的文件復制到目標路徑。config/makefile 中定義了針對復制步驟的規則

PRODUCT_DEVICE

工業設計的名稱。這也是主板名稱,構建系統會使用它來查找 BoardConfig.mk

tuna

PRODUCT_LOCALES

以空格分隔的列表,用于列出由雙字母語言代碼和雙字母國家/地區代碼組成的代碼對,以便說明針對用戶的一些設置,例如界面語言和時間、日期以及貨幣格式。PRODUCT_LOCALES 中列出的第一個語言區域會用作產品的默認語言區域

en_GB、de_DE、es_ES、fr_CA

PRODUCT_MANUFACTURER

制造商的名稱

acme

PRODUCT_MODEL

最終產品的最終用戶可見名稱

PRODUCT_NAME

總體產品的最終用戶可見名稱,將顯示在設置 > 關于屏幕中

PRODUCT_OTA_PUBLIC_KEYS

產品的無線下載 (OTA) 公鑰列表

PRODUCT_PACKAGES

將要安裝的 APK 和模塊列表

日歷聯系人

PRODUCT_PACKAGE_OVERLAYS

指明是使用默認資源還是添加任何產品特定疊加層

vendor/acme/overlay

PRODUCT_SYSTEM_PROPERTIES

系統分區的系統屬性分配(采用 “key=value” 格式)列表。其他分區的系統屬性可通過 PRODUCT__PROPERTIES 設置,如供應商分區的 PRODUCT_VENDOR_PROPERTIES。支持的分區名稱:SYSTEM、VENDOR、ODM、SYSTEM_EXT 和 PRODUCT


2.8.3. 配置默認系統語言和語言區域過濾器
2.8.3.1. 屬性配置
  • ro.product.locale:用于設置默認語言區域。此屬性最初被設置為PRODUCT_LOCALES變量中的第一個語言區域;可以替換該值
  • ro.localization.locale_filter:使用正則表達式(應用于語言區域名稱)設置語言區域過濾器。例如:
    • 包含過濾器:^(de-AT|de-DE|en|uk).*:只允許德語(奧地利變體和德國變體)、所有英語變體和烏克蘭語
    • 排除過濾器:^(?!de-IT|es).*:不包括德語(意大利變體)和西班牙語的所有變體

2.8.3.2. 啟用語言區域過濾器

如需啟用過濾器,請設置ro.localization.locale_filter系統屬性字符串值

通過在出廠校準期間使用oem/oem.prop設置過濾器屬性值和默認語言,無需將過濾器烘焙 (bake) 到系統映像中即可配置限制

通過將這些屬性添加到PRODUCT_OEM_PROPERTIES變量中(如下所示),就可以確保從OEM分區中獲取這些屬性

代碼語言:javascript

代碼運行次數:0

運行

AI代碼解釋

# Delegation for OEM customization
PRODUCT_OEM_PROPERTIES += \ro.product.locale \ro.localization.locale_filter

然后,在生產環境中,實際值會被寫入oem/oem.prop以反映目標要求。借助這種方法,就能在恢復出廠設置期間保留默認值,讓初始設置在用戶看來與首次設置完全一樣


3. 補丁提交的生命周期

3.1. 流程圖


4. 參考

  • Google官方文檔

本文轉發之??Android 項目構建編譯概述-騰訊云開發者社區-騰訊云

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94470.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94470.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94470.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python爬蟲08_Requests聚焦批量爬取圖片

一、Requests聚焦批量爬取圖片 import re import requests import os import timeurl https://www.douban.com/ userAgent {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0}#獲取整個瀏覽頁面 page_text requests.get(urlur…

Spring Cloud系列—簡介

目錄 1 單體架構 2 集群與分布式 3 微服務架構 4 Spring Cloud 5 Spring Cloud環境和工程搭建 5.1 服務拆分 5.2 示例 5.2.1 數據庫配置 5.2.2 父子項目創建 5.2.3 order_service子項目結構配置 5.2.4 product_service子項目結構配置 5.2.5 服務之間的遠程調用 5.…

【普中STM32精靈開發攻略】--第 1 章 如何使用本攻略

學習本開發攻略主要參考的文檔有《STM32F1xx 中文參考手冊》和《Cortex M3權威指南(中文)》&#xff0c;這兩本都是 ST 官方手冊&#xff0c;尤其是《STM32F1xx 中文參考手冊》&#xff0c;里面包含了 STM32F1 內部所有外設介紹&#xff0c;非常詳細。大家在學習 STM32F103的時…

【Docker】RK3576-Debian上使用Docker安裝Ubuntu22.04+ROS2

1、簡述 RK3576自帶Debian12系統,如果要使用ROS2,可以在Debian上直接安裝ROS2,缺點是有的ROS包需要源碼編譯;當然最好是使用Ubuntu系統,可以使用Docker安裝,或者構建Ubuntu系統,替換Debian系統。 推薦使用Docker來安裝Ubuntu22.04,這里會有個疑問,是否可以直接使用Do…

解決docker load加載tar鏡像報json no such file or directory的錯誤

在使用docker加載離線鏡像文件時&#xff0c;出現了json no such file or directory的錯誤&#xff0c;剛開始以為是壓縮包拷貝壞了&#xff0c;重新拷貝了以后還是出現了問題。經過網上查找方案&#xff0c;并且自己實踐&#xff0c;采用下面的簡單方法就可以搞定。 歸結為一句…

《協作畫布的深層架構:React與TypeScript構建多人實時繪圖應用的核心邏輯》

多人在線協作繪圖應用的構建不僅是技術棧的簡單組合,更是對實時性、一致性與用戶體驗的多維挑戰。基于React與TypeScript開發這類應用,需要在圖形繪制的基礎功能之外,解決多用戶并發操作的同步難題、狀態回溯的邏輯沖突以及大規模協作的性能瓶頸。每一層架構的設計,都需兼顧…

智慧社區(八)——社區人臉識別出入管理系統設計與實現

在社區安全管理日益智能化的背景下&#xff0c;傳統的人工登記方式已難以滿足高效、精準的管理需求。本文將詳細介紹一套基于人臉識別技術的社區出入管理系統&#xff0c;該系統通過整合騰訊云 AI 接口、數據庫設計與業務邏輯&#xff0c;實現了居民出入自動識別、記錄追蹤與訪…

嵌入式開發學習———Linux環境下IO進程線程學習(四)

進程相關函數fork創建一個子進程&#xff0c;子進程復制父進程的地址空間。父進程返回子進程PID&#xff0c;子進程返回0。pid_t pid fork(); if (pid 0) { /* 子進程代碼 */ } else { /* 父進程代碼 */ }getpid獲取當前進程的PID。pid_t pid getpid();getppid獲取父進程的P…

標記-清除算法中的可達性判定與Chrome DevTools內存分析實踐

引言 在現代前端開發中&#xff0c;內存管理是保證應用性能與用戶體驗的核心技術之一。作為JavaScript運行時的基礎機制&#xff0c;標記-清除算法(Mark-and-Sweep) 通過可達性判定決定哪些內存需要回收&#xff0c;而Chrome DevTools提供的Memory工具則為開發者提供了深度的內…

微算法科技(NASDAQ:MLGO)基于量子重加密技術構建區塊鏈數據共享解決方案

隨著信息技術的飛速發展&#xff0c;數據已成為數字經濟時代的核心生產要素。數據的共享和安全往往是一對難以調和的矛盾。傳統的加密方法在面對日益強大的計算能力和復雜的網絡攻擊時&#xff0c;安全性受到了挑戰。微算法科技(NASDAQ&#xff1a;MLGO)通過引入量子重加密技術…

FastAPI快速入門P2:與SpringBoot比較

歡迎來到啾啾的博客&#x1f431;。 記錄學習點滴。分享工作思考和實用技巧&#xff0c;偶爾也分享一些雜談&#x1f4ac;。 有很多很多不足的地方&#xff0c;歡迎評論交流&#xff0c;感謝您的閱讀和評論&#x1f604;。 目錄引言1 FastAPI事件管理2 類的使用2.1 初始化方法對…

SAP-ABAP: Open SQL集合函數COUNT(統計行數)、SUM(數值求和)、AVG(平均值)、MAX/MIN(極值)深度指南

SAP Open SQL集合函數深度指南 1. 核心價值與特性函數作用關鍵特性COUNT統計行數用COUNT(*)包含NULL值行&#xff0c;COUNT(字段)排除NULLSUM數值求和自動過濾NULL值&#xff0c;結果類型與源字段相同AVG平均值必須用TYPE f接收&#xff0c;否則四舍五入導致精度丟失MAX/MIN極值…

【docker】UnionFS聯合操作系統

Linux 的 Namespace、CGroups 和 UnionFS 三大技術支撐了 Docker 的實現。 一、為什么需要聯合文件系統&#xff1f;在傳統操作系統中&#xff0c;每個文件系統都是獨立的孤島。但當我們需要&#xff1a;合并多個目錄的內容保持基礎系統不變的同時進行修改高效共享重復文件內容…

CTF-XXE 漏洞解題思路總結

一、XXE 漏洞簡介XXE (XML External Entity) 漏洞允許攻擊者通過構造惡意的 XML 輸入&#xff0c;強迫服務器的 XML 解析器執行非預期的操作。在 CTF 場景中&#xff0c;最常見的利用方式是讓解析器讀取服務器上的敏感文件&#xff0c;并將其內容返回給攻擊者。二、核心攻擊載荷…

GitLab:一站式 DevOps 平臺的全方位解析

GitLab&#xff1a;一站式 DevOps 平臺的全方位解析 在當今數字化時代&#xff0c;軟件研發的效率與質量直接決定企業的市場競爭力。GitLab 作為全球領先的 DevOps 平臺&#xff0c;憑借 “從構思到部署” 的全流程管理能力&#xff0c;已成為無數企業加速軟件交付的核心工具。…

Flink富函數:一種更靈活、可擴展的方式來定義數據流的處理邏輯

本文重點 Flink中的富函數類是一組用于處理數據流的函數接口和實現類。富函數類提供了一種更靈活和可擴展的方式來定義數據流的處理邏輯。 富函數類 富函數類是DataStream API提供的一個函數類的接口,所有的Flink函數類都有其Rich版本。富函數類一般是以抽象類的形式出現的。…

【STM32】HAL庫中的實現(四):RTC (實時時鐘)

&#x1f552;HAL庫中的實現&#xff1a;RTC&#xff08;Real-Time Clock&#xff09;實時時鐘RTC 是 STM32 的低功耗實時時鐘模塊&#xff0c;常用于&#xff1a; 實時時間維護&#xff08;年月日時分秒&#xff09;定時喚醒日志時間戳鬧鐘功能RTC&#xff08;實時時鐘&#x…

Pauli 矩陣指數函數展開為顯式矩陣 e^A -> B

要展開表達式 為普通矩陣&#xff0c;其中 是 Pauli 矩陣&#xff0c; 是單位向量&#xff0c; 是實數。以下是詳細推導步驟&#xff1a;1. Pauli 矩陣的性質Pauli 矩陣定義為&#xff1a;它們滿足以下關系&#xff1a;?其中 是 Kronecker delta&#xff0c;? 是 Levi-…

【Algorithm | 0x03 搜索與圖論】DFS

DFS基礎知識典型例題例1&#xff1a;n皇后問題例2&#xff1a;拍照例3&#xff1a;理發基礎知識 核心原理&#xff1a;一條路走到黑 示意圖&#xff1a;其含義表示&#xff0c;在這個圖中頂層是第0層&#xff0c;也就是后面dfs的入口&#xff0c;一般從dfs(0)開始操作。 模版&a…

Redis的數據過期策略有哪些?

Redis內部通過兩種主要策略來處理過期的Key&#xff1a; 惰性刪除 惰性刪除&#xff1a;顧明思議并不是在TTL到期后就立刻刪除&#xff0c;而是在訪問一個key的時候&#xff0c;Redis會先檢查這個鍵是否過期。如果過期&#xff0c;就刪除它&#xff0c;然后返回nil。 這種方式非…