在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; }
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,
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); }
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中。
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
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),进行设置
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也在其中注册。
更多音频策略相关流程后续分析。