情景模式说明: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();
}
}