Android 4.1 与 4.2 在"强制拍照摄像音"处理上的区别分析

一晃接触Android已经2年多,从上层APK到底层HAL都算是小有了解,所以一直都在考虑是不是应该写些自己的原创心得。最近正好碰到了个问题,帮忙看了下,表示有些东西可以写了。大家将就看看就行了。

--------------


在Android 4.1之前上(也包括它自己)使用一个系统属性“ro.camera.sound.forced”来判断是否需要强制在静音模式下拍照摄像仍然发声。

 

这个属性作为一个只读属性,只可在启动之初进行设置,这样也避免了在开机后被修改。

开发人员可以在平台指定的目录下对相关的设备mk文件进行初始化,并在编译时自动保存到build.prop文件里,这样每次启动后都会设置这个属性。

 

那这个属性是在哪里使用的呢?答案就在AudioPolicyService里(之后简称APS)


我们可以在APS的构造函数里看到这个属性被读取。

 

文件路径:frameworks/av/services/audioflinger/AudioPolicyService.cpp


AudioPolicyService::AudioPolicyService()

    : BnAudioPolicyService() ,mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)

{

    char value[PROPERTY_VALUE_MAX];

    const struct hw_module_t *module;

    int forced_val;

    int rc;

 

    Mutex::Autolock _l(mLock);

 

    rc =mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,

                                              &mpAudioPolicy);

    ALOGE_IF(rc, "couldn't create audiopolicy (%s)", strerror(-rc));

    if (rc)

        return;

 

    property_get("ro.camera.sound.forced",value, "0");

    forced_val = strtol(value, NULL, 0);

   mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy,!forced_val);



我们看到他最终会调用mpAudioPolicy中的set_can_mute_enforced_audible方法,这里的mpAudioPolicy是一个指针,它是被create_audio_policy的赋值。

 

关于create_audio_policy,熟悉audio方面的同学应该知道他会创建到一个用户自定义的policy_hal模块的接口。(不熟悉audio初始化的同学可以等我的博客更新,也可百度撸下)。这里我们以默认android的代码进行分析。

 

文件路径:hardware/libhardware_legacy/audio/audio_policy_hal.cpp

首先我们可以看到在调用create_legacy_ap中定义了set_can_mute_enforced_audible具体实现函数,即ap_set_can_mute_enforced_audible

 

static int create_legacy_ap(const struct audio_policy_device *device,

                            struct audio_policy_service_ops*aps_ops,

                            void *service,

                            struct audio_policy**ap)

{

 

   lap->policy.set_can_mute_enforced_audible =

        ap_set_can_mute_enforced_audible;


然后我们来看下这个函数具体做了神马事情:

 

static void ap_set_can_mute_enforced_audible(struct audio_policy *pol,

                                            bool can_mute)

{

    struct legacy_audio_policy *lap =to_lap(pol);

   lap->apm->setSystemProperty("ro.camera.sound.forced",can_mute ? "0" : "1");

}

 

void AudioPolicyManagerBase::setSystemProperty(const char* property, const char*value)

{

    ALOGV("setSystemProperty() property%s, value %s", property, value);

    if (strcmp(property,"ro.camera.sound.forced") == 0) {

        if (atoi(value)) {

            ALOGV("ENFORCED_AUDIBLE cannotbe muted");

           mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;

        } else {

            ALOGV("ENFORCED_AUDIBLE can bemuted");

           mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;

        }

    }

}


 

我们可以看到它只是简单的根据属性值在AudioPolicyManagerBase里设置了mStreams数组里对于ENFORCED_AUDIBLE的mCanBeMuted变量值。

 

关于mStreams数组,他其实是个在APM里用来保存当前各条stream的音量参数,这包括了最大音量、最小音量、当然是否为静音、当前的音量。(不清楚的同学可以等我更新音频这一部分的博客,会具体介绍。)

 

关于ENFORCED_AUDIBLE,这代表了当前stream的类型,一共有10种stream类型。而拍照摄像就是用了这个stream来播放提示音的。

 

既然这里mCanBeMuted被修改了,所以我们只要关注它在哪里会被用到。

 

首先这里我们先要搞清楚在用户选择静音时,系统到底做了什么?

这个问题其实也很无聊,静音的意思就是没声音,那当然就是把音量调低咯。

之前我提过音量其实有最大、最小设置的,在android里各种stream的音量都是由index来决定的(可以理解为音量等级),比如铃声音量有7个等级,你调节到最高index就是7

所以这里当静音被选择时,所有声音的index就会被修改为0。

 

但是当静音选中时在哪里设置index的呢?答案就在AudioPolicyManagerBase里。

(这里我没有解释静音设置怎么调用到这边的,以后我会更新。)

 

status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream,

                                                     int index,

                                                     audio_devices_t device)

