在Android平台上,音量键,主页键(home),都是全局按键,但是主页键是个例外不能被应用所捕获。下面分析一下音量按键的流程,主要从framework层处理开始,至于

EventHub 从驱动的/dev/input/event0获取按键信息到上抛属于Android input 系统方面的流程,下面基于android KK平台分析。


系统层接收音量按键

ViewRootImpl.processKeyEvent 处理Activity 上面收到的按键

        private int processKeyEvent(QueuedInputEvent q) {            final KeyEvent event = (KeyEvent)q.mEvent;            if (event.getAction() != KeyEvent.ACTION_UP) {                // If delivering a new key event, make sure the window is                // now allowed to start updating.                handleDispatchDoneAnimating();            }            // Deliver the key to the view hierarchy.            if (mView.dispatchKeyEvent(event)) {                return FINISH_HANDLED;            }            if (shouldDropInputEvent(q)) {                return FINISH_NOT_HANDLED;            }            // If the Control modifier is held, try to interpret the key as a shortcut.            if (event.getAction() == KeyEvent.ACTION_DOWN                    && event.isCtrlPressed()                    && event.getRepeatCount() == 0                    && !KeyEvent.isModifierKey(event.getKeyCode())) {                if (mView.dispatchKeyShortcutEvent(event)) {                    return FINISH_HANDLED;                }                if (shouldDropInputEvent(q)) {                    return FINISH_NOT_HANDLED;                }            }            // Apply the fallback event policy.            if (mFallbackEventHandler.dispatchKeyEvent(event)) {                return FINISH_HANDLED;            }            if (shouldDropInputEvent(q)) {                return FINISH_NOT_HANDLED;            }            // Handle automatic focus changes.            if (event.getAction() == KeyEvent.ACTION_DOWN) {                int direction = 0;                switch (event.getKeyCode()) {                    case KeyEvent.KEYCODE_DPAD_LEFT:                        if (event.hasNoModifiers()) {                            direction = View.FOCUS_LEFT;                        }                        break;                    case KeyEvent.KEYCODE_DPAD_RIGHT:                        if (event.hasNoModifiers()) {                            direction = View.FOCUS_RIGHT;                        }                        break;                    case KeyEvent.KEYCODE_DPAD_UP:                        if (event.hasNoModifiers()) {                            direction = View.FOCUS_UP;                        }                        break;                    case KeyEvent.KEYCODE_DPAD_DOWN:                        if (event.hasNoModifiers()) {                            direction = View.FOCUS_DOWN;                        }                        break;                    case KeyEvent.KEYCODE_TAB:                        if (event.hasNoModifiers()) {                            direction = View.FOCUS_FORWARD;                        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {                            direction = View.FOCUS_BACKWARD;                        }                        break;                }                if (direction != 0) {                    View focused = mView.findFocus();                    if (focused != null) {                        View v = focused.focusSearch(direction);                        if (v != null && v != focused) {                            // do the math the get the interesting rect                            // of previous focused into the coord system of                            // newly focused view                            focused.getFocusedRect(mTempRect);                            if (mView instanceof ViewGroup) {                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(                                        focused, mTempRect);                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(                                        v, mTempRect);                            }                            if (v.requestFocus(direction, mTempRect)) {                                playSoundEffect(SoundEffectConstants                                        .getContantForFocusDirection(direction));                                return FINISH_HANDLED;                            }                        }                        // Give the focused view a last chance to handle the dpad key.                        if (mView.dispatchUnhandledMove(focused, direction)) {                            return FINISH_HANDLED;                        }                    } else {                        // find the best view to give focus to in this non-touch-mode with no-focus                        View v = focusSearch(null, direction);                        if (v != null && v.requestFocus(direction)) {                            return FINISH_HANDLED;                        }                    }                }            }            return FORWARD;        }
从中可以看到mView.dispatchKeyEvent(event),完成将按键发送给Activity处理,由于每个Activity都是view的子类,所有这些按键将dispatchKeyEvent传递给onKeyDown

    public boolean dispatchKeyEvent(KeyEvent event) {        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onKeyEvent(event, 0);        }        // Give any attached key listener a first crack at the event.        //noinspection SimplifiableIfStatement        ListenerInfo li = mListenerInfo;        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {            return true;        }        if (event.dispatch(this, mAttachInfo != null                ? mAttachInfo.mKeyDispatchState : null, this)) {            return true;        }        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);        }        return false;    }
由上面View.dispatchKeyEvent方法可知,通过event.dispatch进一步分发

    public final boolean dispatch(Callback receiver, DispatcherState state,            Object target) {        switch (mAction) {            case ACTION_DOWN: {                mFlags &= ~FLAG_START_TRACKING;                if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state                        + ": " + this);                boolean res = receiver.onKeyDown(mKeyCode, this);                if (state != null) {                    if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {                        if (DEBUG) Log.v(TAG, "  Start tracking!");                        state.startTracking(this, target);                    } else if (isLongPress() && state.isTracking(this)) {                        try {                            if (receiver.onKeyLongPress(mKeyCode, this)) {                                if (DEBUG) Log.v(TAG, "  Clear from long press!");                                state.performedLongPress(this);                                res = true;                            }                        } catch (AbstractMethodError e) {                        }                    }                }                return res;            }            case ACTION_UP:                if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state                        + ": " + this);                if (state != null) {                    state.handleUpEvent(this);                }                return receiver.onKeyUp(mKeyCode, this);            case ACTION_MULTIPLE:                final int count = mRepeatCount;                final int code = mKeyCode;                if (receiver.onKeyMultiple(code, count, this)) {                    return true;                }                if (code != KeyEvent.KEYCODE_UNKNOWN) {                    mAction = ACTION_DOWN;                    mRepeatCount = 0;                    boolean handled = receiver.onKeyDown(code, this);                    if (handled) {                        mAction = ACTION_UP;                        receiver.onKeyUp(code, this);                    }                    mAction = ACTION_MULTIPLE;                    mRepeatCount = count;                    return handled;                }                return false;        }        return false;    }
