现象
-
从Android N开始会出现:
设置SD卡里的音乐作为手机铃声后将SD卡取出,在设置下的铃声会显示为“unkonwn”,但来电时响铃的仍旧是SD卡的铃声。
-
当前Android P在此问题的基础上出现:
设置非系统的音乐作为铃声,然后进入文件管理器中对铃声进行删除或rename操作,在设置下的铃声会显示为数字
以上现象在Android M中均会设为默认铃声
源码分析
铃声的保存
- 入口:MtkSettings/src/com/android/settings/DefaultRingtonePreference.java 中
@Override
protected void onSaveRingtone(Uri ringtoneUri) {
RingtoneManager.setActualDefaultRingtoneUri(mUserContext, getRingtoneType(), ringtoneUri);
}
- getRingtoneType()获取铃声的类型(手机铃声、通知铃声、闹钟铃声)
- ringtoneUri为铃声的URI值
在MtkSettings/src/com/android/settings/notification/RingtonePreferenceControllerBase.java中有ringtoneUri的获取
- 进入frameworks\base\media\java\android\media\RingtoneManager.java的setActualDefaultRingtoneUri()
/**
* Sets the {@link Uri} of the default sound for a given sound type.
*
* @param context A context used for querying.
* @param type The type whose default sound should be set. One of
* {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
* {@link #TYPE_ALARM}.
* @param ringtoneUri A {@link Uri} pointing to the default sound to set.
* @see #getActualDefaultRingtoneUri(Context, int)
*/
public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
String setting = getSettingForType(type);
if (setting == null) return;
final ContentResolver resolver = context.getContentResolver();
if (Settings.Secure.getIntForUser(resolver, Settings.Secure.SYNC_PARENT_SOUNDS, 0,
context.getUserId()) == 1) {
// Parent sound override is enabled. Disable it using the audio service.
disableSyncFromParent(context);
}
if(!isInternalRingtoneUri(ringtoneUri)) {
ringtoneUri = ContentProvider.maybeAddUserId(ringtoneUri, context.getUserId());
}
//保存此铃声在铃声数据库的uri
Settings.System.putStringForUser(resolver, setting,
ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId());
// Stream selected ringtone into cache so it's available for playback
// when CE storage is still locked
//将当前的铃声copy下来保存到/data/system_de/0/ringtones/目录下.
//手机铃声则保存到ringtone_cache文件下
//通知铃声则是notification_sound_cache
//闹钟铃声则是alarm_alert_cache
if (ringtoneUri != null) {
final Uri cacheUri = getCacheForType(type, context.getUserId());
try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = resolver.openOutputStream(cacheUri)) {
FileUtils.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
}
}
}
3.通过搜索setActualDefaultRingtoneUri方法在frameworks\base\media\java\android\media\MediaScanner.java 中使用
进入frameworks\base\media\java\android\media\MediaScanner.java,其中含有setDefaultRingtoneFileNames读取默认设置的值
private void setDefaultRingtoneFileNames() {
mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.RINGTONE);
mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.NOTIFICATION_SOUND);
mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.ALARM_ALERT);
// #159395 add multi sim ringtone qianfeifei 20181224 @{
mDefaultRingtone2Filename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.RINGTONE2);
// #159395 @}
android.util.Log.i("MediaScanner","qianfeifei setDefaultRingtoneFileNames---> mDefaultRingtoneFilename= "+mDefaultRingtoneFilename+
" mDefaultRingtone2Filename= "+mDefaultRingtone2Filename);
}
方法setRingtoneIfNotSet保存默认设置的铃声
private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
if (wasRingtoneAlreadySet(settingName)) {
return;
}
ContentResolver cr = mContext.getContentResolver();
String existingSettingValue = Settings.System.getString(cr, settingName);
android.util.Log.i("MediaScanner","qianfeifei setRingtoneIfNotSet---> existingSettingValue= "+existingSettingValue);
if (TextUtils.isEmpty(existingSettingValue)) {
final Uri settingUri = Settings.System.getUriFor(settingName);
final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
android.util.Log.i("MediaScanner","qianfeifei setRingtoneIfNotSet---> settingUri= "+settingUri
+" ringtoneUri= "+ringtoneUri);
RingtoneManager.setActualDefaultRingtoneUri(mContext,
RingtoneManager.getDefaultType(settingUri), ringtoneUri);
}
Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
}
铃声的读取流程
- 当手机来电时会调用Telecomm\src\com\android\server\telecom\Ringer.java的startRinging方法
//当手机判断来电时响铃
public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
if (foregroundCall == null) {
/// M: ALPS03787956 Fix wtf log warning. @{
/// Hand up the call immediately when ringing. Then the foreground call will
/// change to null, but call audio is start ringing at the same time.
/// Log.wtf will occur in this case.
/// Solution:
/// Call audio can handle this case, so change Log.wtf to Log.i here.
Log.i(this, "startRinging called with null foreground call.");
/// @}
return false;
}
AudioManager audioManager =
(AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
boolean isSelfManaged = foregroundCall.isSelfManaged();
boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
boolean hasExternalRinger = hasExternalRinger(foregroundCall);
// Acquire audio focus under any of the following conditions:
// 1. Should ring for contact and there's an HFP device attached
// 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
// present.
// 3. The call is self-managed.
boolean shouldAcquireAudioFocus =
isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
// Don't do call waiting operations or vibration unless these are false.
boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
hasExternalRinger;
if (endEarly) {
if (letDialerHandleRinging) {
Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
}
Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
"isSelfManaged=%s, hasExternalRinger=%s", isTheaterModeOn,
letDialerHandleRinging, isSelfManaged, hasExternalRinger);
return shouldAcquireAudioFocus;
}
stopCallWaiting();
VibrationEffect effect;
if (isRingerAudible) {
mRingingCall = foregroundCall;
Log.addEvent(foregroundCall, LogUtils.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);
effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
} else {
Log.i(this, "startRinging: skipping because ringer would not be audible. " +
"isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
isVolumeOverZero, shouldRingForContact, isRingtonePresent);
effect = mDefaultVibrationEffect;
}
if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
mIsVibrating = true;
} else if (mIsVibrating) {
Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
}
return shouldAcquireAudioFocus;
}
其中一下进入了响铃的实现
if (isRingerAudible) {
mRingingCall = foregroundCall;
Log.addEvent(foregroundCall, LogUtils.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);
effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
}
- 分析Telecomm\src\com\android\server\telecom\AsyncRingtonePlayer.java的play方法
/** Plays the ringtone. */
//响铃的实现
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消息,进入
/**
* Starts the actual playback of the ringtone. Executes on ringtone-thread.
*/
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.
android.util.Log.i("AsyncRingtonePlayer","qianfeifei incomingCall.getRingtone()= "+incomingCall.getRingtone());
if(Uri.EMPTY.equals(incomingCall.getRingtone())) {
mRingtone = null;
return;
}
ThreadUtil.checkNotOnMainThread();
Log.i(this, "Play ringtone.");
android.util.Log.i("AsyncRingtonePlayer","qianfeifei mRingtone= "+mRingtone);
if (mRingtone == null) {
//获取铃声
mRingtone = factory.getRingtone(incomingCall);
android.util.Log.i("AsyncRingtonePlayer","qianfeifei factory.getRingtone(incomingCall)= "+factory.getRingtone(incomingCall));
if (mRingtone == null) {
Uri ringtoneUri = incomingCall.getRingtone();
String ringtoneUriString = (ringtoneUri == null) ? "null" :
ringtoneUri.toSafeString();
android.util.Log.i("AsyncRingtonePlayer","qianfeifei ringtoneUriString= "+ringtoneUriString);
Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " +
"factory. Skipping ringing. Uri was: " + ringtoneUriString);
return;
}
}
/// M: @{ Fix Ringtone conflict with line ALPS03524486
/// Using post delay to play ringtone.
//handleRepeat();
SomeArgs args1 = SomeArgs.obtain();
args1.arg1 = POST_PLAY_RINGER_MILLIS;
postMessage(EVENT_REPEAT, true /* shouldCreateHandler */, args1);
/// @}
}
mRingtone = factory.getRingtone(incomingCall);获取铃声,进入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为null
Uri ringtoneUri = incomingCall.getRingtone();
android.util.Log.i("RingtoneFactory","qianfeifei ringtoneUri= "+ringtoneUri);
Ringtone ringtone = null;
if(ringtoneUri != null && userContext != null) {
// Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
//进入RingtoneManager的getRingtone中获取铃声
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.
Context contextToUse = hasDefaultRingtoneForUser(userContext) ? userContext : mContext;
Uri defaultRingtoneUri;
// #159395 add multi sim ringtone qianfeifei 20181224 @{
int slotId = SubscriptionManager.getSlotIndex(incomingCall.getSubsciptionId());
if (SystemProperties.getBoolean("ro.sr.multisim_ringtone", false) && slotId == 1) {
if (UserManager.get(contextToUse).isUserUnlocked(contextToUse.getUserId())) {
defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(contextToUse,
RingtoneManager.TYPE_RINGTONE2);
} else {
defaultRingtoneUri = Settings.System.DEFAULT_RINGTONE2_URI;
}
//进入RingtoneManager的getRingtone中获取铃声
android.util.Log.i("RingtoneFactory","qianfeifei defaultRingtoneUri222= "+defaultRingtoneUri);
ringtone = RingtoneManager.getRingtone(contextToUse, defaultRingtoneUri);
} else {
if (UserManager.get(contextToUse).isUserUnlocked(contextToUse.getUserId())) {
defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(contextToUse,
RingtoneManager.TYPE_RINGTONE);
} else {
defaultRingtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
}
if (defaultRingtoneUri == null) {
return null;
}
android.util.Log.i("RingtoneFactory","qianfeifei defaultRingtoneUri111= "+defaultRingtoneUri);
ringtone = RingtoneManager.getRingtone(contextToUse, defaultRingtoneUri);
}
// #159395 @}
}
if (ringtone != null) {
ringtone.setStreamType(AudioManager.STREAM_RING);
}
return ringtone;
}
ringtone = RingtoneManager.getRingtone(userContext, ringtoneUri);进入RingtoneManager获取铃声
/**
* Returns a {@link Ringtone} for a given sound URI on the given stream
* type. Normally, if you change the stream type on the returned
* {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
* an optimized route to avoid that.
*
* @param streamType The stream type for the ringtone, or -1 if it should
* not be set (and the default used instead).
* @see #getRingtone(Context, Uri)
*/
private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
try {
final Ringtone r = new Ringtone(context, true);
if (streamType >= 0) {
//FIXME deprecated call
r.setStreamType(streamType);
}
//实现铃声的设置
android.util.Log.i("RingtoneManager","qianfeifei ringtoneUri= "+ringtoneUri);
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方法
/**
* Set {@link Uri} to be used for ringtone playback. Attempts to open
* locally, otherwise will delegate playback to remote
* {@link IRingtonePlayer}.
*
* @hide
*/
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方法
/**
* Sets the data source as a content Uri.
*
* To provide cookies for the subsequent HTTP requests, you can install your own default cookie
* handler and use other variants of setDataSource APIs instead. Alternatively, you can use
* this API to pass the cookies as a list of HttpCookie. If the app has not installed
* a CookieHandler already, this API creates a CookieManager and populates its CookieStore with
* the provided cookies. If the app has installed its own handler already, this API requires the
* handler to be of CookieManager type such that the API can update the manager’s CookieStore.
*
* <p><strong>Note</strong> that the cross domain redirection is allowed by default,
* but that can be changed with key/value pairs through the headers parameter with
* "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
* disallow or allow cross domain redirection.
*
* @param context the Context to use when resolving the Uri
* @param uri the Content URI of the data you want to play
* @param headers the headers to be sent together with the request for the data
* The headers must not include cookies. Instead, use the cookies param.
* @param cookies the cookies to be sent together with the request
* @throws IllegalArgumentException if cookies are provided and the installed handler is not
* a CookieManager
* @throws IllegalStateException if it is called in an invalid state
* @throws NullPointerException if context or uri is null
* @throws IOException if uri has a file scheme and an I/O error occurs
*/
public void setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
throws IOException {
if (context == null) {
throw new NullPointerException("context param can not be null.");
}
if (uri == null) {
throw new NullPointerException("uri param can not be null.");
}
if (cookies != null) {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
throw new IllegalArgumentException("The cookie handler has to be of CookieManager "
+ "type when cookies are provided.");
}
}
// The context and URI usually belong to the calling user. Get a resolver for that user
// and strip out the userId from the URI if present.
final ContentResolver resolver = context.getContentResolver();
final String scheme = uri.getScheme();
final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
android.util.Log.i("MediaPLayer","qianfeifei scheme= "+scheme+" authority= "+authority);
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
setDataSource(uri.getPath());
return;
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
&& Settings.AUTHORITY.equals(authority)) {
// 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);
//缓存的uri
//获取的是保存在\data\system_de\0\ringtones\目录下的手机铃声ringtone_cache文件里的铃声
final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
android.util.Log.i("MediaPLayer","qianfeifei type= "+type+" cacheUri= "+cacheUri);
//qianfeifei type= 1 cacheUri= content://[email protected]/system/ringtone_cache
//qianfeifei actualUri= content://media/external/audio/media/50 type= 1
//该铃声的实际路径,SD卡的音乐设为铃声音乐不存在仍会起作用
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
android.util.Log.i("MediaPLayer","qianfeifei actualUri= "+actualUri+" type= "+type);
if (attemptDataSource(resolver, cacheUri)) {
android.util.Log.i("MediaPLayer","qianfeifei attemptDataSource cacheuri");
return;
} else if (attemptDataSource(resolver, actualUri)) {
android.util.Log.i("MediaPLayer","qianfeifei attemptDataSource actualUri");
return;
} else {
setDataSource(uri.toString(), headers, cookies);
android.util.Log.i("MediaPLayer","qianfeifei attemptDataSource cookies uri.toString()=" + uri.toString());
}
} else {
// Try requested Uri locally first, or fallback to media server
if (attemptDataSource(resolver, uri)) {
return;
} else {
setDataSource(uri.toString(), headers, cookies);
}
}
}
其中
final int type = RingtoneManager.getDefaultType(uri);
//缓存的uri
//获取的是保存在\data\system_de\0\ringtones\目录下的手机铃声ringtone_cache文件里的铃声
final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
android.util.Log.i("MediaPLayer","qianfeifei type= "+type+" cacheUri= "+cacheUri);
//qianfeifei type= 1 cacheUri= content://[email protected]/system/ringtone_cache
//qianfeifei actualUri= content://media/external/audio/media/50 type= 1
//该铃声的实际路径,SD卡的音乐设为铃声音乐不存在仍会起作用
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
android.util.Log.i("MediaPLayer","qianfeifei actualUri= "+actualUri+" type= "+type);
if (attemptDataSource(resolver, cacheUri)) {
android.util.Log.i("MediaPLayer","qianfeifei attemptDataSource cacheuri");
return;
} else if (attemptDataSource(resolver, actualUri)) {
android.util.Log.i("MediaPLayer","qianfeifei attemptDataSource actualUri");
return;
} else {
setDataSource(uri.toString(), headers, cookies);
android.util.Log.i("MediaPLayer","qianfeifei attemptDataSource cookies uri.toString()=" + uri.toString());
}
通过log
01-30 06:53:03.986 5634 8157 I MediaScanner: qianfeifei setDefaultRingtoneFileNames---> mDefaultRingtoneFilename= Ring_Synth_04.ogg mDefaultRingtone2Filename= Ring_Synth_02.ogg
01-30 06:53:04.069 5634 5682 I MediaScanner: qianfeifei setDefaultRingtoneFileNames---> mDefaultRingtoneFilename= Ring_Synth_04.ogg mDefaultRingtone2Filename= Ring_Synth_02.ogg
01-30 06:53:05.904 7903 8160 I RingtoneManager: qianfeifei ringtoneUri= content://settings/system/alarm_alert
01-30 06:53:05.916 7903 8160 I MediaPLayer: qianfeifei scheme= content authority= settings
01-30 06:53:05.917 7903 8160 I MediaPLayer: qianfeifei type= 4 cacheUri= content://[email protected]/system/alarm_alert_cache
01-30 06:53:05.920 7903 8160 I MediaPLayer: qianfeifei actualUri= content://media/internal/audio/media/8 type= 4
01-30 06:53:05.924 7903 8161 I RingtoneManager: qianfeifei ringtoneUri= android.resource://com.android.settings/2131820556
01-30 06:53:05.948 7903 8161 I MediaPLayer: qianfeifei scheme= android.resource authority= com.android.settings
01-30 06:53:05.958 7903 8162 I RingtoneManager: qianfeifei ringtoneUri= content://settings/system/ringtone
01-30 06:53:05.973 7903 8162 I MediaPLayer: qianfeifei scheme= content authority= settings
01-30 06:53:05.973 7903 8162 I MediaPLayer: qianfeifei type= 1 cacheUri= content://[email protected]/system/ringtone_cache
01-30 06:53:05.977 7903 8162 I MediaPLayer: qianfeifei actualUri= content://media/external/audio/media/47 type= 1
01-30 06:53:06.001 7903 8162 I MediaPLayer: qianfeifei attemptDataSource cacheuri
01-30 06:53:06.012 7903 8168 I RingtoneManager: qianfeifei ringtoneUri= content://settings/system/alarm_alert
01-30 06:53:06.035 7903 8168 I MediaPLayer: qianfeifei scheme= content authority= settings
01-30 06:53:06.035 7903 8168 I MediaPLayer: qianfeifei type= 4 cacheUri= content://[email protected]/system/alarm_alert_cache
01-30 06:53:06.036 7903 8168 I MediaPLayer: qianfeifei actualUri= content://media/internal/audio/media/8 type= 4
01-30 06:53:06.048 7903 7903 I Ringtone: qianfeifei uri= content://[email protected]/system/ringtone_cache followSettingsUri= false allowRemote= true
01-30 06:53:06.049 7903 7903 I Ringtone: qianfeifei title111= Unknown
01-30 06:53:06.049 7903 7903 I qianfeifei111: cacheUri= content://[email protected]/system/ringtone_cache,cacheSummary= Unknown
01-30 06:53:06.049 7903 7903 I Ringtone: qianfeifei uri= content://media/external/audio/media/47 followSettingsUri= false allowRemote= true
01-30 06:53:06.060 7903 8160 I MediaPLayer: qianfeifei attemptDataSource actualUri
01-30 06:53:06.061 7903 7903 I Ringtone: qianfeifei cursor.getString(2)= 接受
01-30 06:53:06.068 7903 7903 I Ringtone: qianfeifei uri= content://[email protected]/system/ringtone2_cache followSettingsUri= false allowRemote= true
01-30 06:53:06.069 7903 7903 I Ringtone: qianfeifei title111= Unknown
01-30 06:53:06.069 7903 7903 I qianfeifei111: cacheUri= content://[email protected]/system/ringtone2_cache,cacheSummary= Unknown
01-30 06:53:06.069 7903 7903 I Ringtone: qianfeifei uri= content://media/internal/audio/media/28 followSettingsUri= false allowRemote= true
01-30 06:53:06.080 7903 7903 I Ringtone: qianfeifei cursor.getString(2)= Chimey Phone
01-30 06:53:06.087 7903 7903 I Ringtone: qianfeifei uri= content://[email protected]/system/notification_sound_cache followSettingsUri= false allowRemote= true
01-30 06:53:06.088 7903 7903 I Ringtone: qianfeifei title111= Unknown
01-30 06:53:06.088 7903 7903 I qianfeifei111: cacheUri= content://[email protected]/system/notification_sound_cache,cacheSummary= Unknown
01-30 06:53:06.088 7903 7903 I Ringtone: qianfeifei uri= content://media/internal/audio/media/33 followSettingsUri= false allowRemote= true
01-30 06:53:06.094 7903 7903 I Ringtone: qianfeifei cursor.getString(2)= Alya
01-30 06:53:06.104 7903 8168 I MediaPLayer: qianfeifei attemptDataSource actualUri
01-30 06:53:06.110 7903 7903 I Ringtone: qianfeifei uri= content://0@settings/system/alarm_alert_cache followSettingsUri= false allowRemote= true
01-30 06:53:06.110 7903 7903 I Ringtone: qianfeifei title111= Unknown
01-30 06:53:06.110 7903 7903 I qianfeifei111: cacheUri= content://[email protected]/system/alarm_alert_cache,cacheSummary= Unknown
01-30 06:53:06.110 7903 7903 I Ringtone: qianfeifei uri= content://media/internal/audio/media/8 followSettingsUri= false allowRemote= true
01-30 06:53:06.123 7903 8161 I MediaPLayer: qianfeifei scheme= android.resource authority= com.android.settings
01-30 06:53:06.133 7903 7903 I Ringtone: qianfeifei cursor.getString(2)= Cesium
01-30 06:53:06.164 7903 8162 I MediaPLayer: qianfeifei scheme= content authority= settings
01-30 06:53:06.171 7903 8162 I MediaPLayer: qianfeifei type= 1 cacheUri= content://[email protected]/system/ringtone_cache
01-30 06:53:06.171 7903 8162 I MediaPLayer: qianfeifei actualUri= content://media/external/audio/media/47 type= 1
01-30 06:53:06.177 7903 8160 I MediaPLayer: qianfeifei scheme= content authority= settings
01-30 06:53:06.177 7903 8160 I MediaPLayer: qianfeifei type= 4 cacheUri= content://[email protected]/system/alarm_alert_cache
01-30 06:53:06.177 7903 8160 I MediaPLayer: qianfeifei actualUri= content://media/internal/audio/media/8 type= 4
01-30 06:53:06.209 7903 8162 I MediaPLayer: qianfeifei attemptDataSource cacheuri
01-30 06:53:06.262 7903 8160 I MediaPLayer: qianfeifei attemptDataSource actualUri
01-30 06:53:06.302 7903 8168 I MediaPLayer: qianfeifei scheme= content authority= settings
01-30 06:53:06.303 7903 8168 I MediaPLayer: qianfeifei type= 4 cacheUri= content://[email protected]/system/alarm_alert_cache
01-30 06:53:06.307 7903 8168 I MediaPLayer: qianfeifei actualUri= content://media/internal/audio/media/8 type= 4
01-30 06:53:06.380 7903 8168 I MediaPLayer: qianfeifei attemptDataSource actualUri
所有的铃声均会在缓存中做备份,保存的标志为mediaId
以上判断条件中只有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, context.getUserId())获取的为保存在\data\system_de\0\ringtones\目录下手机铃声ringtone_cache文件里面的铃声,只有此处设置失败,才会执行attemptDataSource(resolver, actualUri)此路径下的铃声。
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);的铃声获取的是非缓存区的实际铃声路径,所以当设置SD卡音乐作为铃声时尽管音乐不存在但响铃依旧是之前的铃声。