AndroidAutomotive模块介绍(二)应用及接口介绍

发布于:2024-04-14 ⋅ 阅读:(191) ⋅ 点赞:(0)

前言

上一篇文章中从整体角度描述了 Android Automotive 模块。本篇文章将对 Android Automotive 中的 APP 以及 API 部分展开描述。
上一篇:AndroidAutomotive模块介绍(一)整体介绍
下一篇:AndroidAutomotive模块介绍(三)CarService服务

正文

1、CarServiceApp

Android Automotive 为系统定制了一些专门适用车载系统的应用,以代替传统的手机应用模块。

1.1 代码路径

代码路径为:/android/packages/apps/Car

1.2 应用

代码路径应用如下所示:

ubuntu16-017:/android/packages/apps/Car$ ls -l
total 76
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Cluster
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Dialer
drwxrwxr-x  3  domain users 4096 Jun  7  2022 externallibs
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Hvac
drwxrwxr-x  5  domain users 4096 Nov  9  2022 LatinIME
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Launcher
drwxrwxr-x  5  domain users 4096 Nov  9  2022 LensPicker
drwxrwxr-x 18  domain users 4096 Nov  9  2022 libs
drwxrwxr-x  5  domain users 4096 Nov  9  2022 LinkViewer
drwxrwxr-x  6  domain users 4096 Nov  9  2022 LocalMediaPlayer
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Media
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Messenger
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Notification
drwxrwxr-x  3  domain users 4096 Jun  7  2022 Overview
drwxrwxr-x  5  domain users 4096 Nov  9  2022 Radio
drwxrwxr-x  6  domain users 4096 Nov  9  2022 Settings
drwxrwxr-x  3  domain users 4096 Jun  7  2022 Stream
drwxrwxr-x  5  domain users 4096 May 11  2023 SystemUpdater
drwxrwxr-x  5  domain users 4096 Nov  9  2022 tests

原生提供了许多应用,介绍如下:

应用 描述
Cluster 仪表板
Dialer 电话
Hvac 空调
LatinIME 输入法
Launcher 桌面
LensPicker 活动窗口选择应用(Launcher)
LinkViewer 二维码
LocalMediaPlayer 本地播放器
Media 媒体应用
Messenger 消息管理应用(消息及TTS)
Notification 通知
Overview 音源声卡选择应用
Radio 收音机
Settings 设置
Stream 音源管理应用
SystemUpdater 系统升级
test 测试
libs 支持库

2、CarServiceApi

2.1 代码路径

CarServiceApi 代码路径为 /android/packages/services/Car/car-lib。

ubuntu16-017:/android/packages/services/Car/car-lib$ ls -l
total 32
-rw-r--r-- 1  domain users 7494 Nov  9  2022 Android.bp
-rw-r--r-- 1  domain users  932 Nov  9  2022 AndroidManifest.xml
-rw-r--r-- 1  domain users 1535 Nov  9  2022 Android.mk
drwxrwxr-x 4  domain users 4096 Nov  9  2022 api
drwxrwxr-x 4  domain users 4096 Jun  7  2022 native
drwxrwxr-x 4  domain users 4096 Jun  7  2022 src
drwxrwxr-x 3  domain users 4096 Jun  7  2022 src_feature_future

如上所示,Android.bp 是 car-lib 的编译文件;AndroidManifest.xml 是编译配置文件;Android.mk 描述了添加 car-lib 到系统编译中;api 文件夹中描述了 car-lib 所支持的接口描述 txt 文件;native 文件夹中保存了 car-lib 所需的 libcarpowermanager 库,关于 CarPower 模块的回调信息;src 中描述了 car-lib 提供的接口代码;src_feature_future 文件夹下主要保存 FeatureConfiguration.java 文件,以静态变量的方式保存有关 feature 的默认状态。

2.2 Api 介绍

2.2.1 编译
2.2.1.1 编译命令

编译 Carlib 有三种不同的指令

2.2.1.1.1 make android.car

android.car 编译出的 Carlib 库包含 Car API 中定义的所有方法和实现细节。

