CarAudioService介绍
packages/services/Car/service/src/com/android/car/audio/CarAudioService.java
CarAudioService是CarService服务中的其中一种关于Audio的特定服务。主要是负责与汽车音响系统进行交互。对音频进行分区,分区内的焦点分配,音量调节,音频路由等功能。因为CarAudioService是CarService中的其中一个服务,所以,我们直接make CarService 就可以进行编译。
CarAudioManager API方法
返回值 | 方法 | 参数 | 描述 |
---|---|---|---|
boolean | isDynamicRoutingEnabled | 返回是否动态路由是否可用的 | |
void | setGroupVolume | int groupId, int index, int flags | 设置组音量的音量值到primary zone |
void | setGroupVolume | int zoneId, int groupId, int index, int flags | 设置组音量的音量值 |
int | getGroupMaxVolume | int groupId | 获取通用域的传入groupId最大组音量 |
int | getGroupMaxVolume | int zoneId, int groupId | 获取传入zoneId域里的groupId最大组音量 |
int | getGroupMinVolume | int groupId | 获取通用域的传入groupId最小组音量 |
int | getGroupMinVolume | int zoneId, int groupId | 获取zoneId域里的groupId最小组音量 |
int | getGroupVolume | int groupId | 获取通用域的传入groupId音量值 |
int | getGroupVolume | int zoneId, int groupId | 获取传入zoneId域里的groupId音量值 |
void | setFadeTowardFront | float value | 设置前后音量偏移,0.0是平衡,1.0是前面 |
void | setBalanceTowardRight | float value | 设置左右音量偏移,0.0是平衡,1.0是右面 |
String[] | getExternalSources | 获取外部音源,除麦克风外的输入设备(警报音、DVD、收音机等) | |
CarAudioPatchHandle | createAudioPatch | String sourceAddress, @AudioAttributes.AttributeUsage int usage, int gainInMillibels | 通过getExternalSources给出的input port,创建一个外部音源到output的补丁,返回一个CarAudioPatchHandle |
void | releaseAudioPatch | CarAudioPatchHandle patch | 释放input port和output的关联 |
int | getVolumeGroupCount | 获取通用域的可用音量组数目 | |
int | getVolumeGroupCount | int zoneId | 获取zoneId指定域的可用音量组数目 |
int | getVolumeGroupIdForUsage | @AudioAttributes.AttributeUsage int usage | 获取传入音频用例对应的音量组Id |
int | getVolumeGroupIdForUsage | int zoneId @AudioAttributes.AttributeUsage int usage | 获取zoneId指定域传入音频用例对应的音量组Id |
boolean | setZoneIdForUid | int zoneId, int uid | 设置zoneId和uid的映射 |
boolean | clearZoneIdForUid | int uid | 清除uid的映射 |
int[] | getAudioZoneIds | 获取所有音频域的id | |
int | getZoneIdForUid | int uid | 获取uid映射的zoneId,没有映射返回primaryId |
int | getZoneIdForDisplay | Display display | 获取指定display的zoneId,没有找到返回primaryId |
int | getZoneIdForDisplayPortId | byte displayPortId | 获取指定display端口ID所对应的zoneId,没有找到返回primaryId |
int[] | getUsagesForVolumeGroupId | int groupId | 获取通用域里指定groupId所有的音频用例 |
int[] | getUsagesForVolumeGroupId | int zoneId, int groupId | 获取指定zoneId域里指定groupId所有的音频用例 |
void | registerCarVolumeCallback | CarVolumeCallback callback | 注册音量callback,添加到CarAudioManager维护的Callback组里,有onGroupVolumeChanged和onMasterMuteChanged的回调 |
void | unregisterCarVolumeCallback | CarVolumeCallback callback | 注销音量callback,从Callback组里删除 |
其实看下来CarAudioManager提供的方法主要集中在音量和音频分区两个方面。我们后面再详细讲解。其中大部分都行SystemApi方法,只有系统应用能调用,或者用java反射的方式调用。
服务的获取
应用成功连接CarService之后,调用Car的getCarManager(String servicename)方法获取服务
mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
-
1.CarService启动后,会在onCreate方法中构造一个ICarImpl类。
//CarService.java public void onCreate() { mICarImpl = new ICarImpl(this, mVehicle, SystemInterface.Builder.defaultSystemInterface(this).build(), mVehicleInterfaceName); mICarImpl.init(); }
-
2.ICarImpl类在构造方法中创建CarAudioService服务,并添加到服务列表内
//ICarImpl.java ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface, String vehicleInterfaceName, @Nullable CarUserService carUserService, @Nullable CarWatchdogService carWatchdogService, @Nullable ICarPowerPolicySystemNotification powerPolicyDaemon) { LimitedTimingsTraceLog t = new LimitedTimingsTraceLog( CAR_SERVICE_INIT_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS); t.traceBegin("ICarImpl.constructor"); //创建CarAudioService mCarAudioService = constructWithTrace(t, CarAudioService.class, () -> new CarAudioService(serviceContext)); ... //添加到服务列表 allServices.add(mCarAudioService); }
-
3.应用通过Car的getCarManager(String servicename)方法获取CarAudioManager。
//Car.java public Object getCarManager(String serviceName) { CarManagerBase manager; IBinder binder = mService.getCarService(serviceName); manager = createCarManagerLocked(serviceName, binder); return manager; }
-
4.Car.java与ICarImpl.java通过aidl通讯,所有会走到ICarImpl的getCarService方法中,通过servicename获取到CarAudoService,返回给Car.java中。
//ICarImpl.java @Override public IBinder getCarService(String serviceName) { if (!mFeatureController.isFeatureEnabled(serviceName)) { Log.w(CarLog.TAG_SERVICE, "getCarService for disabled service:" + serviceName); return null; } switch (serviceName) { case Car.AUDIO_SERVICE: return mCarAudioService; } }
-
5.将servicename和CarAudioService传递到createCarManagerLocked方法,创建一个CarAudioManager,同时将CarAudioService传入CarAudioManager中。
//Car.java private CarManagerBase createCarManagerLocked(String serviceName, IBinder binder) { CarManagerBase manager = null; switch (serviceName) { case AUDIO_SERVICE: manager = new CarAudioManager(this, binder); break; } return manager; }
构造方法
public CarAudioService(Context context) {
mContext = context;
//1.获取TelephonyManager和AudioManager服务,用于判断通话状态,调用系统Audio方法实现大部分audio功能。
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
//2.从config.xml的文件中获取mUseDynamicRouting属性。 mUseDynamicRouting是尤为重要的属性,为true时。才能开启汽车的动态路由配置。
mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting);
mKeyEventTimeoutMs =
mContext.getResources().getInteger(R.integer.audioVolumeKeyEventTimeoutMs);
mUseHalDuckingSignals = mContext.getResources().getBoolean(
R.bool.audioUseHalDuckingSignals);
//3.创建CarAudioSetting用于加载和保存音量设置。创建 mUidToZoneMap用于管理音频分区和uid关联集合。
mUidToZoneMap = new HashMap<>();
mCarVolumeCallbackHandler = new CarVolumeCallbackHandler();
mCarAudioSettings = new CarAudioSettings(mContext.getContentResolver());
mAudioZoneIdToUserIdMapping = new SparseIntArray();
mAudioVolumeAdjustmentContextsVersion =
mContext.getResources().getInteger(R.integer.audioVolumeAdjustmentContextsVersion);
mCarVolume = new CarVolume(mClock,
mAudioVolumeAdjustmentContextsVersion, mKeyEventTimeoutMs);
boolean useCarVolumeGroupMuting = mUseDynamicRouting && mContext.getResources().getBoolean(
R.bool.audioUseCarVolumeGroupMuting);
if (mAudioVolumeAdjustmentContextsVersion != VERSION_TWO && useCarVolumeGroupMuting) {
throw new IllegalArgumentException("audioUseCarVolumeGroupMuting is enabled but "
+ "this requires audioVolumeAdjustmentContextsVersion 2,"
+ " instead version " + mAudioVolumeAdjustmentContextsVersion + " was found");
}
mUseCarVolumeGroupMuting = useCarVolumeGroupMuting;
mPersistMasterMuteState = !mUseCarVolumeGroupMuting && mContext.getResources().getBoolean(
R.bool.audioPersistMasterMuteState);
}
初始化
CarAudioService初始化方法的调用是在CarAudioService启动,创建ICarImpl对像,调用ICarImpl的init方法,在ICarImpl的init方法初始所有car的 相关服务。
CarAudioService初始化的时候,根据mUseDynamicRouting这个属性走上的完全不一样的逻辑。
public void init() {
synchronized (mImplLock) {
mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class);
Car car = new Car(mContext, /* service= */null, /* handler= */ null);
mOccupantZoneManager = new CarOccupantZoneManager(car, mOccupantZoneService);
if (mUseDynamicRouting) {
setupDynamicRoutingLocked();//2.设置动态路由
setupHalAudioFocusListenerLocked();//3.注册来自hal层的焦点监听
setupAudioConfigurationCallbackLocked();//4.注册Audio播放和配置修改的监听
setupPowerPolicyListener();//5.启动Power电量监听和Policy策略修改监听
} else {
Slog.i(CarLog.TAG_AUDIO, "Audio dynamic routing not enabled, run in legacy mode");
setupLegacyVolumeChangedListener();//6.设置音量监听
}
mAudioManager.setSupportedSystemUsages(SYSTEM_USAGES);//
}
restoreMasterMuteState();//
}
其中setupDynamicRoutingLocked方法是初始化过程中的核心方法。看下具体的代码流程
private void setupDynamicRoutingLocked() {
//1.创建AudioPolicy策略
final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
builder.setLooper(Looper.getMainLooper());
//2.加载音频分区
loadCarAudioZonesLocked();
for (int i = 0; i < mCarAudioZones.size(); i++) {
CarAudioZone zone = mCarAudioZones.valueAt(i);
// Ensure HAL gets our initial value
//3.给音频分区设置音量增益及对应的index
zone.synchronizeCurrentGainIndex();
Slog.v(CarLog.TAG_AUDIO, "Processed audio zone: " + zone);
}
//4.给每个CarAudioZones中的CarVolumeGroup配置混音策略,并添加到AudioPolicy策略中。
CarAudioDynamicRouting.setupAudioDynamicRouting(builder, mCarAudioZones);
//5.将音量变化的监听回调注册到AudioPolicy中
CarAudioPolicyVolumeCallback
.addVolumeCallbackToPolicy(builder, this, mAudioManager,
mUseCarVolumeGroupMuting);
...
//6.创建车载音频焦点的管理对象,并且设置到AudioPolicy策略中
mFocusHandler = CarZonesAudioFocus.createCarZonesAudioFocus(mAudioManager,
mContext.getPackageManager(),
mCarAudioZones,
mCarAudioSettings,
ENABLE_DELAYED_AUDIO_FOCUS,
mCarDucking);
builder.setAudioPolicyFocusListener(mFocusHandler);
builder.setIsAudioFocusPolicy(true);
mAudioPolicy = builder.build();
mFocusHandler.setOwningPolicy(this, mAudioPolicy);
//7.将AudioPoliy策略注册到AudioManager中
int r = mAudioManager.registerAudioPolicy(mAudioPolicy);
...
setupOccupantZoneInfo();
}
总的来说,就是创建AudioPolicy策略,配置音频分区,混音,音频焦点相关策略,并注册到AudioManager中,AuiodManager再传入navie层的AudioPolicy中实现。