天天看点

Android P对铃声的分析

现象

  • 从Android N开始会出现:

    设置SD卡里的音乐作为手机铃声后将SD卡取出,在设置下的铃声会显示为“unkonwn”,但来电时响铃的仍旧是SD卡的铃声。

  • 当前Android P在此问题的基础上出现:

    设置非系统的音乐作为铃声,然后进入文件管理器中对铃声进行删除或rename操作,在设置下的铃声会显示为数字

    以上现象在Android M中均会设为默认铃声

源码分析

铃声的保存

  1. 入口: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的获取

  1. 进入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);
}
           

铃声的读取流程

  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);
}
           
  1. 分析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卡音乐作为铃声时尽管音乐不存在但响铃依旧是之前的铃声。

继续阅读