KeyEvent.dispatch通过receiver.onKeyDown将最终的按键消息发送给当前的Activity,而receiver即为KeyEvent.Callback的实现类(View的子类等等),至此如果上面上传

应用处理完了就会返回,如果没有处理就会流向mFallbackEventHandler.dispatchKeyEvent(event),其实mFallbackEventHandler就是PhoneFallbackEventHandler,接着看

PhoneFallbackEventHandler.dispatchKeyEvent的处理流程

    public boolean dispatchKeyEvent(KeyEvent event) {        final int action = event.getAction();        final int keyCode = event.getKeyCode();        if (action == KeyEvent.ACTION_DOWN) {            return onKeyDown(keyCode, event);        } else {            return onKeyUp(keyCode, event);        }    }
进入onKeyDown

    boolean onKeyDown(int keyCode, KeyEvent event) {        /* ****************************************************************************         * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.         * See the comment in PhoneWindow.onKeyDown         * ****************************************************************************/        final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();        switch (keyCode) {            case KeyEvent.KEYCODE_VOLUME_UP:            case KeyEvent.KEYCODE_VOLUME_DOWN:            case KeyEvent.KEYCODE_VOLUME_MUTE: {                getAudioManager().handleKeyDown(event, AudioManager.USE_DEFAULT_STREAM_TYPE);                return true;            }            ......        }        return false;    }

AudioManager处理音量

