音量上下鍵控制


 

frameworks/base/media/java/android/media/AudioService.java
    //direction 一般取1(音量+),0, -1(音量-) 
    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
            String callingPackage) {
        adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
                Binder.getCallingUid());
    }


    private void adjustStreamVolume(int streamType, int direction, int flags,
            String callingPackage, int uid) {
        if (mUseFixedVolume) {
            return;
        }
        if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction
                + ", flags="+flags);

        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], uid, 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
            final int result = checkForRingerModeChange(aliasIndex, direction, step);
            adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
            // If suppressing a volume adjustment in silent mode, display the UI hint
            if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
                flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
            }
        }

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

            // Check if volume update should be send to Hdmi system audio.
            int newIndex = mStreamStates[streamType].getIndex(device);
            if (mHdmiManager != null) {
                synchronized (mHdmiManager) {
                    if (mHdmiTvClient != null &&
                        streamTypeAlias == AudioSystem.STREAM_MUSIC &&
                        (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0 &&
                        oldIndex != newIndex) {
                        int maxIndex = getStreamMaxVolume(streamType);
                        synchronized (mHdmiTvClient) {
                            if (mHdmiSystemAudioSupported) {
                                mHdmiTvClient.setSystemAudioVolume(
                                        (oldIndex + 5) / 10, (newIndex + 5) / 10, maxIndex);
                            }
                        }
                    }
                    // mHdmiCecSink true => mHdmiPlaybackClient != null
                    if (mHdmiCecSink &&
                            streamTypeAlias == AudioSystem.STREAM_MUSIC &&
                            oldIndex != newIndex) {
                        synchronized (mHdmiPlaybackClient) {
                            int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
                                                               KeyEvent.KEYCODE_VOLUME_UP;
                            mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
                            mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
                        }
                    }
                }
            }
        }
        int index = mStreamStates[streamType].getIndex(device);
        sendVolumeUpdate(streamType, oldIndex, index, flags);
    }

    //mSafeMediaVolumeState並非一直是SAFE_MEDIA_VOLUME_ACTIVE, 警告界面設置后狀態為SAFE_MEDIA_VOLUME_INACTIVE  //會針對音量大於一定值時計時,當時間超過20個小時時,會修改狀態為SAFE_MEDIA_VOLUME_ACTIVE,當再次調節音量,  //該音量超過警告值時,會再次彈出警告界面
    private boolean checkSafeMediaVolume(int streamType, int index, int device) {
        synchronized (mSafeMediaVolumeState) {
            if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
                    (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
                    ((device & mSafeMediaVolumeDevices) != 0) &&
                    (index > mSafeMediaVolumeIndex)) {
                return false;
            }
            return true;
        }
    }

    // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
    // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
    // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
    private int mMusicActiveMs;
    private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
    private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000;  // 1 minute polling interval

    private void onCheckMusicActive() {
        synchronized (mSafeMediaVolumeState) {
            if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
                int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);

                if ((device & mSafeMediaVolumeDevices) != 0) {
                    //循環消息,不停的統計音量超值時間,1分鍾統計一次
                    sendMsg(mAudioHandler,
                            MSG_CHECK_MUSIC_ACTIVE,
                            SENDMSG_REPLACE,
                            0,
                            0,
                            null,
                            MUSIC_ACTIVE_POLL_PERIOD_MS);
                    int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
                    if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
                            (index > mSafeMediaVolumeIndex)) {
                        // Approximate cumulative active music time
                        mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
                        if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
                            setSafeMediaVolumeEnabled(true);
                            mMusicActiveMs = 0;
                        }
                        saveMusicActiveMs();
                    }
                }
            }
        }
    }

 

 

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

    /** {@inheritDoc} */
    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            return 0;
        }

        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final boolean canceled = event.isCanceled();
        final int keyCode = event.getKeyCode();

        if(isAlarmBoot() && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN){
            return 0;
        }
        final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;

        // If screen is off then we treat the case where the keyguard is open but hidden
        // the same as if it were open and in front.
        // This will prevent any keys other than the power button from waking the screen
        // when the keyguard is hidden by another activity.
        final boolean keyguardActive = (mKeyguardDelegate == null ? false :
                                            (interactive ?
                                                mKeyguardDelegate.isShowingAndNotOccluded() :
                                                mKeyguardDelegate.isShowing()));

        if (DEBUG_INPUT) {
            Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                    + " interactive=" + interactive + " keyguardActive=" + keyguardActive
                    + " policyFlags=" + Integer.toHexString(policyFlags));
        }

        // Basic policy based on interactive state.
        int result;
        boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                || event.isWakeKey();
        if (interactive || (isInjected && !isWakeKey)) {
            // When the device is interactive or the key is injected pass the
            // key to the application.
            result = ACTION_PASS_TO_USER;
            isWakeKey = false;
        } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {
            // If we're currently dozing with the screen on and the keyguard showing, pass the key
            // to the application but preserve its wake key status to make sure we still move
            // from dozing to fully interactive if we would normally go from off to fully
            // interactive.
            result = ACTION_PASS_TO_USER;
        } else {
            // When the screen is off and the key is not injected, determine whether
            // to wake the device but don't pass the key to the application.
            result = 0;
            if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
                isWakeKey = false;
            }
        }

        // If the key would be handled globally, just return the result, don't worry about special
        // key processing.
        if (mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
            if (isWakeKey) {
                mPowerManager.wakeUp(event.getEventTime());
            }
            return result;
        }

        boolean useHapticFeedback = down
                && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
                && event.getRepeatCount() == 0;

        // Handle special keys.
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                    if (down) {
                        if (interactive && !mVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mVolumeDownKeyTriggered = true;
                            mVolumeDownKeyTime = event.getDownTime();
                            mVolumeDownKeyConsumedByScreenshotChord = false;
                            cancelPendingPowerKeyAction();
                            interceptScreenshotChord();
                        }
                    } else {
                        mVolumeDownKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                    }
                } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                    if (down) {
                        if (interactive && !mVolumeUpKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mVolumeUpKeyTriggered = true;
                            cancelPendingPowerKeyAction();
                            cancelPendingScreenshotChordAction();
                        }
                    } else {
                        mVolumeUpKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                    }
                }
                if (down) {
                    TelecomManager telecomManager = getTelecommService();
                    if (telecomManager != null) {
                        if (telecomManager.isRinging()) {
                            // If an incoming call is ringing, either VOLUME key means
                            // "silence ringer".  We handle these keys here, rather than
                            // in the InCallScreen, to make sure we'll respond to them
                            // even if the InCallScreen hasn't come to the foreground yet.
                            // Look for the DOWN event here, to agree with the "fallback"
                            // behavior in the InCallScreen.
                            Log.i(TAG, "interceptKeyBeforeQueueing:"
                                  + " VOLUME key-down while ringing: Silence ringer!");

                            // Silence the ringer.  (It's safe to call this
                            // even if the ringer has already been silenced.)
                            telecomManager.silenceRinger();

                            // And *don't* pass this key thru to the current activity
                            // (which is probably the InCallScreen.)
                            result &= ~ACTION_PASS_TO_USER;
                            break;
                        }
                        if (telecomManager.isInCall()
                                && (result & ACTION_PASS_TO_USER) == 0) {
                            // If we are in call but we decided not to pass the key to
                            // the application, just pass it to the session service.

                            MediaSessionLegacyHelper.getHelper(mContext)
                                    .sendVolumeKeyEvent(event, false);
                            break;
                        }
                    }

                    if ((result & ACTION_PASS_TO_USER) == 0) {
                        // If we aren't passing to the user and no one else
                        // handled it send it to the session manager to figure
                        // out.
                        MediaSessionLegacyHelper.getHelper(mContext)
                                .sendVolumeKeyEvent(event, true);
                        break;
                    }
                }
                break;
            }

            case KeyEvent.KEYCODE_ENDCALL:
            case KeyEvent.KEYCODE_POWER:
            case KeyEvent.KEYCODE_SLEEP:
            case KeyEvent.KEYCODE_MEDIA_PLAY:
            case KeyEvent.KEYCODE_MEDIA_PAUSE:
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            case KeyEvent.KEYCODE_HEADSETHOOK:
            case KeyEvent.KEYCODE_MUTE:
            case KeyEvent.KEYCODE_MEDIA_STOP:
            case KeyEvent.KEYCODE_MEDIA_NEXT:
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            case KeyEvent.KEYCODE_MEDIA_REWIND:
            case KeyEvent.KEYCODE_MEDIA_RECORD:
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
            case KeyEvent.KEYCODE_CALL:
            case KeyEvent.KEYCODE_BACK:
        return result;
    }

