记一次 Android 系统铃声不响的解决过程

记一次 Android 系统铃声不响的解决过程

给主力机Phoenix刷入最新的EvolutionX系统(20210214版本)之后,使用了一两天,看起来一切正常,直到我发现自己漏接了3个电话。。。(连来电震动都忘了开,罪过,罪过)

试着用别人的手机打电话过来,铃声总是不响。奇怪的是,通知声音和闹钟声音都是正常的,这很令人疑惑。

在排除勿扰模式的影响之后,尝试替换其他铃声(包括系统内置铃声),仍然没能解决问题。

没办法,只能logcat咯。。。

抓取来电时产生的log,搜索关键词ringtone,最后我关注到这一行:

02-19 22:59:10.789  1748  3208 I Telecom: Ringer: startRinging: skipping because ringer would not be audible. isVolumeOverZero=true, shouldRingForContact=true, isRingtonePresent=false: NCSSF.ac->ICFG.sf->ICFG.sf->ICFG.sf->CAMSM.pM_2002@Bfc

以上这段log直译:正在跳过,因为无法听到铃声

顺带打印的三个参数为:音量是否大于0:True,该联系人是否应该响铃:True,铃声是否存在:False

通过日志文本内容,从EvolutionX项目源码中全局搜索:

/* https://github.com/Evolution-X/packages_services_Telecomm/blob/5f75e65fd8b65ba3a1759d75a72440f5e27e755b/src/com/android/server/telecom/Ringer.java#L365 */
        if (isRingerAudible) {
            // 略...
        } else {
            String reason = String.format(
                    "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
                    isVolumeOverZero, shouldRingForContact, isRingtonePresent);
            Log.i(this, "startRinging: skipping because ringer would not be audible. " + reason);
            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " + reason);
            effect = mDefaultVibrationEffect;
        }

isRingerAudible参数定义在同文件的第319行:

/* https://github.com/Evolution-X/packages_services_Telecomm/blob/5f75e65fd8b65ba3a1759d75a72440f5e27e755b/src/com/android/server/telecom/Ringer.java#L319 */
        boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
        timer.record("isRingerAudible");

可以看出,只有isVolumeOverZeroshouldRingForContactisRingtonePresent都为真时才会响铃。

那么就可以确定问题出在系统没有找到铃声文件了

查看isRingtonePresent的定义,向上找到getRingtone方法,该方法定义如下:

/* https://github.com/Evolution-X/packages_services_Telecomm/blob/58d940e14cd89d7e0b56ed0e904ae0b53eba30e9/src/com/android/server/telecom/RingtoneFactory.java#L67 */
    public Ringtone getRingtone(Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig) {
        // 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, volumeShaperConfig);
        }
        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;
            if (UserManager.get(contextToUse).isUserUnlocked(contextToUse.getUserId())) {
                defaultRingtoneUri =
                        RingtoneManager.getActualDefaultRingtoneUriForPhoneAccountHandle(
                                contextToUse,
                                RingtoneManager.TYPE_RINGTONE,
                                incomingCall.getTargetPhoneAccount());
            } else {
                defaultRingtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
            }
            if (defaultRingtoneUri == null) {
                return null;
            }
            ringtone = RingtoneManager.getRingtone(
                contextToUse, defaultRingtoneUri, volumeShaperConfig);
        }
        if (ringtone != null) {
            ringtone.setAudioAttributes(new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .build());
        }
        return ringtone;
    }

简单分析一下该方法:关于工作资料(work profile)暂且不关注,先关注if(ringtone == null) {这段语句块:如果对应联系人有特定的铃声的话,那么使用特定的铃声,否则使用默认铃声,如果没有设置默认铃声的话,那么getRingtone方法返回null,也就导致isRingtonePresent为假了。

那么就先试试给某个联系人设置一个特定来电铃声,看看会不会响铃。

结果是可以响铃。那么就可以确定问题出在系统找不到默认铃声了

adb连接手机进行调试,通过settings命令,获取系统所有已经定义的键值:

phoenix:/ $ settings list system
accelerometer_rotation=0
accent_color=-15043608
adaptive_playback_timeout=30000
advanced_reboot=1
alarm_alert=content://0@media/external/audio/media/1641?title=alarm_clock1&canonical=1
alarm_alert_set=1
...
notification_sound=content://0@media/external/audio/media/1640?title=Meet&canonical=1
notification_sound_set=1
notification_sound_vib_screen_on=1
...
ringtone_89*****************9=content://0@media/external/audio/media/1639?title=Cloud&canonical=1
ringtone_89*****************2=content://0@media/external/audio/media/1639?title=Cloud&canonical=1
ringtone_set=1
ringtone_vibration_pattern=0
...

关注ringtone_89*****************9ringtone_89*****************2这两个键,这应该是对应了双卡不同的铃声(EvolutionX这次更新带来了可以为两张SIM卡设置不同来电铃声的功能)。

于是我推测,这应该是双卡铃声功能带来的bug。用settings命令定义一个键为ringtone的键值对:

phoenix:/ $ settings
Settings provider (settings) commands:
  help
      Print this help text.
  get [--user <USER_ID> | current] NAMESPACE KEY
      Retrieve the current value of KEY.
  put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]
      Change the contents of KEY to VALUE.
      TAG to associate with the setting.
      {default} to set as the default, case-insensitive only for global/secure namespace
  delete [--user <USER_ID> | current] NAMESPACE KEY
      Delete the entry for KEY.
  reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}
      Reset the global/secure table for a package with mode.
      RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive
  list [--user <USER_ID> | current] NAMESPACE
      Print all defined keys.
      NAMESPACE is one of {system, secure, global}, case-insensitive
255|phoenix:/ $ settings put system ringtone 'content://0@media/external/audio/media/1639?title=Cloud&canonical=1'

phoenix:/ $ settings list system
...
ringtone=content://0@media/external/audio/media/1639?title=Cloud&canonical=1
ringtone_89*****************9=content://0@media/external/audio/media/1639?title=Cloud&canonical=1
ringtone_89*****************2=content://0@media/external/audio/media/1639?title=Cloud&canonical=1
ringtone_set=1
ringtone_vibration_pattern=0
...

现在,问题彻底解决了。