编译结果保存到:/out/target/common/obj/JAVA_LIBRARIES/android.car_intermediates 路径下。

2.2.1.1.2 make android.car-system-stubs

android.car-system-stubs 包含了 Car 服务端的 API 接口定义,但不包含实际的实现代码。主要用于在开发 Car 服务时进行编译和链接,以便在编译时检查代码的正确性。确保 Car 服务与 Car 应用之间的通信接口正确对接。

2.2.1.1.3 make android.car-stubs

make android.car-stubs 包含了 Car 应用的 API 接口定义,但并不包含实际的实现代码。主要用于在开发 Car 应用时进行编译和链接,以便在编译时检查代码的正确性。确保 Car 应用与 Car 服务之间的通信接口正确对接。

2.2.1.2 编译文件

下面通过 Android.bp 文件分析下 car-lib 编译逻辑:

// 编译 libcarpowermanager 静态库,主要编译文件是 native 目录下的 CarPowerManager 相关文件
cc_library {
    name: "libcarpowermanager",

    aidl: {
        export_aidl_headers: true,
        local_include_dirs: [
            "src",
        ],
    },

    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
        "-Wno-unused-parameter",
    ],

    include_dirs: [
        "packages/services/Car/car-lib/native/include",
    ],

    shared_libs: [
        "libbinder",
        "liblog",
        "libutils",
    ],

    srcs: [
        "src/android/car/ICar.aidl",
        "src/android/car/hardware/power/ICarPower.aidl",
        "src/android/car/hardware/power/ICarPowerStateListener.aidl",
        "native/CarPowerManager/CarPowerManager.cpp",
    ],
}

java_library {
    name: "android.car.cluster.navigation",
    proto: {
        type: "lite",
    },
    static_libs: ["libprotobuf-java-lite"],
    srcs: ["src/android/car/navigation/navigation_state.proto"]
}

// android.car 库,是 Carlib API 库
java_library {
    name: "android.car",
    srcs: [
        "src/**/*.java",
        "src_feature_future/**/*.java",
        "src/**/I*.aidl",
    ],
    static_libs: ["android.hardware.automotive.YFvehicle-V2.0-java"],
    aidl: {
        include_dirs: [
            "system/bt/binder",
        ],
    },
    exclude_srcs: [
        "src/android/car/storagemonitoring/IoStats.aidl",
        "src/android/car/storagemonitoring/IoStatsEntry.aidl",
    ],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    installable: true,
}

// android.car-stubs 库,Car 应用端的 API 接口定义,不包含实现代码逻辑。
java_library_static {
    name: "android.car-stubs",
    srcs: [
        ":android.car-stub-docs",
    ],
    libs: [
        "android.car",
    ],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    compile_dex: true,
    dist: {
        targets: ["dist_files"],
    }
}

// android.car-system-stubs 库。Car 服务端 API 接口定义,不包含实现代码逻辑。
java_library_static {
    name: "android.car-system-stubs",
    srcs: [
        ":android.car-system-stubs-docs",
    ],
    libs: [
        "android.car",
    ],
    product_variables: {
        pdk: {
            enabled: false,
        },
    },
    compile_dex: true,
    dist: {
        targets: ["dist_files"],
    }
}

在源码中添加 AndroidAutomotive 相关编译,包括 APP、Jar 包、CarService 服务等,编译文件在 /android/packages/services/Car/car_product/build/car.mk 文件中。

# Automotive specific packages 添加 Automotive 应用编译
PRODUCT_PACKAGES += \
    CarFrameworkPackageStubs \
    CarService \
    CarDialerApp \
    OverviewApp \
    CarLauncher \
    CarSystemUI \
    LocalMediaPlayer \
    CarMediaApp \
    CarMapsPlaceholder \
    CarSettings \
    android.car \
    car-frameworks-service \
    com.android.car.procfsinspector \
    libcar-framework-service-jni \
    CarHvacApp \
    
