1. 程式人生 > >Android 7.1.1 系統時區,語言,地區,鈴聲配置

Android 7.1.1 系統時區,語言,地區,鈴聲配置

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[email protected]()->[email protected](),在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虛擬機器啟動後呼叫[email protected]()檢查下列系統屬性,並返回列表中的第一個非空屬性。
(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);
    }