1. 前言 - 移動互聯網退潮下的汽車大戰
將時間回退到2017年我大學剛畢業時,彼時移動互聯網就已經開始退潮,各大個培訓機構也紛紛停止了Android相關的培訓,曾經熱火朝天的應用開發從那時起,就開始走向下坡路,小程序以及眾多跨平臺框架也讓市場對Android原生開發的需求逐年降低,市場需求的降低也造就了Android開發的面試變得史無前例的“卷”。
終于我在2019年選擇離開了互聯網,投身當時還不是非常火熱的車載Android領域繼續從事Android原生開發。而這一年中國首個外商獨資的整車制造項目,“上海特斯拉超級工廠”開工了。
特斯拉在智能化和電子化上的巨大優勢將智能汽車推向了一個全新的高度,先進的自動駕駛以及BMS電池管理系統,深深震撼了全世界的人,在當時的國人眼中特斯拉幾乎就是新能源汽車的代名詞,時至今日,Model Y和Model 3已也依然是新能源汽車領域的暢銷車型。
眾所周知汽車工業是發達國家重要的經濟支柱,而中國是世界上最大汽車生產和銷售國,特斯拉的熱銷立馬就引發了一場 鯰魚效應 ,國內外的汽車制造商紛紛開始布局智能化汽車,汽車工業走向了軟件定義汽車的時代。軟件定義汽車的核心思想是,決定未來汽車的是以人工智能為核心的軟件技術,車載軟件在汽車領域的重要性首次被拔高到了前所未有的高度,就這樣一場轟轟烈烈的車載軟件技術大戰上演了。
2. 智能汽車座艙基本結構
在從事車載Android應用開發前,必須要對汽車座艙的基本結構有一個大體的認知,只有意識到汽車座艙是一種與手機完全不同的架構,才能更好的助力我們日后學習車載Android應用的開發。下面就來介紹一個比較主流的車載操作系統架構。
注意:并不是所有的車載操作系統都采用了下面的架構,比如,特斯拉采用的是基于Linux一套架構。
上面就是目前主流汽車座艙采用技術架構,我們從上到下依次介紹:
T-BOX
T-Box又稱TCU(車聯網控制單元),指安裝在汽車上用于控制跟蹤汽車的嵌入式系統,是車載信息交互系統核心部件,有了它汽車才能實現聯網功能,所以也起到中央網關的作用。通常包括GPS單元、移動通訊外部接口電子處理單元、微控制器、移動通訊單元以及存儲器等。
對車輛,T-Box可提供車輛故障監控、電源管理、遠程升級、數據采集、智慧交通等功能,對車主,T-Box可為提供車輛遠程控制、安防服務等功能。
T-BOX屬于外圍硬件,與中控、儀表并不集成在一個主板上。
SOC
SoC的定義多種多樣,由于其內涵豐富、應用范圍廣,很難給出準確定義。一般說來, SoC稱為系統級芯片,也有稱片上系統(System on Chip),意指它是一個產品,是一個有專用目標的集成電路,其中包含完整系統并有嵌入軟件的全部內容。
車載Soc和我們最常見的手機Soc非常類似,內部集成了CPU和GPU。目前最主流的車載Soc是高通的SA8155,它就是高通在手機Soc驍龍855的基礎上發展而來的。
MCU
微控制單元(Microcontroller Unit;MCU) ,又稱單片微型計算機(Single Chip Microcomputer )或者單片機,是把中央處理器(Central Process Unit;CPU)的頻率與規格做適當縮減,并將內存(memory)、計數器(Timer)、USB、A/D轉換、UART、PLC、DMA等周邊接口,甚至LCD驅動電路都整合在單一芯片上,形成芯片級的計算機。
一般汽車座艙內,集成SOC的主板上也會額外集成一個或多個MCU。
AutoSAR
Adaptive AutoSAR 是一種適用于高級自動駕駛的軟件架構平臺,提要提供高性能的計算和通信,提供靈活的軟件配置,支撐應用的更新。
Adaptive AutoSAR 的主要架構分為硬件層、ARA(AutoSAR Run-timeFor Adaptive實時運行環境)以及應用層。
應用層包含的應用程序模塊(AA)運行在ARA之上,每個AA以獨立的進程運行。ARA由功能集群提供的應用接口組成,他們屬于自適應平臺。自適應平臺提供Adaptive AutoSAR 的基本功能和標準服務。
每個AA可以向其他AA發生服務。基于這種架構,整車的功能之間可以解耦。
Hypervisor
一種運行在基礎物理服務器和操作系統之間的中間軟件層,可允許多個操作系統和應用共享硬件。也可叫做VMM( virtual machine monitor ),即虛擬機監視器。
目前主流的汽車座艙,都是同時在一個Soc上運行著兩個不同的操作系統,一個是顯示汽車儀表盤的QNX系統,另一個用于車載信息娛樂的Android系統,其底層技術原理就是Hypervisor。
QNX
QNX是一種商用的、遵從POSIX規范的類Unix實時操作系統,目標市場主要是面向嵌入式系統,具備高運行效率、高可靠性特點,并在工控領域擁有近40年的使用經驗,被廣泛應用于汽車、軌道交通、航空航天等對安全性、實時性要求較高的領域。
QNX在車載操作系統市場的占有率超過75%,在更注重生態和內容的車載娛樂系統占有率也超過60%,而在強調安全性的儀表盤以及駕駛輔助領域,QNX的市占率更是達到了近100%。
2010年QNX被加拿大RIM公司收購,而這家公司就是黑莓BlackBerry的母公司。
SOA
SOA(Service-OrientedArchitecture)是一種基于業務實現的粗粒度松耦合的面向服務的分布式架構,即實現業務和技術的分離,又實現業務和技術的自由組合。
以位置服務為例,很多車內應用會用到位置信息,像天氣、拍照、導航,這些應用根據自身服務有不同的需求,對位置信息的處理各不相同,SOA就可以很好地解決這個問題。
SOA原本是服務器開發中用到的技術,現如今也被用在車載操作系統領域,但是目前關于SOA的技術規范比較混亂,國內主機廠商外對于SOA的實現方式也有區別。
SOA并不車載操作系統必須的,其實目前為止已經上市的車型中,很少采用了SOA架構,所以它還只是車載操作系統未來的一個發展方向。
2021年上汽零束率先發布業界首個車載SOA軟件架構規范。威馬汽車科技集團旗下的W6號稱國內首款采用SOA的量產車。
車載以太網
車載以太網是一種用以太網連接車內電子單元的新型局域網技術,與傳統以太網使用4對非屏蔽雙絞線電纜不同,車載以太網在單對非屏蔽雙絞線上可實現100Mbit/s,甚至1Gbit/s的傳輸速率,同時還滿足汽車行業對高可靠性、低電磁輻射、低功耗、帶寬分配、低延遲以及同步實時性等方面的要求。
車載以太網的設計是為了滿足車載環境中的一些特殊需求。例如:滿足車載設備對于電氣特性的要求(EMI/RF);滿足車載設備對高帶寬、低延遲以及音視頻同步等應用的要求;滿足車載系統對網絡管理的需求等。因此可以理解為,車載以太網在民用以太網協議的基礎上,改變了物理接口的電氣特性,并結合車載網絡需求專門定制了一些新標準。針對車載以太網標準,IEEE組織也對IEEE 802.1和IEEE 802.3標準進行了相應的補充和修訂。
CAN
CAN是控制器域網 (Controller Area Network, CAN) 的簡稱,是由研發和生產汽車電子產品著稱的德國BOSCH公司開發了的,并最終成為國際標準(ISO11898)。是國際上應用最廣泛的現場總線之一。 在北美和西歐,CAN總線協議已經成為汽車計算機控制系統和嵌入式工業控制局域網的標準總線,并且擁有以CAN為底層協議專為大型貨車和重工機械車輛設計的J1939協議。近年來,其所具有的高可靠性和良好的錯誤檢測能力受到重視,被廣泛應用于汽車計算機控制系統和環境溫度惡劣、電磁輻射強和振動大的工業環境。
CAN在車載操作系統&應用開發中使用非常廣泛,車載Android的核心服務之一 - CarService本質上就是將外部硬件通信報文解析成上層應用可以識別的數據,這里的通信報文目前普遍都是CAN報文。
CAN通信在車載中使用的是如此廣泛,以至于作為Android程序員,我們都不得不去學習CAN仿真測試工具的使用,有時候甚至需要我們去閱讀、解析CAN報文。
值得一提的是CAN仿真測試工具非常昂貴,雖有國產替代,但目前依然普遍采用德國維克多公司出品的各類工具和軟件,價格在數萬元到數十萬元不等。
3D HMI設計工具 & 嵌入式圖形引擎
隨著車載Soc算力的提高,現代座艙越來越喜歡引入3D化的圖形界面,3D化的界面可以實時生成動畫反饋,大大提升了界面的美觀性和易用性。目前車載開發中主流的3D HMI設計工具&圖形引擎有老牌的游戲開發工具如Unity 3d、Unreal(虛幻),也有專用于汽車HMI設計&圖形顯示的 — Kanzi 。
2016年芬蘭汽車軟件公司Rightware以及旗下產品Kanzi,被國內的汽車軟件供應商中科創達收購。
上面介紹了汽車座艙的基礎知識,Android應用程序員說到底還是負責在座艙中控,編寫各類型的應用,下面就來介紹車載應用與互聯網應用的不同之處。
3. 車載應用開發
車載Android應用說到底就是,在車載Android系統中嵌入一系列系統級應用,這里既包含與用戶存在交互的HMI應用,也包含在后臺運行沒有HMI的Service應用。
一般而言,車載應用復雜度比一般的互聯網應用還要低一些。
常見有HMI的車載應用如,車載空調、多媒體應用、桌面、SystemUI、系統設置、車控車設、藍牙電話以及一些第三方應用等等。
沒有HMI的應用有,CarService、AudioService、AccountService等等。在車載應用開發中需要定制大量的Service,這也是應用開發中工作量比較大的一部分。
3.1 系統級應用與普通應用的區別
系統應用需要嵌入到Android ROM中運行,雖然普通的應用也可以嵌入到ROM中,但是系統應用可以調用Android SDK的內部API,而這一點是普通應用做不到的,總得來說系統應用具有以下特點
- 可以訪問Android SDK內部的API
- 不需要申請動態權限
- 可配置開機自啟動
- 必須對應用進行簽名
接下來我們實際上手編寫一個系統級應用。
3.2 編寫一個系統級應用
編寫Android系統應用與普通的Android應用基本相同,我們首先在AndroidStudio中編寫一個demo,只需要一個空白的Activity和Application即可。
public class DemoApp extends Application {
private Handler handler;
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate: start");
handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
showView();
}
},5000);
}
private void showView(){
WindowManager manager = getSystemService(WindowManager.class);
View view = new View(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT);
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
manager.addView(view,params);
}
}
上面的application邏輯很簡單,app啟動5秒后,彈出一個全屏的Window的。
接下來在AndroidManifest.xml中注冊application。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.car"
android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:persistent="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.First">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
在上面源碼中我們需要關注兩個普通應用用不到的屬性:
android:sharedUserId
將與其他應用程序共享的 Linux 用戶 ID 的名稱。默認情況下,Android 會為每個應用分配自己唯一的用戶 ID。但是,如果為兩個或多個應用將此屬性設置為相同的值,則它們將共享相同的 ID,前提是它們的證書集相同。具有相同用戶 ID 的應用可以訪問彼此的數據,如果需要,可以在同一進程中運行。
開發系統應用時,此項不是必須配置的。配置為android.uid.system
后,該應用會變成system用戶,可以訪問一些system用戶才能訪問的空間。
android:persistent
配置應用程序是否應始終保持運行,默認為false。設為true之后,應用在開機廣播發出之前就會自行啟動,而且應用被殺死后,也會立即重啟。
開發系統應用時,此項不是必須配置的。
3.3 測試系統應用
3.3.1 準備測試環境
測試系統應用就比較麻煩了,由于手邊沒有開發板,只能基于模擬器進行測試,所以就必須下載Android的源碼,并使用Android源碼環境編譯出帶有系統簽名的APK。
下載、編譯Android源碼 請參考 :Android車載應用開發與分析(1) - Android Automotive概述與編譯
完成Android源碼編譯后,我們將編寫好的FirstCarApp部分源碼拷貝到 /aosp/packages/apps/Car/ 下,
基于Android源碼環境的app工程結構與基于Gradle的AndroidStudio工程結構是完全不一樣的,目錄結構如下:
你應該注意到了 src 目錄下沒有androids studio工程結構中的main/java
需要強調的是,這種基于原生的寫法,并不常用。實際開發中,我們依然是在Android Studio中開發完畢,將源碼提交到gerrit上,后續的編譯、簽名、復制的過程會有jenkins幫我們完成。
3.3.2 編譯&運行應用
源碼環境下編譯出Android應用,需要編寫一個Android.bp或Android.mk腳本,如果你對Android.bp或Android.mk并不了解的話請參考:Android.mk 上手指南 | Android.bp入門教程
本篇測試用的Android.bp腳本如下
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
android_app {
name: "CarFirstApp",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
platform_apis: true,
certificate: "platform",
privileged: true,
static_libs: [
"androidx.appcompat_appcompat",
"com.google.android.material_material",
],
optimize: {
enabled: false,
},
dex_preopt: {
enabled: false,
},
product_variables: {
pdk: {
enabled: false,
},
},
}
然后完整編譯一次Android的源碼
# 編譯Android源碼
/aosp$ source build/envsetup.sh
/aosp$ lunch 12
/aosp$ make -j 32
/aosp$ emulator -writable-system -netdelay none -netspeed full
一般情況下我們可以直接使用emulator指令就可以啟動編譯好的模擬器,但是此時的模擬器的文件系統還是read-only模式,并且不可以執行remount指令,通過添加-writable-system -netdelay none -netspeed full,我們就可以正常使用remount指令了。
/aosp$ adb root
/aosp$ adb remount
/aosp$ adb shell reboot
等模擬器重啟后,我們繼續編譯出CarFristApp的apk。
link@link-PC:/aosp$ make CarFirstApp
...
## 編譯后輸出的apk路徑
============================================
[100% 4/4] Install: out/target/product/generic_car_x86/system/priv-app/CarFirstApp/CarFirstApp.apk
#### build completed successfully (2 seconds) ####
然后使用adb指令在模擬器中創建一個CarFristApp目錄,將編譯好的apk push到system/priv-app/CarFristApp/目錄下。
/CarFirstApp$ adb root
/CarFirstApp$ adb remount
# 創建目錄
/CarFirstApp$ adb shell mkdir /system/priv-app/CarFirstApp
/CarFirstApp$ adb push CarFirstApp.apk /system/priv-app/CarFirstApp
# 重啟
/CarFirstApp$ adb shell reboot
等待模擬器重啟結束后,就可以看到,app會自行啟動,然后會彈出一個WindowView遮住屏幕。不知道你是否注意到了,無論是自啟動,還是彈出一個遮住屏幕的Window,都沒有申請權限的窗口顯示出來,這就系統級應用的一個重要特點。
在上面的操作中我們選擇把apk push到priv-app下面,除此以外Android應用還有以下幾種安裝路徑,可以根據實際需要安裝到不同的目錄中去。
/system/priv-app
該路徑存放一些系統底層的應用,比如Setting,systemUI等。該目錄中的app擁有較高的系統權限,而且如果要使用android:protectionLevel=signatureOrSystem
,那么該app必須放到priv-app目錄中去。
/system/app
該目錄中存放的系統app權限相對較低,而且當擁有root權限時,就有可能卸載掉這些app。
/vendor/app
該目錄存放vendor廠商的app
/data/app
用戶安裝的第三方app
3.4 車載應用的難點
車載應用開發過程中,往往都會遇到以下幾個難點:
調試耗時且費力
車載應用開發難度其實并不大,但是很煩!特別是調試,不同于開發手機應用,車載應用的運行環境是基于AOSP定制的,而且大多數時候都會存在數不清的BUG,有時系統底層的bug會在上層應用中體現,這就要求應用開發者必須有能力準確識別出這個bug的歸屬方。復雜的UI
現如今的車載應用都會有著一個套復雜且炫酷交互UI,同時,由于車載Android與QNX共享一個Soc和內存,所以多數時候系統資源都比主流的手機要差不少,對應用開發者來說,實現一套復雜且高性能的HMI,往往會非常有挑戰性。對系統API理解不夠
開發車載應用多數時候都會要求重新定制一個原本系統中已經存在的應用,比如系統設置。這就要求開發者對于原生應用的運行方式、調用的API都有一定的了解。
4. 車載Android開發的前景
讀完以上的內容,相信你已經對車載Android的開發有一個淺顯的認識了。不知道你會不會認為我在勸你轉行做車載Android的開發?答案是NO!
單純的Android應用工程師在整車座艙上只能負責非常小的一個技術領域,這就已經決定了這個職業的發展高度,如果想突破這層天花板,就必須要深入到Android系統的底層,掌握Framework、HAL甚至于Native的一些運行原理。除此以外,Linux、汽車相關的知識也是需要額外學習的。
就目前而言,車載Android開發依然有著不錯的前景,但還遠沒有達到曾經的移動互聯網的熱度,甚至可能以后也不會達到,并且就像曾經熱火朝天的移動互聯網一樣,隨著大量開發人員的涌入、汽車制造業的重新洗牌、供需關系的改變,總有一天它也會不可避免的走向下坡路。
我曾經后悔過入行車載開發,因為相比手機應用開發,所需要學習知識實在太多太雜,調試過程也比手機應用復雜,但是人這一輩何嘗不是在后悔中度過的呢?
參考資料
[智能座艙:智能化基礎平臺及架構(下)]
[2020年中國T-BOX行業現狀分析,乘用車T-Box裝配率迅速提升「圖」]
[車載操作系統(三):智能座艙操作系統]
[首個專為先進智能駕艙打造的一體化HMI工具——Kanzi One重磅發布]
[Automotive | Android 開源項目 | Android Open Source Project]
[車載以太網-電子發燒友網]