Android N的铃声问题(2)

       在上一篇文章中介绍了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新的铃声机制。




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值