Android手机开机后,有一些常用的默认属性配置,今天就捡主要的说下手机里默认时区,语言,铃声这些常用的属性配置

1、配置默认时区

对于China来说默认时区是GMT+08:00,如果系统中没有配置默认时区的属性,时区肯定不是中国标准时间,控制时区的属性为persist.sys.timezone,只需要在配置系统默认属性的地方加上下面属性就行


#在mk文件中配置系统属性如下
PRODUCT_PROPERTY_OVERRIDES += persist.sys.timezone=Asia/Shanghai
#或者
ADDITIONAL_BUILD_PROPERTIES += persist.sys.timezone=Asia/Shanghai

#在*.prop文件配置如下
persist.sys.timezone=Asia/Shanghai

关于属性的配置可以参考《

Android 7.1.1中SystemProperties详解》,知道了怎么配置后好奇为啥要这样配置,就看来看android系统中对这个属性的使用


当SystemServer启动后,会在main方法中调用SystemServer@run()->SystemServer@startOtherServices(),在startOtherServices中调用mSystemServiceManager.startService(AlarmManagerService.class)启动AlarmManagerService



//frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
 static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 public void onStart() {
        ...
        // We have to set current TimeZone info to kernel
        // because kernel doesn't keep this after reboot
        setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
    }

当AlarmManagerService启动后,都会根据TIMEZONE_PROPERTY调用setTimeZoneImpl设置时区,置于这个方法具体怎么实现的就不在啰嗦了,有兴趣可以自己去看看。


除了这个地方,在ServiceStateTracker中fixTimeZone也会用到这个属性,在执行fixTimeZone时先会调用shouldFixTimeZoneNow根据运营商mcc和needToFixTimeZone判断是否需要修正时区


//frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java
    protected boolean shouldFixTimeZoneNow(Phone phone, String operatorNumeric,
            String prevOperatorNumeric, boolean needToFixTimeZone) {
        // Return false if the mcc isn't valid as we don't know where we are.
        // Return true if we have an IccCard and the mcc changed or we
        // need to fix it because when the NITZ time came in we didn't
        // know the country code.

        // If mcc is invalid then we'll return false
        int mcc;
        try {
            mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
        } catch (Exception e) {
            if (DBG) {
                log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
                        " retVal=false");
            }
            return false;
        }

        // If prevMcc is invalid will make it different from mcc
        // so we'll return true if the card exists.
        int prevMcc;
        try {
            prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
        } catch (Exception e) {
            prevMcc = mcc + 1;
        }

        // Determine if the Icc card exists
        boolean iccCardExist = false;
        if (mUiccApplcation != null) {
            iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
        }

        // Determine retVal
        boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
        if (DBG) {
            long ctm = System.currentTimeMillis();
            log("shouldFixTimeZoneNow: retVal=" + retVal +
                    " iccCardExist=" + iccCardExist +
                    " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
                    " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
                    " needToFixTimeZone=" + needToFixTimeZone +
                    " ltod=" + TimeUtils.logTimeOfDay(ctm));
        }
        return retVal;
    }

其中needToFixTimeZone是在收到运营商上报RIL_UNSOL_NITZ_TIME_RECEIVED事件后,才会把needToFixTimeZone置为true,关于NITZ可以查看《

Android 7.1.1时间更新NITZ和NTP详解》


当满足条件后就会在fixTimeZone中根据isoCountryCode和配置的属性"persist.sys.timezone"修复时区,在系统资源time_zones_by_country.xml中counrtycode对应着time_zones如下


//frameworks/base/core/res/res/xml/time_zones_by_country.xml
    <!-- CHINA, 8:00 -->
    <timezone code="cn">Asia/Shanghai</timezone>
    <timezone code="cn">Asia/Harbin</timezone>
    <timezone code="cn">Asia/Chongqing</timezone>
    <timezone code="cn">Asia/Urumqi</timezone>
    <timezone code="cn">Asia/Kashgar</timezone>