# should add to BOOT_JARS only once 添加 Carlib Jar 包编译
ifeq (,$(INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS))
PRODUCT_BOOT_JARS += \
    android.car

PRODUCT_HIDDENAPI_STUBS := \
    android.car-stubs

PRODUCT_HIDDENAPI_STUBS_SYSTEM := \
    android.car-system-stubs

PRODUCT_HIDDENAPI_STUBS_TEST := \
    android.car-test-stubs
2.2.2 Api 分类

Android Automotive 的 carlib 是一个用于 Car 应用开发的库,提供了一系列 API 接口供开发者使用。

CarApi 类图如下所示:
在这里插入图片描述

下表是所有 Car-lib API 接口描述:

接口类 说明
CarAudioManager 用于管理车辆音频统的接口,可以控制音量、音频输入输出等功能
CarSensorManager 用于访问车辆传感器数据的接口,可以获取车辆的加速度、转向角度、车辆倾斜角度等传感器数据
CarInfoManager 用于获取车辆信息的接口,可以获取车辆的VIN码、车辆速度、车辆位置等信息
CarDiagManager 用于控制汽车EOL/Diag系统的接口
CarAppFocusManager 设置和监听当前应用焦点的接口
CarPackageManager 提供专用的和包管理相关的接口
CarNavigationStatusManager 为仪表盘提供导航状态的接口
CarCabinManager 控制汽车座舱系统的接口(CarPropertyManager 代替)
CarDiagnosticManager 监控诊断数据的接口
CarHvacManager 控制空调系统的接口(CarPropertyManager 代替)
CarPowerManager 接收电源状态变化的接口
CarProjectionManager 投屏管理接口
CarPropertyManager 与车辆属性交互的接口
CarVendorExtensionManager 访问厂商扩展车辆属性的接口(CarPropertyManager 代替)
CarInstrumentClusterManager 配合仪表控制的接口(CarPropertyManager 代替)
CarTestManagerBinderWrapper 仅用于系统测试
VmsSubscriberManager 供地图服务订阅者使用的接口
CarBluetoothManager 提供和特定的汽车蓝牙管理器交互的接口
CarStorageMonitoringManager 检索闪存信息的接口
CarDrivingStateManager 获取与驾驶状态相关信息的接口
CarUxRestrictionsManager 获取驾驶过程中用户体验限制的接口
CarConfigurationManager 显示存储在系统中的车辆配置值的接口
CarTrustAgentEnrollmentManager 授权可信任设备的接口
CarMediaManager 接收媒体音源变化的接口
CarBugreportManager 报告错误的接口

Car-lib 常用的接口类有:

  • CarApiManager:

用于管理Car API的接口,可以通过该接口获取Car的各种功能模块,如 CarInfoManager、 CarSensorManager 等。

  • CarInfoManager:

用于获取车辆信息的接口,可以获取车辆的VIN码、车辆速度、车辆位置等信息。

  • CarSensorManager:

用于访问车辆传感器数据的接口,可以获取车辆的加速度、转向角度、车辆倾斜角度等传感器数据。

  • CarAudioManager:

用于管理车辆音频系统的接口,可以控制音量、音频输入输出等功能。

  • CarNavigationManager:

用于车辆导航功能的接口,可以获取车辆当前位置、目的地信息等。

  • CarPackageManager:

用于管理Car应用包的接口,可以安装、卸载、启动 Car 应用。

  • CarProjectionManager:

用于管理车辆投影功能的接口,可以控制车辆的投影屏幕显示。

  • CarInputManager:

用于处理车辆输入事件的接口,可以处理车辆上的按键事件、触摸事件等。

2.3 Api 使用方法

CarService 中各个服务本质上是 AIDL 接口的实现类,属于 Server 端,而对应的 Client 端就需要一个 IBinder 对象来访问 Server 端的方法,这些 IBinder 对象在 Car API 中被封装在一个个 XXXManager 类中。

Car API 与 CarService 中的服务,名称上存在对应关系,所以很好理解。例如:CarWatchdogManager 对应 CarWatchdogService,CarMediaManager 对应 CarMediaService。