frameworks/base/media/java/android/media/session/MediaSessionLegacyHelper.java public void sendVolumeKeyEvent(KeyEvent keyEvent, boolean musicOnly) { if (keyEvent == null) { Log.w(TAG, "Tried to send a null key event. Ignoring."); return; } boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; int direction = 0; switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_VOLUME_UP: direction = AudioManager.ADJUST_RAISE; break; case KeyEvent.KEYCODE_VOLUME_DOWN: direction = AudioManager.ADJUST_LOWER; break; case KeyEvent.KEYCODE_VOLUME_MUTE: // TODO break; } if ((down || up) && direction != 0) { int flags; // If this is action up we want to send a beep for non-music events if (up) { direction = 0; } if (musicOnly) { // This flag is used when the screen is off to only affect // active media flags = AudioManager.FLAG_ACTIVE_MEDIA_ONLY; } else { // These flags are consistent with the home screen if (up) { flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; } else { flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE; } } mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags); } } frameworks/base/media/java/android/media/session/MediaSessionManager.java /** * Dispatch an adjust volume request to the system. It will be sent to the * most relevant audio stream or media session. The direction must be one of * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, * {@link AudioManager#ADJUST_SAME}. * * @param suggestedStream The stream to fall back to if there isn't a * relevant stream * @param direction The direction to adjust volume in. * @param flags Any flags to include with the volume change. * @hide */ public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) { try { mService.dispatchAdjustVolume(suggestedStream, direction, flags); } catch (RemoteException e) { Log.e(TAG, "Failed to send adjust volume.", e); } } frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java @Override public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { MediaSessionRecord session = mPriorityStack .getDefaultVolumeSession(mCurrentUserId); dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session); } } finally { Binder.restoreCallingIdentity(token); } } private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags, MediaSessionRecord session) { if (DEBUG) { String description = session == null ? null : session.toString(); Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags=" + flags + ", suggestedStream=" + suggestedStream); } if (session == null) { if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { if (DEBUG) { Log.d(TAG, "No active session to adjust, skipping media only volume event"); } return; } try { mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags, getContext().getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error adjusting default volume.", e); } } else { session.adjustVolume(direction, flags, getContext().getPackageName(), UserHandle.myUserId(), true); if (session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE && mRvc != null) { try { mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); } catch (Exception e) { Log.wtf(TAG, "Error sending volume change to system UI.", e); } } } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM