在上一篇文章中介绍了Android N的铃声保存问题,这一篇文章介绍的为Android N的铃声读取流程,以手机响铃过程作介绍,当手机判断来电时会调用packages\services\Telecomm\src\com\android\server\telecom\Ringer.java的startRinging方法:
public void startRinging(Call foregroundCall) {
if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
return;
}
if (foregroundCall == null) {
/// M: ALPS02801645 @{
// Consider case: 1A+1W, airplane mode on => receive disconnect of active call first.
// => audio change to ringing. When we come here, W has been removed in another thread.
// Original code:
// Log.wtf(this, "startRinging called with null foreground call.");
Log.d(this, "startRinging called with null foreground call.");
/// @}
return;
}
if (mInCallController.doesConnectedDialerSupportRinging()) {
Log.event(foregroundCall, Log.Events.SKIP_RINGING);
return;
}
stopCallWaiting();
if (!shouldRingForContact(foregroundCall.getContactUri())) {
return;
}
AudioManager audioManager =
(AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
mRingingCall = foregroundCall;
Log.event(foregroundCall, Log.Events.START_RINGER);
// Because we wait until a contact info query to complete before processing a
// call (for the purposes of direct-to-voicemail), the information about custom
// ringtones should be available by the time this code executes. We can safely
// request the custom ringtone from the call and expect it to be current.
mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
} else {
Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
}
if (shouldVibrate(mContext) && !mIsVibrating) {
mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
VIBRATION_ATTRIBUTES);
mIsVibrating = true;
}
}
而真正响铃的代码为mRingtonePlayer.play(mRingtoneFactory, foregroundCall);接着进入
packages\services\Telecomm\src\com\android\server\telecom\AsyncRingtonePlayer.java的play方法
public void play(RingtoneFactory factory, Call incomingCall) {
Log.d(this, "Posting play.");
SomeArgs args = SomeArgs.obtain();
args.arg1 = factory;
args.arg2 = incomingCall;
postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
}
由值EVENT_PLAY传递过去最终进入的方法为:
private void handlePlay(SomeArgs args) {
RingtoneFactory factory = (RingtoneFactory) args.arg1;
Call incomingCall = (Call) args.arg2;
args.recycle();
// don't bother with any of this if there is an EVENT_STOP waiting.
if (mHandler.hasMessages(EVENT_STOP)) {
return;
}
// If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. Do not play
// anything.
if(Uri.EMPTY.equals(incomingCall.getRingtone())) {
mRingtone = null;
return;
}
ThreadUtil.checkNotOnMainThread();
Log.i(this, "Play ringtone.");
if (mRingtone == null) {
mRingtone = factory.getRingtone(incomingCall);
if (mRingtone == null) {
Uri ringtoneUri = incomingCall.getRingtone();
String ringtoneUriString = (ringtoneUri == null) ? "null" :
ringtoneUri.toSafeString();
Log.event(null, Log.Events.ERROR_LOG, "Failed to get ringtone from factory. " +
"Skipping ringing. Uri was: " + ringtoneUriString);
return;
}
}
handleRepeat();
}
获取铃声为mRingtone = factory.getRingtone(incomingCall);此行代码进入
packages\services\Telecomm\src\com\android\server\telecom\RingtoneFactory.java的
public Ringtone getRingtone(Call incomingCall) {
// Use the default ringtone of the work profile if the contact is a work profile contact.
Context userContext = isWorkContact(incomingCall) ?
getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
getContextForUserHandle(mCallsManager.getCurrentUserHandle());
Uri ringtoneUri = incomingCall.getRingtone();
Ringtone ringtone = null;
if(ringtoneUri != null && userContext != null) {
// Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
ringtone = RingtoneManager.getRingtone(userContext, ringtoneUri);
}
if(ringtone == null) {
// Contact didn't specify ringtone or custom Ringtone creation failed. Get default
// ringtone for user or profile.
ringtone = RingtoneManager.getRingtone(
hasDefaultRingtoneForUser(userContext) ? userContext : mContext,
Settings.System.DEFAULT_RINGTONE_URI);
}
if (ringtone != null) {
ringtone.setStreamType(AudioManager.STREAM_RING);
}
return ringtone;
}
一般Uri ringtoneUri = incomingCall.getRingtone();中获取的Uri ringtoneUri 为null,所以进入
ringtone = RingtoneManager.getRingtone(
hasDefaultRingtoneForUser(userContext) ? userContext : mContext,
Settings.System.DEFAULT_RINGTONE_URI);
而在frameworks\base\media\java\android\media\RingtoneManager.java中
private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
Log.d(TAG, "getRingtone() ringtoneUri = " + ringtoneUri
+ ", streamType = " + streamType);
try {
final Ringtone r = new Ringtone(context, true);
if (streamType >= 0) {
r.setStreamType(streamType);
}
r.setUri(ringtoneUri);
return r;
} catch (Exception ex) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
}
return null;
}
真正设置铃声的为r.setUri(ringtoneUri);进入到frameworks\base\media\java\android\media\Ringtone.java的setUri方法:
public void setUri(Uri uri) {
destroyLocalPlayer();
mUri = uri;
if (mUri == null) {
return;
}
// TODO: detect READ_EXTERNAL and specific content provider case, instead of relying on throwing
// try opening uri locally before delegating to remote player
mLocalPlayer = new MediaPlayer();
try {
mLocalPlayer.setDataSource(mContext, mUri);
mLocalPlayer.setAudioAttributes(mAudioAttributes);
synchronized (mPlaybackSettingsLock) {
applyPlaybackProperties_sync();
}
mLocalPlayer.prepare();
} catch (SecurityException | IOException e) {
destroyLocalPlayer();
if (!mAllowRemote) {
Log.w(TAG, "Remote playback not allowed: " + e);
}
}
if (LOGD) {
if (mLocalPlayer != null) {
Log.d(TAG, "Successfully created local player");
} else {
Log.d(TAG, "Problem opening; delegating to remote player");
}
}
}
mLocalPlayer.setDataSource(mContext, mUri);此为设置铃声的响铃路径,进入frameworks\base\media\java\android\media\MediaPlayer.java的
setDataSource方法:
public void setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers) throws IOException, IllegalArgumentException,
SecurityException, IllegalStateException {
final ContentResolver resolver = context.getContentResolver();
final String scheme = uri.getScheme();
if (scheme == null) {
setDataSource(uri.toString());
return;
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
setDataSource(uri.getPath());
return;
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
&& Settings.AUTHORITY.equals(uri.getAuthority())) {
// Try cached ringtone first since the actual provider may not be
// encryption aware, or it may be stored on CE media storage
final int type = RingtoneManager.getDefaultType(uri);
final Uri cacheUri = RingtoneManager.getCacheForType(type);
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
if (attemptDataSource(resolver, cacheUri)) {
return;
} else if (attemptDataSource(resolver, actualUri)) {
return;
} else {
setDataSource(uri.toString(), headers);
}
} else {
// Try requested Uri locally first, or fallback to media server
if (attemptDataSource(resolver, uri)) {
return;
} else {
IOmaSettingHelper helper = MPlugin.createInstance(IOmaSettingHelper.class.getName(),
context);
if (helper != null) {
headers = helper.setSettingHeader(context, uri, headers);
} else {
Log.w(TAG, "IOmaSettingHelper plugin returns null, uses default headers");
}
setDataSource(uri.toString(), headers);
}
}
}
最后设置铃声的为
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
&& Settings.AUTHORITY.equals(uri.getAuthority())) {
// Try cached ringtone first since the actual provider may not be
// encryption aware, or it may be stored on CE media storage
final int type = RingtoneManager.getDefaultType(uri);
final Uri cacheUri = RingtoneManager.getCacheForType(type);
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
if (attemptDataSource(resolver, cacheUri)) {
return;
} else if (attemptDataSource(resolver, actualUri)) {
return;
} else {
setDataSource(uri.toString(), headers);
}
只有attemptDataSource(resolver, cacheUri)不成功,返回的为false,才会进入attemptDataSource(resolver, actualUri),
private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
setDataSource(afd);
return true;
} catch (NullPointerException | SecurityException | IOException ex) {
Log.w(TAG, "Couldn't open " + uri + ": " + ex);
return false;
}
}
而final Uri cacheUri = RingtoneManager.getCacheForType(type);获取的为保存在\
data\system_de\0\ringtones\目录下面手机铃声ringtone_cache文件里面的铃声,只有此设置失败了,才会设置attemptDataSource(resolver, actualUri)此路径的铃声,而
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);的铃声为此铃声的实际铃声路径,所以当设置SD卡音乐为铃声时当此音乐不存在还是会想起设置的音乐铃声,此为android N新的铃声机制。