从上面分析知道PhoneFallbackEventHandler处理一些Activity没有处理的全局按键,音量键接着进入handleKeyDown处理流程
    public void handleKeyDown(KeyEvent event, int stream) {        int keyCode = event.getKeyCode();        switch (keyCode) {            case KeyEvent.KEYCODE_VOLUME_UP:            case KeyEvent.KEYCODE_VOLUME_DOWN:                /*                 * Adjust the volume in on key down since it is more                 * responsive to the user.                 */                int flags = FLAG_SHOW_UI | FLAG_VIBRATE;                if (mUseMasterVolume) {                    adjustMasterVolume(                            keyCode == KeyEvent.KEYCODE_VOLUME_UP                                    ? ADJUST_RAISE                                    : ADJUST_LOWER,                            flags);                } else {                    adjustSuggestedStreamVolume(                            keyCode == KeyEvent.KEYCODE_VOLUME_UP                                    ? ADJUST_RAISE                                    : ADJUST_LOWER,                            stream,                            flags);                }                break;            case KeyEvent.KEYCODE_VOLUME_MUTE:                if (event.getRepeatCount() == 0) {                    if (mUseMasterVolume) {                        setMasterMute(!isMasterMute());                    } else {                        // TODO: Actually handle MUTE.                    }                }                break;        }    }
mUseMasterVolume ( =  com.android.internal.R.bool.config_useMasterVolume),配置文件config.xml中该值为0,那么将进入adjustSuggestedStreamVolume, 再接着就进入adjustSuggestedStreamVolume,如果当前的streamType为STREAM_REMOTE_MUSIC,则走mMediaFocusControl.adjustRemoteVolume,其它类型 走音量的通用设置流程adjustStreamVolume

AudioService音量控制流程

从adjustSuggestedStreamVolume 过渡到adjustStreamVolume,进入音量设置的主要流程,主要对流类型,设备,声音设备状态,步进大小进行判断处理,另外蓝牙设 备音量和主设备音量进行了控制,最后通过mVolumePanel刷新界面音量显示,并且广播通过上层应用。
    public void adjustStreamVolume(int streamType, int direction, int flags,            String callingPackage) {        if (mUseFixedVolume) {            return;        }        if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);        ensureValidDirection(direction);        ensureValidStreamType(streamType);        // use stream type alias here so that streams with same alias have the same behavior,        // including with regard to silent mode control (e.g the use of STREAM_RING below and in        // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)        int streamTypeAlias = mStreamVolumeAlias[streamType];        VolumeStreamState streamState = mStreamStates[streamTypeAlias];        final int device = getDeviceForStream(streamTypeAlias);        int aliasIndex = streamState.getIndex(device);        boolean adjustVolume = true;        int step;        // skip a2dp absolute volume control request when the device        // is not an a2dp device        if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 &&            (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {            return;        }        if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),                callingPackage) != AppOpsManager.MODE_ALLOWED) {            return;        }        // reset any pending volume command        synchronized (mSafeMediaVolumeState) {            mPendingVolumeCommand = null;        }        flags &= ~AudioManager.FLAG_FIXED_VOLUME;        if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&               ((device & mFixedVolumeDevices) != 0)) {            flags |= AudioManager.FLAG_FIXED_VOLUME;            // Always toggle between max safe volume and 0 for fixed volume devices where safe            // volume is enforced, and max and 0 for the others.            // This is simulated by stepping by the full allowed volume range            if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&                    (device & mSafeMediaVolumeDevices) != 0) {                step = mSafeMediaVolumeIndex;            } else {                step = streamState.getMaxIndex();            }            if (aliasIndex != 0) {                aliasIndex = step;            }        } else {            // convert one UI step (+/-1) into a number of internal units on the stream alias            step = rescaleIndex(10, streamType, streamTypeAlias);        }        // If either the client forces allowing ringer modes for this adjustment,        // or the stream type is one that is affected by ringer modes        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||                (streamTypeAlias == getMasterStreamType())) {            int ringerMode = getRingerMode();            // do not vibrate if already in vibrate mode            if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {                flags &= ~AudioManager.FLAG_VIBRATE;            }            // Check if the ringer mode changes with this volume adjustment. If            // it does, it will handle adjusting the volume, so we won't below            adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);        }        int oldIndex = mStreamStates[streamType].getIndex(device);        if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {            // Check if volume update should be send to AVRCP            if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&                (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&                (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {                synchronized (mA2dpAvrcpLock) {                    if (mA2dp != null && mAvrcpAbsVolSupported) {                        mA2dp.adjustAvrcpAbsoluteVolume(direction);                    }                }            }            if ((direction == AudioManager.ADJUST_RAISE) &&                    !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {                Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);                mVolumePanel.postDisplaySafeVolumeWarning(flags);            } else if (streamState.adjustIndex(direction * step, device)) {                // Post message to set system volume (it in turn will post a message                // to persist). Do not change volume if stream is muted.                sendMsg(mAudioHandler,                        MSG_SET_DEVICE_VOLUME,                        SENDMSG_QUEUE,                        device,                        0,                        streamState,                        0);            }        }        int index = mStreamStates[streamType].getIndex(device);        sendVolumeUpdate(streamType, oldIndex, index, flags);    }

蓝牙音量的控制

有上可知,如果当前连接了蓝牙也将对音量进行控制,mA2dp.adjustAvrcpAbsoluteVolume,以后分析。

音频处理设置

音频处理由AudioHandler来进行, adjustStreamVolume做完相关处理后,通过sendMsg发送音量变化消息MSG_SET_DEVICE_VOLUME进入 AudioHandler.handleMessage调用AudioHandler.setDeviceVolume
        private void setDeviceVolume(VolumeStreamState streamState, int device) {            // Apply volume            streamState.applyDeviceVolume(device);            // Apply change to all streams using this one as alias            int numStreamTypes = AudioSystem.getNumStreamTypes();            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {                if (streamType != streamState.mStreamType &&                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {                    // Make sure volume is also maxed out on A2DP device for aliased stream                    // that may have a different device selected                    int streamDevice = getDeviceForStream(streamType);                    if ((device != streamDevice) && mAvrcpAbsVolSupported &&                            ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {                        mStreamStates[streamType].applyDeviceVolume(device);                    }                    mStreamStates[streamType].applyDeviceVolume(streamDevice);                }            }            // Post a persist volume msg            sendMsg(mAudioHandler,                    MSG_PERSIST_VOLUME,                    SENDMSG_QUEUE,                    device,                    0,                    streamState,                    PERSIST_DELAY);        }
VolumeStreamState.applyDeviceVolume设置设备音量
        public void applyDeviceVolume(int device) {            int index;            if (isMuted()) {                index = 0;            } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&                       mAvrcpAbsVolSupported) {                index = (mIndexMax + 5)/10;            } else {                index = (getIndex(device) + 5)/10;            }            AudioSystem.setStreamVolumeIndex(mStreamType, index, device);        }
接着发送MSG_PERSIST_VOLUME消息通过handleMessage进入persistVolume,最终调用System.putIntForUser将用户设置的内容设置到Settings.system中。

AudioSystem处理

applyDeviceVolume处理完,AudioSystem就开始接着往下设置setStreamVolumeIndex,该接口也即android_media_AudioSystem_setStreamVolumeIndex 在frameworks\base\core\jni\android_media_AudioSystem.cpp中有定义。
static int android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,                                               jobject thiz,                                               jint stream,                                               jint index,                                               jint device){    return check_AudioSystem_Command(            AudioSystem::setStreamVolumeIndex(static_cast (stream),                                              index,                                              (audio_devices_t)device));}
进入AudioSystem.cpp中setStreamVolumeIndex
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,                                           int index,                                           audio_devices_t device){    const sp& aps = AudioSystem::get_audio_policy_service();    if (aps == 0) return PERMISSION_DENIED;    return aps->setStreamVolumeIndex(stream, index, device);}
获取去音频策略服务(AudioPolicyService.cpp),进行设置
status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,                                                  int index,                                                  audio_devices_t device){    if (mpAudioPolicy == NULL) {        return NO_INIT;    }    if (!settingsAllowed()) {        return PERMISSION_DENIED;    }    if (uint32_t(stream) >= AUDIO_STREAM_CNT) {        return BAD_VALUE;    }    Mutex::Autolock _l(mLock);    if (mpAudioPolicy->set_stream_volume_index_for_device) {        return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy,                                                                stream,                                                                index,                                                                device);    } else {        return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);    }}
AudioPolicyService为音频策略系统服务在main_mediaserver.cpp中注册,AudioFlinger也在其中注册。
        mpAudioPolicy作为audio_policy类型的对象,其方法主要在Hardware层实现,可以查看相关文件audio_policy_hal.cpp 或者 audio_policy.c,也就是在库 audio.a2dp.xxx.so ,audio.btmic.xxx.so,audio.primary.xxxx 库中实现.

