android10 按键音量调节源码解析

/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

static IAudioService getAudioService() {
    IAudioService audioService = IAudioService.Stub.asInterface(
            ServiceManager.checkService(Context.AUDIO_SERVICE));
    if (audioService == null) {
        Log.w(TAG, "Unable to find IAudioService interface.");
    }
    return audioService;
}

......................................................................

private void dispatchDirectAudioEvent(KeyEvent event) {
    // When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
    // or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
    HdmiControlManager hdmiControlManager = getHdmiControlManager();
    if (null != hdmiControlManager
            && !hdmiControlManager.getSystemAudioMode()
            && shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
        HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
        if (audioSystemClient != null) {
            audioSystemClient.sendKeyEvent(
                    event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
            return;
        }
    }
    if (event.getAction() != KeyEvent.ACTION_DOWN) {
        return;
    }
    int keyCode = event.getKeyCode();
    int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
            | AudioManager.FLAG_FROM_KEY;
    String pkgName = mContext.getOpPackageName();

    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_UP:
            try {
                getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
                        AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
            } catch (Exception e) {
                Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
            }
            break;
        case KeyEvent.KEYCODE_VOLUME_DOWN:
            try {
                getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
                        AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
            } catch (Exception e) {
                Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
            }
            break;
        case KeyEvent.KEYCODE_VOLUME_MUTE:
            try {
                if (event.getRepeatCount() == 0) {
                    getAudioService().adjustSuggestedStreamVolume(
                            AudioManager.ADJUST_TOGGLE_MUTE,
                            AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
                }
            } catch (Exception e) {
                Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
            }
            break;
    }
}

按键的响应都是在PhoneWindowManager.java类中进行处理的。根据不同的按键事件调节音量大小。

KeyEvent.KEYCODE_VOLUME_UP 音量+
KeyEvent.KEYCODE_VOLUME_DOWN 音量-
KeyEvent.KEYCODE_VOLUME_MUTE 静音

在dispatchDirectAudioEvent方法中通过switch判断调节音量的类型。

/frameworks/base/services/core/java/com/android/server/audio/AudioService.java

public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
        String callingPackage, String caller) {
    final IAudioPolicyCallback extVolCtlr;
    synchronized (mExtVolumeControllerLock) {
        extVolCtlr = mExtVolumeController;
    }
    if (extVolCtlr != null) {
        sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
                direction, 0 /*ignored*/,
                extVolCtlr, 0 /*delay*/);
    } else {
        adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
                caller, Binder.getCallingUid());
    }
}

通过AudioService的代理调用AudioService的adjustSuggestedStreamVolume。如果extVolCtlr == null就会执行adjustSuggestedStreamVolume方法。

protected void adjustStreamVolume(int streamType, int direction, int flags,
        String callingPackage, String caller, int uid) {
    
	.....................................................................

    if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
        mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);

        if (isMuteAdjust) {
            
			.....................................................................
			
        } else if ((direction == AudioManager.ADJUST_RAISE) &&
                !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
            Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
            mVolumeController.postDisplaySafeVolumeWarning(flags);
        } else if (((device & mFullVolumeDevices) == 0)
                && (streamState.adjustIndex(direction * step, device, caller)
                        || streamState.mIsMuted)) {
            
			.....................................................................
			
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);
        }

        .....................................................................
		
    }
}

由streamState.adjustIndex(direction * step, device, caller)方法会通知UI音量大小的变化,

通过sendMsg发送消息MSG_SET_DEVICE_VOLUME设置底层音量大小

public boolean adjustIndex(int deltaIndex, int device, String caller) {
    return setIndex(getIndex(device) + deltaIndex, device, caller);
}

