Q 版本之前修改方法
新增预制铃声文件至 frameworks/base/data/sounds/ringtones/ogg/ 目录,注意文件格式为 .ogg
编译时拷贝文件至 out 中
frameworks/base/data/sounds/AllAudio.mk
LOCAL_PATH := frameworks/base/data/sounds PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \ $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \ $(LOCAL_PATH)/ringtones/ogg/ring1.ogg:system/media/audio/ringtones/ring1.ogg \ $(LOCAL_PATH)/ringtones/ogg/ring2.ogg:system/media/audio/ringtones/ring2.ogg \ $(LOCAL_PATH)/ringtones/ogg/ring3.ogg:system/media/audio/ringtones/ring3.ogg \ ....
设置默认铃声为 ring3, 替换原来 Ring_Synth_04.ogg
build/make/target/product/full_base.mk
# Additional settings used in all AOSP builds PRODUCT_PROPERTY_OVERRIDES := \ - ro.config.ringtone=Ring_Synth_04.ogg \ + ro.config.ringtone=ring3.ogg \ ro.config.notification_sound=pixiedust.ogg
最终编译后将写入到 default.prop 文件中
通过 adb 命令可以查看默认值
默认铃声
adb shell getprop ro.config.ringtone
ring1.ogg
默认通知声
adb shell getprop ro.config.notification_sound
OnTheHunt.ogg
默认闹钟声
adb shell getprop ro.config.alarm_alert
Alarm_Classic.ogg
资源文件对应URI
adb shell settings get system ringtone/notification_sound/alarm_alert
content://media/internal/audio/media/200
Q 版本修改方法
Q 版本编译后系统中是没有默认铃声的,也就是没有设置对应值,按照之前方法修改并未生效
搜索 ro.config.ringtone 发现有一堆地方,显然都不是这些,如果是那就会有默认铃声
./build/make/target/product/mainline.mk: ro.config.ringtone=Ring_Synth_04.ogg \ ./build/make/target/product/gsi_common.mk: ro.config.ringtone=Ring_Synth_04.ogg \ ./build/make/target/product/full_base.mk: ro.config.ringtone=Ring_Synth_04.ogg \ ./packages/services/Car/car_product/build/car.mk: ro.config.ringtone=Girtab.ogg \ ./device/generic/goldfish/sdk_phone_x86_vendor.mk: ro.config.ringtone=Ring_Synth_04.ogg \ ./device/generic/armv7-a-neon/mini_common.mk: ro.config.ringtone=Ring_Synth_04.ogg \ ./device/google/bonito/aosp_sargo.mk: ro.config.ringtone=Ring_Synth_04.ogg \ ./device/google/bonito/aosp_bonito.mk: ro.config.ringtone=Ring_Synth_04.ogg \
修改位置
build/make/target/product/handheld_system.mk
PRODUCT_PROPERTY_OVERRIDES += \ ro.carrier=unknown \ + ro.config.ringtone=ring3.ogg \ ro.config.notification_sound=OnTheHunt.ogg \ ro.config.alarm_alert=Alarm_Classic.ogg
预制文件位置不变, system 分区需要改为 product
frameworks/base/data/sounds/AllAudio.mk
LOCAL_PATH := frameworks/base/data/sounds PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \ $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \ $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \ + $(LOCAL_PATH)/ringtones/ogg/ring1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ring1.ogg \ + $(LOCAL_PATH)/ringtones/ogg/ring2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ring2.ogg \ + $(LOCAL_PATH)/ringtones/ogg/ring3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ring3.ogg \ ....
简单看下流程
上面说到的通过 adb 指令可以获取对应资源值,系统播放最终对应的都是资源URI,也就对应着数据库
frameworks\base\media\java\android\media\MediaScanner.java
MediaScanner 读取 ro.config.ringtone 值后组装成 URI 写入 Settings.system 数据库
/** The filename for the default sound for the ringer ringtone. */ @UnsupportedAppUsage private String mDefaultRingtoneFilename; private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config."; 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); } // Setting a flag in order not to use bulk insert for the file related with // notifications, ringtones, and alarms, because the rowId of the inserted file is // needed. else if (ringtones && !mDefaultRingtoneSet) { if (TextUtils.isEmpty(mDefaultRingtoneFilename) || doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) { needToSetSettings = true; } } if(needToSetSettings) { if (notifications) { setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId); mDefaultNotificationSet = true; } else if (ringtones) { setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId); mDefaultRingtoneSet = true; } else if (alarms) { setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId); mDefaultAlarmSet = true; } } private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) { if (wasRingtoneAlreadySet(settingName)) { return; } ContentResolver cr = mContext.getContentResolver(); String existingSettingValue = Settings.System.getString(cr, settingName); if (TextUtils.isEmpty(existingSettingValue)) { final Uri settingUri = Settings.System.getUriFor(settingName); final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId); RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.getDefaultType(settingUri), ringtoneUri); } Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1); }
RingtoneManager 中从 Settings.system 读取指定类型对应的URI
frameworks\base\media\java\android\media\RingtoneManager.java
public static Uri getActualDefaultRingtoneUri(Context context, int type) { String setting = getSettingForType(type); if (setting == null) return null; final String uriString = Settings.System.getStringForUser(context.getContentResolver(), setting, context.getUserId()); Uri ringtoneUri = uriString != null ? Uri.parse(uriString) : null; // If this doesn't verify, the user id must be kept in the uri to ensure it resolves in the // correct user storage if (ringtoneUri != null && ContentProvider.getUserIdFromUri(ringtoneUri) == context.getUserId()) { ringtoneUri = ContentProvider.getUriWithoutUserId(ringtoneUri); } return ringtoneUri; } private static String getSettingForType(int type) { if ((type & TYPE_RINGTONE) != 0) { return Settings.System.RINGTONE; } else if ((type & TYPE_NOTIFICATION) != 0) { return Settings.System.NOTIFICATION_SOUND; } else if ((type & TYPE_ALARM) != 0) { return Settings.System.ALARM_ALERT; } else { return null; } }
Settings 中声音界面获取默认值对应名称显示
vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\notification\RingtonePreferenceControllerBase.java
private void updateSummary(Preference preference) { final Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri( mContext, getRingtoneType()); final CharSequence summary = getSummary(ringtoneUri); if (summary != null) { ThreadUtils.postOnMainThread(() -> preference.setSummary(summary)); } } private CharSequence getSummary(Uri ringtoneUri) { CharSequence summary = null; try { summary = Ringtone.getTitle( mContext, ringtoneUri, false /* followSettingsUri */, true /* allowRemote */); return summary; }catch (Exception e){ e.printStackTrace(); return null; } }
Ringtone 中 getTitle 解析获取对应文本名称
frameworks\base\media\java\android\media\Ringtone.java
public static String getTitle( Context context, Uri uri, boolean followSettingsUri, boolean allowRemote) { ContentResolver res = context.getContentResolver(); String title = null; if (uri != null) { String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()); if (Settings.AUTHORITY.equals(authority)) { if (followSettingsUri) { Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.getDefaultType(uri)); String actualTitle = getTitle( context, actualUri, false /*followSettingsUri*/, allowRemote); title = context .getString(com.android.internal.R.string.ringtone_default_with_actual, actualTitle); } } else { Cursor cursor = null; try { if (MediaStore.AUTHORITY.equals(authority)) { final String mediaSelection = allowRemote ? null : MEDIA_SELECTION; cursor = res.query(uri, MEDIA_COLUMNS, mediaSelection, null, null); if (cursor != null && cursor.getCount() == 1) { cursor.moveToFirst(); return cursor.getString(1); } // missing cursor is handled below } } catch (SecurityException e) { IRingtonePlayer mRemotePlayer = null; if (allowRemote) { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mRemotePlayer = audioManager.getRingtonePlayer(); } if (mRemotePlayer != null) { try { title = mRemotePlayer.getTitle(uri); } catch (RemoteException re) { } } } finally { if (cursor != null) { cursor.close(); } cursor = null; } if (title == null) { title = uri.getLastPathSegment(); } } } else { title = context.getString(com.android.internal.R.string.ringtone_silent); } if (title == null) { title = context.getString(com.android.internal.R.string.ringtone_unknown); if (title == null) { title = ""; } } return title; }