{

 

    // Force max volume if stream cannot bemuted

    if (!mStreams[stream].mCanBeMuted) index =mStreams[stream].mIndexMax;


 

我们可以看到这里会进行判断是否mCanBeMuted为true,如果是,那就设置为最大音量值。

之后就会调用checkAndSetVolume进行音量设置了。(具体设置请等我更新的博客,或者百度,说实话百度下会快点。)

 

好了,说到这4.1关于强制拍照摄像音的分析就结束了。

 

接下来我们看下4.2较之4.1的改变。

 

如果我们去4.2的AudioPolicyService里找获取属性的相关代码,我们会发现他们不见了。如果你有git服务器的话,通过查询历史,会提示有一个补丁删除这段代码,并且注释此功能已被AudioService实现,既然这样我们就去AudioService找找。

 

通过查找我们可以发现一段注释:

//=============================================================================

// Camera shutter sound policy.

// config_camera_sound_forced configuration option in config.xml defines if the camera shutter

// sound is forced (sound even if thedevice is in silent mode) or not. This option is false by

// default and can be overridden by country specific overlay in values-mccXXX/config.xml.

//=============================================================================


从注释来看,以往4.1代码里使用的属性已经被一个系统设置config_camera_sound_forced替代了。

 

这个设置文件的路径:frameworks/base/core/res/res/values/config.xml

 

首先我们看看AudioService的构造函数,我们发现它会读取这个系统设置,并初始化了类变量mCameraSoundForced

 

    /** @hide */

    public AudioService(Context context) {

 

        boolean cameraSoundForced = mContext.getResources().getBoolean(

               com.android.internal.R.bool.config_camera_sound_forced);

        mCameraSoundForced = newBoolean(cameraSoundForced);


 

这个变量会在程序使用AudioService改变音量index时进行判断。

 

        public synchronized boolean setIndex(int index, int device, boolean lastAudible) {

           int oldIndex = getIndex(device, false /* lastAudible */);

            index = getValidIndex(index);

            synchronized (mCameraSoundForced) {

                if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {

                    index = mIndexMax;

                }

            }


这里我简单的介绍下这个SetIndex函数,它的输入参数有“音量等级”,“设备值”,“是否记录改变操作” 三个。这里主要说下设备值,在android里每个设备都会对应一个stream对应的音量等级。而这个函数就是配置当前设备的音量等级的,比如扬声器的设备值为0x2,听筒为0x1。(在之后的博客中我应该会更新这些知识)

 

这时候大家可能在想为什么静音操作就会调用这个函数呢?所以我们接下来再来看下在设置静音模式之后都做了神马龌龊的事情。

首先当用户长按电源按钮之后会弹出一个对话框,这个对话框里有我们想要选择的静音模式按钮。(当然也包括了关机、飞行模式等其他按钮)

这个对话框的实现文件是在以下路径:

frameworks/base/policy/src/com/android/internal/policy/impl/GlobalActions.java

    private GlobalActionsDialog createDialog(){

        // Simple toggle style if there's novibrator, otherwise use a tri-state

        if (!mHasVibrator) {

            mSilentModeAction = newSilentModeToggleAction();

        } else {

            mSilentModeAction = newSilentModeTriStateAction(mContext, mAudioManager, mHandler);

        }

        // last: silent mode

        if (SHOW_SILENT_TOGGLE) {

            mItems.add(mSilentModeAction);

        }


这里我省略了些其他内容(关机、飞行模式选项),只列出了静音模式选项,我们可以从代码里知道如果设备支持震动,那么就是三个按钮,否则就是两个按钮。(即是否有震动选项)

接下来我们就来看下这个静音模式选项类SilentModeTriStateAction内部对用户选择的处理。

        public void onClick(View v) {

            if(!(v.getTag() instanceof Integer)) return;

            int index= (Integer) v.getTag();

           mAudioManager.setRingerMode(indexToRingerMode(index));

           mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);

        }

}


这里我们看到当用户选择某个设置时,函数首先会调用setRingerMode函数来设置响铃模式。(响铃模式在这里也分三种,即普通、静音、震动)

最终这个函数会通过binder(不熟悉binder的同学可以自己百度复习下)进而调用AudioService的setRingerMode。


    /** @see AudioManager#setRingerMode(int) */
    public void setRingerMode(int ringerMode) {
        if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
            ringerMode = AudioManager.RINGER_MODE_SILENT;
        }
        if (ringerMode != getRingerMode()) {
            setRingerModeInt(ringerMode, true);
            // Send sticky broadcast
            broadcastRingerMode(ringerMode);
        }
}


    private void setRingerModeInt(int ringerMode, boolean persist) {
        synchronized(mSettingsLock) {
            mRingerMode = ringerMode;
        }

        // Mute stream if not previously muted by ringer mode and ringer mode
        // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
        // Unmute stream if previously muted by ringer mode and ringer mode
        // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
        int numStreamTypes = AudioSystem.getNumStreamTypes();
        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
            if (isStreamMutedByRingerMode(streamType)) {
                if (!isStreamAffectedByRingerMode(streamType) ||
                    ringerMode == AudioManager.RINGER_MODE_NORMAL) {
                    // ring and notifications volume should never be 0 when not silenced
                    // on voice capable devices
                    if (mVoiceCapable &&
                            mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
                        synchronized (mStreamStates[streamType]) {
                            Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
                            Iterator i = set.iterator();
                            while (i.hasNext()) {
                                Map.Entry entry = (Map.Entry)i.next();
                                if ((Integer)entry.getValue() == 0) {
                                    entry.setValue(10);
                                }
                            }
                        }
                    }
                    mStreamStates[streamType].mute(null, false);
                    mRingerModeMutedStreams &= ~(1 << streamType);
                }
            } else {
                if (isStreamAffectedByRingerMode(streamType) &&
                    ringerMode != AudioManager.RINGER_MODE_NORMAL) {
                   mStreamStates[streamType].mute(null, true);
                   mRingerModeMutedStreams |= (1 << streamType);
               }
            }
        }

这里我们可以看到又继续调用了setRingerModeInt,我们在来看看这个函数。

    private voidsetRingerModeInt(int ringerMode, boolean persist) {

       synchronized(mSettingsLock) {

           mRingerMode = ringerMode;

        }

 

        // Mute streamif not previously muted by ringer mode and ringer mode

        // is notRINGER_MODE_NORMAL and stream is affected by ringer mode.

        // Unmutestream if previously muted by ringer mode and ringer mode

        // isRINGER_MODE_NORMAL or stream is not affected by ringer mode.

        intnumStreamTypes = AudioSystem.getNumStreamTypes();

        for (intstreamType = numStreamTypes - 1; streamType >= 0; streamType--) {

            if(isStreamMutedByRingerMode(streamType)) {

                if(!isStreamAffectedByRingerMode(streamType) ||

                   ringerMode == AudioManager.RINGER_MODE_NORMAL) {

                    //ring and notifications volume should never be 0 when not silenced

                    //on voice capable devices

                    if(mVoiceCapable &&

                           mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {

                        synchronized(mStreamStates[streamType]) {

                           Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();

                           Iterator i = set.iterator();

                           while (i.hasNext()) {

                                Map.Entry entry =(Map.Entry)i.next();

                                if((Integer)entry.getValue() == 0) {

                                   entry.setValue(10);

                                }

                           }

                       }

                    }

                   mStreamStates[streamType].mute(null, false);

                   mRingerModeMutedStreams &= ~(1 << streamType);

                }

            } else {

                if(isStreamAffectedByRingerMode(streamType) &&

                   ringerMode != AudioManager.RINGER_MODE_NORMAL) {

                  mStreamStates[streamType].mute(null, true);

                  mRingerModeMutedStreams |= (1 << streamType);

               }

            }

        }


我们可以从注释里看到这里首先会对各个stream做些判断,即包括是否上一次已被静音且不是普通模式、或者是否当前stream可以被修改。如果上次已经被修改为静音,那将会解除。当然这里我们是从普通模式变成静音模式,所以肯定会进行静音操作。

mStreamStates[streamType].mute(null, true);

在这里简单介绍下mStreamStates,它是一个VolumeStreamState的类组数,用来在AudioService存放各个stream音量相关信息的类。

这里我们就看下mute函数都做了什么事。

        public synchronized void mute(IBinder cb, boolean state) {
            VolumeDeathHandler handler = getDeathHandler(cb, state);
            if (handler == null) {
                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
                return;
            }
            handler.mute(state);
        }
        private class VolumeDeathHandler implements IBinder.DeathRecipient {
            private IBinder mICallback; // To be notified of client's death
            private int mMuteCount; // Number of active mutes for this client


            // must be called while synchronized on parent VolumeStreamState
            public void mute(boolean state) {
                if (state) {
                    if (mMuteCount == 0) {
                        // Register for client death notification

                            if (muteCount() == 0) {
                                Set set = mIndex.entrySet();
                                Iterator i = set.iterator();
                                while (i.hasNext()) {
                                    Map.Entry entry = (Map.Entry)i.next();
                                    int device = ((Integer)entry.getKey()).intValue();
                                    setIndex(0, device, false /* lastAudible */);
                                }
                                sendMsg(mAudioHandler,
                                        MSG_SET_ALL_VOLUMES,
                                        SENDMSG_QUEUE,
                                        0,
                                        0,
                                        VolumeStreamState.this, 0);
                            }


这里我省略了部分代码,只保留了关键的信息。我们可以清楚的看到最终mute函数调用了SetIndex函数对设配相关音量进行设置,这样最终它就会判断是否为强制拍照摄像音。这样我们就知道了为什么我们设置的系统设置才会生效了。



好了,说到这里,应该都能知道为什么要判断这个强制变量了,即当每次设置音量等级时都设置为最大音量等级。所以我们即使在设置了静音模式后依然可以听到声音,其实是因为音量一直是最大值。

 

4.1和4.2对于强制拍照摄像声音的处理就介绍到这了。

 

结语:第一次写这种技术博客有些不足之初望大家谅解,有些知识点也只是个人观点,如果有错误的话也请嘴下留情。说实话,个人的文采不是很好,写不出那种幽默的感觉,以后可以改进改进,但是通过分享我觉得还是能提高自己的能力,当然也希望大家能够在其中学到或者找到自己想要的知识。


你可能感兴趣的:(Android 4.1 与 4.2 在"强制拍照摄像音"处理上的区别分析)