目錄(?)
[+]
在Android平台上,音量鍵,首頁鍵(home),都是全局按鍵,但是首頁鍵是個例外不能被應用所捕獲。下面分析一下音量按鍵的流程,主要從framework層處理開始,至于
EventHub 從驅動的/dev/input/event0擷取按鍵資訊到上抛屬于Android input 系統方面的流程,下面基于android KK平台分析。
系統層接收音量按鍵
ViewRootImpl.processKeyEvent 處理Activity 上面收到的按鍵
[java] view plain copy
- 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
[java] view plain copy
- 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進一步分發
[java] view plain copy
- 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的處理流程
[java] view plain copy
- 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
[java] view plain copy
- boolean onKeyDown(int keyCode, KeyEvent event) {
- 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處理流程 [java] view plain copy
- public void handleKeyDown(KeyEvent event, int stream) {
- int keyCode = event.getKeyCode();
- switch (keyCode) {
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- 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重新整理界面音量顯示,并且廣播通過上層應用。 [java] view plain copy
- 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 [java] view plain copy
- 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設定裝置音量 [java] view plain copy
- 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中有定義。 [cpp] view plain copy
- static int android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
- jobject thiz,
- jint stream,
- jint index,
- jint device)
- {
- return check_AudioSystem_Command(
- AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
- index,
- (audio_devices_t)device));
- }
進入AudioSystem.cpp中setStreamVolumeIndex [cpp] view plain copy
- status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
- int index,
- audio_devices_t device)
- {
- const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return PERMISSION_DENIED;
- return aps->setStreamVolumeIndex(stream, index, device);
- }
擷取去音頻政策服務(AudioPolicyService.cpp),進行設定 [cpp] view plain copy
- 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
更多音頻政策相關流程後續分析。