CarAudio(一)构造方法与初始化

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);
888.png
  • 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);
    }

初始化

999.png

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中实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容