AudioPolicyService.cpp构造函数中就有hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);打印HAL层的库。

通知上层应用

sendVolumeUpdate在音量设置完成之后,完成画面刷新,并广播通知上层应用。

扩展连接:http://www.2cto.com/kf/201409/337102.html


更多音频策略相关流程后续分析。

更多相关文章

  1. android发送模拟按键消息,出现死锁,timeout的解决方法
  2. Android(安卓)M 差分包的制作流程
  3. Android(安卓)8.1 开机流程分析(1)
  4. 32位Ubuntu12.04编译VLC流程
  5. Android进程系列第三篇---SystemServer进程的创建流程
  6. Android耳机线控详解,蓝牙耳机按钮监听(仿酷狗线控效果)
  7. Android(安卓)-- Home按键
  8. Android(安卓)判断系统用户无操作
  9. Android按键分发流程之java层按键传递

随机推荐

  1. Android DisplayMetrics类简介
  2. Android SDK Platforms 提取下载
  3. 如何去写 Android(安卓)init.rc
  4. android 条码识别软件开发全解析(续1详解
  5. Android 学习笔记【基础扫盲篇】
  6. Android(安卓)UI布局优化的要点
  7. android 联系人详解
  8. Android中10个成功的开源项目
  9. Android推送通知的实现--Android推送通知
  10. android模拟器无法启动问题之中文路径