public boolean setIndex(int index, int device, String caller) {
    boolean changed;
    int oldIndex;
    synchronized (mSettingsLock) {
        synchronized (VolumeStreamState.class) {
            oldIndex = getIndex(device);
            index = getValidIndex(index);
            if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
                index = mIndexMax;
            }
            mIndexMap.put(device, index);

            changed = oldIndex != index;
            // Apply change to all streams using this one as alias if:
            // - the index actually changed OR
            // - there is no volume index stored for this device on alias stream.
            // If changing volume of current device, also change volume of current
            // device on aliased stream
            final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
            final int numStreamTypes = AudioSystem.getNumStreamTypes();
            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                final VolumeStreamState aliasStreamState = mStreamStates[streamType];
                if (streamType != mStreamType &&
                        mStreamVolumeAlias[streamType] == mStreamType &&
                        (changed || !aliasStreamState.hasIndexForDevice(device))) {
                    final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
                    aliasStreamState.setIndex(scaledIndex, device, caller);
                    if (isCurrentDevice) {
                        aliasStreamState.setIndex(scaledIndex,
                                getDeviceForStream(streamType), caller);
                    }
                }
            }
            // Mirror changes in SPEAKER ringtone volume on SCO when
            if (changed && mStreamType == AudioSystem.STREAM_RING
                    && device == AudioSystem.DEVICE_OUT_SPEAKER) {
                for (int i = 0; i < mIndexMap.size(); i++) {
                    int otherDevice = mIndexMap.keyAt(i);
                    if ((otherDevice & AudioSystem.DEVICE_OUT_ALL_SCO) != 0) {
                        mIndexMap.put(otherDevice, index);
                    }
                }
            }
        }
    }
    if (changed) {
        oldIndex = (oldIndex + 5) / 10;
        index = (index + 5) / 10;
        // log base stream changes to the event log
        if (mStreamVolumeAlias[mStreamType] == mStreamType) {
            if (caller == null) {
                Log.w(TAG, "No caller for volume_changed event", new Throwable());
            }
            EventLogTags.writeVolumeChanged(mStreamType, oldIndex, index, mIndexMax / 10,
                    caller);
        }
        // fire changed intents for all streams
        mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
        mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
        mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
                mStreamVolumeAlias[mStreamType]);
        sendBroadcastToAll(mVolumeChanged);
    }
    return changed;
}


................................................................................

private void sendBroadcastToAll(Intent intent) {
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    final long ident = Binder.clearCallingIdentity();
    try {
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

调用sendBroadcastToAll方法发送广播,通知UI声音大小的变化。

/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {

    .......................................................................

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {

            case MSG_SET_DEVICE_VOLUME:
                setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
                break;
    .......................................................................
        }
    }
}

在AudioHandler中调用setDeviceVolume调节底层声音的大小

/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {

    final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();

    synchronized (VolumeStreamState.class) {
        // Apply volume
        streamState.applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);

        // 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) && isAvrcpAbsVolSupported
                        && ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
                    mStreamStates[streamType].applyDeviceVolume_syncVSS(device,
                            isAvrcpAbsVolSupported);
                }
                mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice,
                        isAvrcpAbsVolSupported);
            }
        }
    }
    // Post a persist volume msg
    sendMsg(mAudioHandler,
            MSG_PERSIST_VOLUME,
            SENDMSG_QUEUE,
            device,
            0,
            streamState,
            PERSIST_DELAY);

}

调用applyDeviceVolume_syncVSS设置声音的大小

// must be called while synchronized VolumeStreamState.class
/*package*/ void applyDeviceVolume_syncVSS(int device, boolean isAvrcpAbsVolSupported) {
    int index;
    if (mIsMuted) {
        index = 0;
    } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && isAvrcpAbsVolSupported) {
        index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
    } else if ((device & mFullVolumeDevices) != 0) {
        index = (mIndexMax + 5)/10;
    } else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
        index = (mIndexMax + 5)/10;
    } else {
        index = (getIndex(device) + 5)/10;
    }
    setStreamVolumeIndex(index, device);
}

在setStreamVolumeIndex方法中调用AudioSystem的setStreamVolumeIndexAS方法设置声音大小

private void setStreamVolumeIndex(int index, int device) {
    // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
    // This allows RX path muting by the audio HAL only when explicitly muted but not when
    // index is just set to 0 to repect BT requirements
    if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0 && !mIsMuted) {
        index = 1;
    }
    AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}

/frameworks/base/media/java/android/media/AudioSystem.java

/** Wrapper for native methods called from AudioService */
public static int setStreamVolumeIndexAS(int stream, int index, int device) {
    if (DEBUG_VOLUME) {
        Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
                + " dev=" + Integer.toHexString(device) + " idx=" + index);
    }
    return setStreamVolumeIndex(stream, index, device);
}

..............................................................................

private static native int setStreamVolumeIndex(int stream, int index, int device);

调用到setStreamVolumeIndex之后就是native底层的方法

/frameworks/av/media/libaudioclient/AudioSystem.cpp

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);
}

你可能感兴趣的:(android)