Android系统情景模式下铃声/闹铃/其他声音以及震动屏蔽修改

情景模式说明:frameworks/base/media/java/android/media/AudioManager.java

  • RINGER_MODE_NORMAL:正常模式
  • 所有声音都正常,包括系统声音,来电响铃,媒体声音,闹钟,通知声音都有
  • RINGER_MODE_SILENT:静音模式
  • 该模式下,来电响铃,通知,系统声音和震动都没有;闹钟,通话声音保持;大部分手机媒体声音依然有,但是小米和少部分oppo手机在设置静音的同时会将媒体声音自动调整为0,此时没有媒体声音。
  • RINGER_MODE_VIBRATE:震动模式
  • 该模式下,来电,通知保持震动没有声音;但是媒体,闹钟依然有声音。不过小米手机只有正常模式和静音模式,没有震动模式

定制化需求:在静音或震动模式下,不允许闹铃或媒体播放声音或震动
声音屏蔽:
闹铃特殊屏蔽:packages/apps/DeskClock/src/com/android/deskclock/alarms/AlarmKlaxon.java

public static void start(Context context, AlarmInstance instance) {
        // Make sure we are stopped before starting
        stop(context);
        LogUtils.v("AlarmKlaxon.start()");
//声音屏蔽位置  最后选择在packages/apps/DeskClock/src/com/android/deskclock/AsyncRingtonePlayer.java 下屏蔽
        if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) {
            final long crescendoDuration = DataModel.getDataModel().getAlarmCrescendoDuration();
            getAsyncRingtonePlayer(context).play(instance.mRingtone, crescendoDuration);
        }
//闹铃震动屏蔽位置
        if (instance.mVibrate) {
            final Vibrator vibrator = getVibrator(context);
            if (Utils.isLOrLater()) {
                vibrateLOrLater(vibrator);
            } else {
                vibrator.vibrate(VIBRATE_PATTERN, 0);
            }
        }

        sStarted = true;
    }

  private class RingtonePlaybackDelegate implements PlaybackDelegate {

        /**
         * Starts the actual playback of the ringtone. Executes on ringtone-thread.
         */
        @Override
        public boolean play(Context context, Uri ringtoneUri, long crescendoDuration) {
            checkAsyncRingtonePlayerThread();
            mCrescendoDuration = crescendoDuration;

            LOGGER.i("Play ringtone via android.media.Ringtone.");

            if (mAudioManager == null) {
                mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            }

            final boolean inTelephoneCall = isInTelephoneCall(context);
            if (inTelephoneCall) {
                ringtoneUri = getInCallRingtoneUri(context);
            }
//情景模式判断,震动模式下不响铃声音
            int ringerMode = mAudioManager.getRingerMode();
            if(ringerMode == AudioManager.RINGER_MODE_VIBRATE){
                LOGGER.i("Play ringtone via model.");
                return false;                
            }
            // Attempt to fetch the specified ringtone.
            mRingtone = RingtoneManager.getRingtone(context, ringtoneUri);

            if (mRingtone == null) {
                // Fall back to the system default ringtone.
                ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
                mRingtone = RingtoneManager.getRingtone(context, ringtoneUri);
            }

声音全屏蔽:
无论铃声还是多媒体播放的音视频声音,最终都对调用audioservice中的getStreamVolume()来获取对应的音源音量大小值,通过返回0来达到屏蔽要求。
frameworks/base/services/core/java/com/android/server/audio/AudioService.java

本来准备找到多媒体的播放入口api直接进行音量屏蔽,可惜系统代码过于复杂没找到,有找到的大手子给下路径!!!

frameworks/base/services/core/java/com/android/server/audio/AudioService.java
/** @see AudioManager#getStreamVolume(int) */
    public int getStreamVolume(int streamType) {
        ensureValidStreamType(streamType);
        int device = getDeviceForStream(streamType);
        synchronized (VolumeStreamState.class) {
            int index = mStreamStates[streamType].getIndex(device);

            // by convention getStreamVolume() returns 0 when a stream is muted.
            if (mStreamStates[streamType].mIsMuted) {
                index = 0;
            }
            if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
                    isFixedVolumeDevice(device)) {
                index = mStreamStates[streamType].getMaxIndex();
            }
//这里显示判断音源 streamType,然后在判断情景模式,多条件过滤返回值0
//*****注意****这里有一个隐藏的bug,当使用这段屏蔽代码后,在settings中进行音量设置大小,会出现卡顿乃至settings奔溃的现象,
原因是由于县城锁没有释放导致anr,这里的解决方式,在settings设置音量时直接return,详见下面 截取代码setStreamVolume()方法***
            if(streamType == AudioSystem.STREAM_ALARM || streamType == AudioManager.STREAM_MUSIC){
                if(getRingerModeExternal() == AudioManager.RINGER_MODE_SILENT ){
                    return 0;
                }
            }
            return (index + 5) / 10;
        }
    }

private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
            String caller, int uid, boolean hasModifyAudioSettings) {
        if (DEBUG_VOL) {
            Log.d(TAG, "setStreamVolume(stream=" + streamType+", index=" + index
                    + ", calling=" + callingPackage + ")");
        }

        if (mUseFixedVolume) {
            return;
        }

        if(streamType == AudioSystem.STREAM_ALARM || streamType == AudioSystem.STREAM_MUSIC){
            if(getRingerModeExternal() == AudioManager.RINGER_MODE_SILENT  ){
                return;
            }
        }

屏蔽震动:
对于铃声震动上面已有说明,下面是全局震动屏蔽frameworks/base/core/java/android/os/SystemVibrator.java

 @Override
    public void vibrate(int uid, String opPkg, VibrationEffect effect,
            String reason, AudioAttributes attributes) {
        if (mService == null) {
            Log.w(TAG, "Failed to vibrate; no vibrator service.");
            return;
        }
        try {
//添加情景模式屏蔽条件,直接return
            if(mContext!=null){
                AudioManager am=(AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
                if(am.getRingerMode() == AudioManager.RINGER_MODE_SILENT  ){
                    
                    return;
                } 
            }

            if (attributes == null) {
                attributes = new AudioAttributes.Builder().build();
            }
            VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
            mService.vibrate(uid, opPkg, effect, atr, reason, mToken);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to vibrate.", e);
        }
    }

以上就是关于系统声音和震动的屏蔽一些思路和方法

// 来电震动
        Settings.System.putInt( getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, isVibrate?1:0);
        // 触摸振动
        Settings.System.putInt( getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, isVibrate?1:0);

bug解决

/frameworks/base/media/java/android/media/MediaPlayer.java

public void start() throws IllegalStateException {
        //FIXME use lambda to pass startImpl to superclass
        final int delay = getStartDelayMs();
        int modeType=getRingerMode();
        int streamType=getAudioStreamType();
  
        if(streamType == AudioManager.STREAM_ALARM ){
            if(modeType == AudioManager.RINGER_MODE_SILENT){  
                setVolume(0);
            }
        }
        if (delay == 0) {
            startImpl();
        } else {
            new Thread() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    baseSetStartDelayMs(0);
                    try {
                        startImpl();
                    } catch (IllegalStateException e) {
                        // fail silently for a state exception when it is happening after
                        // a delayed start, as the player state could have changed between the
                        // call to start() and the execution of startImpl()
                    }
                }
            }.start();
        }
    }

    public int getRingerMode() {
        try {
            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
            final IAudioService service = IAudioService.Stub.asInterface(b);
            return service.getRingerModeExternal();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值