Android音频相关(四)设置铃声流程总结

您所在的位置:网站首页 安卓手机铃声音量不能用 Android音频相关(四)设置铃声流程总结

Android音频相关(四)设置铃声流程总结

2024-07-12 19:52| 来源: 网络整理| 查看: 265

 

本文主要介绍的是设置铃声的流程,在流程梳理清楚后解决问题也有大致的方向了。

一、要点概述

补充知识点:

frameworks/base/packages/SettingProvider/中的生成的数据库文件存储在data/system/users/userid(没有设置多用户userid则为0)。

在生成的Settings_system.xml数据库中记录着ringtone的信息和是否设置成功的ringtone_set值。

二、设置铃声流程

从安卓N开始,在设置铃声之后会拷贝一份到data/system_de/0/ringtones/ringtone_cache作为铃声的缓存文件(ringtone_cache是文件),因此即使sd卡拔了。依然能播放之前在sd卡中设置的铃声。

当然了正常的铃声存储在data/system/users/0 下面的settings_system.xml文件中。

首先我们来研究一下铃声的ringtone_cache存储是如何实现的,当然了解这个对实现双卡铃声也是有帮助的。

frameworks/base/core/java/android/provider/Settings.java

/** {@hide} */ public static final String RINGTONE_CACHE = "ringtone_cache"; /** {@hide} */ public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE); ......... /** * The content:// style URL for this table */ //content://settings/system/ringtone_cache public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/system"); .......... /** * Construct the content URI for a particular name/value pair, * useful for monitoring changes with a ContentObserver. * @param name to look up in the table * @return the corresponding content URI, or null if not present */ public static Uri getUriFor(String name) { if (MOVED_TO_SECURE.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System" + " to android.provider.Settings.Secure, returning Secure URI."); return Secure.getUriFor(Secure.CONTENT_URI, name); } if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System" + " to android.provider.Settings.Global, returning read-only global URI."); return Global.getUriFor(Global.CONTENT_URI, name); } return getUriFor(CONTENT_URI, name); } ...... public static Uri getUriFor(Uri uri, String name) { return Uri.withAppendedPath(uri, name); }

1.注意是首次设置铃声会走到下面的方法中。(首次设置铃声流程)

frameworks/base/media/java/android/media/MediaScanner.java

这里需要注意 ringtones是扫描时,判断此文件是否是铃声资源文件。

 下面要补充的是SettingUri是Settings数据库中的uri形式,ringtoneUri为当前铃声实际的uri。

Settings.System.DEFAULT_NOTIFICATION_URI----content://setting/system/notification_sound Settings.System.DEFAULT_RINGTONE_URI------content://settings/system/ringtone Settings.System.DEFAULT_ALARM_ALERT_URI-----content://settings/system/alarm_alert

注意在下面的方法里面会设置ringtone_set的值为1.

2.平时我们设置铃声调用的都是下面的方法

frameworks/base/media/java/android/media/RingtoneManager.java

/** * 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()); } //将设置的内容写入Setting数据库 //adb shell settings get system [key](查询setting数据库底下的system表的【key】对应的value) 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 if (ringtoneUri != null) { final Uri cacheUri = getCacheForType(type, context.getUserId()); try (InputStream in = openRingtone(context, ringtoneUri); OutputStream out = resolver.openOutputStream(cacheUri)) { //将铃声写入data/system_de/ringtones目录下 Streams.copy(in, out); } catch (IOException e) { Log.w(TAG, "Failed to cache ringtone: " + e); } } } /** {@hide} */ public static Uri getCacheForType(int type, int userId) { if ((type & TYPE_RINGTONE) != 0) { return ContentProvider.maybeAddUserId(Settings.System.RINGTONE_CACHE_URI, userId); } else if ((type & TYPE_NOTIFICATION) != 0) { return ContentProvider.maybeAddUserId(Settings.System.NOTIFICATION_SOUND_CACHE_URI, userId); } else if ((type & TYPE_ALARM) != 0) { return ContentProvider.maybeAddUserId(Settings.System.ALARM_ALERT_CACHE_URI, userId); } //add by xiangzaixiansheng for support dual sim card ringtones @{ else if ((type & TYPE_RINGTONE_2) != 0) { return ContentProvider.maybeAddUserId(Settings.System.RINGTONE_CACHE_URI_2, userId); //}else if ((type & TYPE_MMS_NOTIFICATION) != 0) { // return ContentProvider.maybeAddUserId(Settings.System.MMS_NOTIFICATION_SOUND_CACHE_URI, userId); //add by xiangzaixiansheng for support dual sim card ringtones @} } return null; } 三、播放在ringtone_cache中文件

/frameworks/base/media/java/android/media/MediaPlayer.java

这里面我们可以看见RingtoneManager类直接提供了两个方法可以获取URl

public void setDataSource(@NonNull Context context, @NonNull Uri uri, @Nullable Map headers, @Nullable List 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()); /// M: If scheme is null, try to get path from uri and setDataSource with path. if (scheme == null || 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); //获取铃声的两个URL final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId()); final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type); if (attemptDataSource(resolver, cacheUri)) { return; } else if (attemptDataSource(resolver, actualUri)) { return; } else { setDataSource(uri.toString(), headers, cookies); } } else { // Try requested Uri locally first, or fallback to media server if (attemptDataSource(resolver, uri)) { return; } else { setDataSource(uri.toString(), headers, cookies); } } } 四、自定义铃声

1.铃声一般都放在  rameworks/base/data/sounds/  2.修改默认铃声的话,需要修改build/target/product/core_base.mk 和full_base.mk

PRODUCT_PROPERTY_OVERRIDES := \  ro.config.notification_sound=OnTheHunt.ogg \  ro.config.alarm_alert=Alarm_Classic.ogg  3.铃声增减都在mk文件中添加

frameworks/base/data/sounds/AllAudio.mk

五、问题分析

来电无声的问题分析

1.首先需要判断在data/system/users/0/settings_system.xml文件,搜索ringtone,查看ringtone_set和ringtone的值。

1) 若ringtone_set不是1,检查MediaScanner.java文件的endFile的方法。

 adb shell settings get system ringtone_set 看值是否为1. ( alarm_alert_set 和 notification_sound_set )