不过也有例外:CarInfoManager、CarSensorManager、CarHvacManager、CarCabinManager、CarVendorExtensionManager 都对应 CarPropertyService。但是在 Android 11 中这些 Manager 都已经过时,Google 建议统一使用 CarPropertyManager。

对于 Car SDK 的调用,在 Android 9 与 Android 10 版本略有不同。

2.3.1 Car 类

Car 类是 Android Automotive 的 API 接口,Car 是 Car-lib 总功能的入口,Car 类中包含许多的 Manager,通过 getCarManager(String) 获取 Car-lib 提供的接口对象。

常用方法如下:

方法 描述
public static Car createCar(Context context) 获取Car实例
public static Car createCar(Context context, @Nullable Handler handler) 获取Car实例
public static Car createCar(@NonNull Context context, @Nullable Handler handler, long waitTimeoutMs, @NonNull CarServiceLifecycleListener statusChangeListener) 获取Car实例
public Object getCarManager(String serviceName) 获取子Manager
public boolean isConnected() 判断是否和CarService保持连接
public void disconnect() 断开和CarService的连接
2.3.1.1 createCar()

createCar() 函数是应用创建 Car 对象的调用方法,这里来分析下 createCar() 函数的代码流程,下面是 /android/packages/services/Car/car-lib/src/android/car/Car.java 文件内容

    public static Car createCar(Context context, @Nullable Handler handler) {
        Log.i(TAG_CAR, "createCar");
        Car car = null;
        IBinder service = null;
        boolean started = false;
        int retryCount = 0;
        while (true) {
            service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
            if (car == null) {
                // service can be still null. The constructor is safe for null service.
                car = new Car(context, ICar.Stub.asInterface(service),
                        null /*serviceConnectionListener*/, null /*statusChangeListener*/, handler);
            }
            if (service != null) {
                if (!started) {  // specialization for most common case.
                    return car;
                }
                break;
            }
            if (!started) {
                car.startCarService();
                started = true;
            }
            retryCount++;
            if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) {
                Log.e(TAG_CAR, "cannot get car_service, waited for car service (ms):"
                                + CAR_SERVICE_BINDER_POLLING_INTERVAL_MS
                                * CAR_SERVICE_BINDER_POLLING_MAX_RETRY,
                        new RuntimeException());
                return null;
            }
            try {
                Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);
            } catch (InterruptedException e) {
                Log.e(CarLibLog.TAG_CAR, "interrupted while waiting for car_service",
                        new RuntimeException());
                return null;
            }
        }
        // Can be accessed from mServiceConnectionListener in main thread.
        synchronized (car) {
            if (car.mService == null) {
                car.mService = ICar.Stub.asInterface(service);
                Log.w(TAG_CAR,
                        "waited for car_service (ms):"
                                + CAR_SERVICE_BINDER_POLLING_INTERVAL_MS * retryCount,
                        new RuntimeException());
            }
            car.mConnectionState = STATE_CONNECTED;
        }
        return car;
    }

1、通过一个 while 循环一直去创建 Car 实例,并且建立与 CarService 服务的连接,直到实例化成功或者创建次数超过最大限制 CAR_SERVICE_BINDER_POLLING_MAX_RETRY(100)。

2、service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);首先获取CarService的服务实例

3、car = new Car(context, ICar.Stub.asInterface(service), null , null, handler); 这里前面方法中声明,所以判断一定是null,会new一个Car实例。

4、car.startCarService(); 这里实际是去用Bind的方式启动 CarService。