2、配置默认地区和语言配置


当Dalvik虚拟机启动后调用AndroidRuntime.cpp@readLocale()检查下列系统属性,并返回列表中的第一个非空属性。

(1) persist.sys.locale

(2) persist.sys.language/country/localevar (language不为才空检查country 和 localevar)

(3) ro.product.locale

(4) ro.product.locale.language/region


//frameworks/base/core/jni/AndroidRuntime.cpp
const std::string readLocale()
{
    const std::string locale = getProperty("persist.sys.locale", "");
    if (!locale.empty()) {
        return locale;
    }

    const std::string language = getProperty("persist.sys.language", "");
    if (!language.empty()) {
        const std::string country = getProperty("persist.sys.country", "");
        const std::string variant = getProperty("persist.sys.localevar", "");

        std::string out = language;
        if (!country.empty()) {
            out = out + "-" + country;
        }

        if (!variant.empty()) {
            out = out + "-" + variant;
        }

        return out;
    }

    const std::string productLocale = getProperty("ro.product.locale", "");
    if (!productLocale.empty()) {
        return productLocale;
    }

    // If persist.sys.locale and ro.product.locale are missing,
    // construct a locale value from the individual locale components.
    const std::string productLanguage = getProperty("ro.product.locale.language", "en");
    const std::string productRegion = getProperty("ro.product.locale.region", "US");
    //都没有配置的话,默认返回en-US
    return productLanguage + "-" + productRegion;
}

在系统资源文件locale_config.xml,有"语言-地区"的所有列表,下面只保留了国内的


//frameworks/base/core/res/res/values/locale_config.xml
<resources>
    <string-array translatable="false" name="supported_locales">
        ... 
        <item>zgh-MA</item> <!-- Standard Moroccan Tamazight (Morocco) -->
        <item>zh-Hans-CN</item> <!-- Chinese (Simplified Han,China) -->
        <item>zh-Hans-HK</item> <!-- Chinese (Simplified Han,Hong Kong) -->
        <item>zh-Hans-MO</item> <!-- Chinese (Simplified Han,Macau) -->
        <item>zh-Hans-SG</item> <!-- Chinese (Simplified Han,Singapore) -->
        <item>zh-Hant-HK</item> <!-- Chinese (Traditional Han,Hong Kong) -->
        <item>zh-Hant-MO</item> <!-- Chinese (Traditional Han,Macau) -->
        <item>zh-Hant-TW</item> <!-- Chinese (Traditional Han,Taiwan) -->
        <item>zu-ZA</item> <!-- Zulu (South Africa) -->
    </string-array>
</resources>

3、默认铃声配置


系统中默认铃声分为三类,来电默认铃声,通知默认铃声,闹铃默认铃声,分别对应一下属下

ro.config.ringtone  

ro.config.notification_sound

ro.config.alarm_alert

在系统启动MediaScanner初始化时,会利用这三个属性,设置默认的铃声


//frameworks/base/media/java/android/media/MediaScanner.java
//Settings.System.RINGTONE = "ringtone";
//Settings.System.NOTIFICATION_SOUND = "notification_sound";
//Settings.System.ALARM_ALERT = "alarm_alert";
   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);
    }

利用doScanFile所有扫描所有文件,调用endFile(),把所有media信息插入数据库,在插入数据库时候会根据mDefaultRingtoneFilename,来判断文件是否存在,来决定是否设置对应默认铃声


//frameworks/base/media/java/android/media/MediaScanner.java
   private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
                boolean alarms, boolean music, boolean podcasts)
                throws RemoteException {
            // update database
            ...
            if (notifications && !mDefaultNotificationSet) {
                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
                    needToSetSettings = true;
                }
            } else if (ringtones && !mDefaultRingtoneSet) {
                if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
                        doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
                    needToSetSettings = true;
                }
            } else if (alarms && !mDefaultAlarmSet) {
                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
                    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;
                }
            }
            return result;
        }

最后调用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);
        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);
    }