2),若ringtone未设置成功,检查setDefaultRingtoneFileNames(MediaScanner.java)和setRingtoneIfNotSet(MediaScanner.java)。

2,如何查看是否加载成功?

搜索setDataSource,加载成功的log如下: MediaPlayerService: line:938 setDataSource fd=8 (/system/media/audio/ringtones/Ring_Synth_04.ogg), offset=0, length=576460752303423487 未有setDataSource,检查如下流程:

1)/vendor/mediatek/proprietary/packages/services/Telecomm/src/com/android/server/telecom/Ringer.java  startRinging ==> 2)/vendor/mediatek/proprietary/packages/services/Telecomm/src/com/android/server/telecom/AsyncRingtonePlayer.java handlePlay ==> 3)/frameworks/base/media/java/android/media/Ringtone.java play ==> 4)/frameworks/base/media/java/android/media/MediaPlayer.java setDataSource

3,播放流程是否成功?

播放流程为:new mediaPlayer,setDataSource,prepare,start,搜索以上相关log,查看没有调用的原因。4,是否被静音?

1)播放音量是否为0?

如下表示铃声(stream type为2)的音量值index是14,若index为0,则调大音量测试 APM_AudioPolicyManager: startOutput() output 13, stream 2, session 289 AudioPolicyManagerCustomImpl: checkAndSetVolume stream = 2 index = 14 mId = 3 device = 0x2(0x2) delayMs = 0 force = 0 [1/0x2/14]

2)是否被mute?

搜索“mute”的关键字,如下被标示的log表示被铃声被mute,muted count>0,就标示被mute APM::AudioPolicyManager: checkAndSetVolume() stream 2 muted count 1 check被静音的地方,加log: 1,/frameworks/base/services/core/java/com/android/server/audio/AudioService.java 1), public static final boolean DEBUG_VOL = true;//将 Log.isLoggable(TAG + ".VOL", Log.DEBUG) || LOGD修改为true; 2),1180 private void adjustStreamVolume(int streamType, int direction, int flags, 1181 String callingPackage, String caller, int uid) { 1182 if (mUseFixedVolume) { 1183 return; 1184 } 1185 if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction 1186 + ", flags=" + flags + ", caller=" + caller,new Exception("CallStack"));//add new Exception("CallStack")

2) ,/frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp (1),#include //add (2),5784void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, 5785 bool on, 5786 const sp; outputDesc, 5787 int delayMs, 5788 audio_devices_t device) 5789{ 5790 const StreamDescriptor; streamDesc = mStreams.valueFor(stream); 5791 if (device == AUDIO_DEVICE_NONE) { 5792 device = outputDesc-;device(); 5793 } 5794 5795 ALOGD("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", 5796 stream, on, outputDesc-;mMuteCount[stream], device);//change toALOGD CallStack stack("CSXXX", 1); //add

5,抓取log方式的方式 首先清空mtklog文件夹 进入工模,Hardware Testing -- Audio -- Audio Logger 勾选 Audio Stream Output Dump Audio Mixer Buffer Dump Audio Track Buffer Dump start log 复现问题 来电无声的时候添加adb命令 adb shell cat /sys/kernel/debug/mtksocaudio filename adb shell cat /sys/kernel/debug/mtksocanaaudio filename stop log



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3