5、if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) { 这里会有个判断重试的次数,如果超过次数还没取到 CarService 服务,则会返回 null。sleep 的时间是 50ms,重试 100 次,也就是获取 Car 实例最长时间长达 5 s。

2.3.1.2 startCarService()

startCarService() 函数实际是调用 Context.bindServiceAsUser() 函数去启动 CarService 服务。

下面是 startCarService() 函数逻辑:

    private void startCarService() {
        Intent intent = new Intent();
        intent.setPackage(CAR_SERVICE_PACKAGE);
        intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
        boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
                Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
        Log.i(TAG_CAR, "bindServiceAsUser bound" +bound);
        synchronized (mLock) {
            if (!bound) {
                Log.i(TAG_CAR, "bindServiceAsUser 1");
                mConnectionRetryCount++;
                if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
                    Log.w(TAG_CAR, "cannot bind to car service after max retry");
                    mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
                } else {
                Log.i(TAG_CAR, "bindServiceAsUser 2");
                    mEventHandler.postDelayed(mConnectionRetryRunnable,
                            CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
                }
            } else {
                Log.i(TAG_CAR, "bindServiceAsUser 3");
                mEventHandler.removeCallbacks(mConnectionRetryRunnable);
                mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
                mConnectionRetryCount = 0;
                mServiceBound = true;
            }
        }
    }

如果调用 Context.bindServiceAsUser() 启动 CarService 服务失败,会重试连接 CAR_SERVICE_BIND_MAX_RETRY (20) 次。超过尝试次数后会调用 mConnectionRetryFailedRunnable。

2.3.1.3 connect()

connect() 方法实际就是调用 startCarService() 方法,因为 connect() 是公开的,startCarService() 是私有的。connect() 函数只能调用一次,如果当前已经处于连接状态,再次调用 connect() 函数会抛 IllegalStateException 异常,client 没有捕获该异常,则会引起 client 程序的崩溃。

    public void connect() throws IllegalStateException {
        synchronized (mLock) {
            if (mConnectionState != STATE_DISCONNECTED) {
                throw new IllegalStateException("already connected or connecting");
            }
            mConnectionState = STATE_CONNECTING;
            startCarService();
        }
    }

connect() 已经弃用了,我们知道 startCarService() 就是和 CarService 建立连接,因为在 CreatCar() 里已经保证获取到 CarService,所以 connect() 不在必要。

2.3.2 示例

通过 Car.createCar() 函数可以创建 Car 对象,参数需要传入 ServiceConnection,并在 service 连接成功后,获取想要的 Manager 实例,调用 connect() 函数,当与 CarService 连接成功后,会回调 ServiceConnection 对象的 onServiceConnected() 函数完成连接。如下所示:

private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);
            } catch (CarNotConnectedException e) {
                Log.e(TAG, "Car not connected in onServiceConnected");
            }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};

...
Car carApiClient = Car.createCar(context, mCarServiceConnection);
carApiClient.connect();
...

但是在 Android 10 以后,Google 改写了 Car API 的使用方式,Android 10 以后构建 Car 对象不再建议传入 ServiceConnection 对象,而是使用了下面的方法。

Car carApiClient = Car.createCar(context);
CarHvacManager manager = (CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE);

Android 10 以后 Car API 的调用方式由异步方法改为了同步方法,保留了 disconnect(),但是不需要调用 connect(),这样使用起来更加简单。

这种调用方式是 CarService 不发生异常的情况下,Client 端与 CarService 的连接也不会断开,但是如果 CarService 发生异常,Client 端会被直接杀死,使用如下方式可以避免:

Car car = Car.createCar(this, workThreadHandler, 2000, new Car.CarServiceLifecycleListener() {
    @Override
    public void onLifecycleChanged(@NonNull Car car, boolean ready) {
        // ready 在Service断开连接时会变为false
if (ready) {
            
        } else {
            // CarService 发生异常或连接被断开了,需要client端处理。
}
    }
});

通过创建 Car 对象时传入 CarServiceLifecycleListener 对象,注册回调的方式,通过回调以知道 CarService 的生命周期。

3、总结

本篇文档介绍了 AndroidAutomotive 架构中 APP 应用与 API 接口等内容,对于应用分类、功能、代码;API 接口介绍、初始化、使用方法等。

上一篇:AndroidAutomotive模块介绍(一)整体介绍
下一篇:AndroidAutomotive模块介绍(三)CarService服务


网站公告

今日签到

点亮在社区的每一天
去签到