【Android 数据业务解析】PreferredApn修改的源码分析

来源:互联网 发布:男包品牌 知乎 编辑:程序博客网 时间:2024/05/27 20:04
DcTracker中需要去获取preferredapn的id以及修改preferredapn的id,涉及到两个方法的使用,如下:

getPreferredApn方法

// 得到preferredapn的方法private ApnSetting getPreferredApn() {    if (mAllApnSettings == null || mAllApnSettings.isEmpty()) { // 为空判断        log("getPreferredApn: mAllApnSettings is " + ((mAllApnSettings == null)?"null":"empty"));        return null;    }    String subId = Long.toString(mPhone.getSubId());    Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);    // 从数据库中读取preferredapn    Cursor cursor = mPhone.getContext().getContentResolver().query(            uri, new String[] { "_id", "name", "apn" },            null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);    if (cursor != null) {        mCanSetPreferApn = true;    } else {        mCanSetPreferApn = false;    }    // mRequestedApnType就是PhoneConstants.APN_TYPE_DEFAULT    log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor            + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));    if (mCanSetPreferApn && cursor.getCount() > 0) {        int pos;        cursor.moveToFirst();        // 获取preferredapn在数据库中的id号        pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));        for(ApnSetting p : mAllApnSettings) {            log("getPreferredApn: apnSetting=" + p);            // 遍历apn集合,存在id相同,并且apn type符合preferredapn的要求时,则返回给preferredapn            if (p.id == pos && p.canHandleType(mRequestedApnType)) {                log("getPreferredApn: X found apnSetting" + p);                cursor.close();                return p;            }        }    }    if (cursor != null) {        cursor.close();    }    log("getPreferredApn: X not found");    return null;}

setPreferredApn方法

// 更新数据库中保存的preferredapn的idprivate void setPreferredApn(int pos) {    if (!mCanSetPreferApn) {        log("setPreferredApn: X !canSEtPreferApn");        return;    }    // 由于id只有一个,所以首先要删除过去的id    String subId = Long.toString(mPhone.getSubId());    // 注意PREFERAPN_NO_UPDATE_URI_USING_SUBID中包含了NO_UPDATE,说明此修改无需将数据库的变化通知相应的监听器    Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);    log("setPreferredApn: delete");    ContentResolver resolver = mPhone.getContext().getContentResolver();    // 删除数据库中的数据    resolver.delete(uri, null, null);    if (pos >= 0) {        // 如果pos为-1,则不执行插入,否则插入新的preferredapn,pos即为id        log("setPreferredApn: insert");        ContentValues values = new ContentValues();        values.put(APN_ID, pos); // APN_ID = "apn_id"        resolver.insert(uri, values);    }}


涉及到ContentProvider的query、delete和insert方法。而该ContentProvider对应的是TelephonyProvider。
因而query、delete和insert方法最终会调用到TelephonyProvider这个类的对应方法。

下面来看下TelephonyProvider这个类。
query方法:
@Overridepublic synchronized Cursor query(Uri url, String[] projectionIn, String selection,                                 String[] selectionArgs, String sort) {    if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection="            + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort);    TelephonyManager mTelephonyManager =            (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);    int subId = SubscriptionManager.getDefaultSubId();    String subIdString;    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();    qb.setStrict(true); // a little protection from injection attacks    qb.setTables(CARRIERS_TABLE);    // 匹配URI    int match = s_urlMatcher.match(url);    switch (match) {        ... ...        // preferredapn的处理        case URL_PREFERAPN_USING_SUBID:        case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {            // 获取subid            subIdString = url.getLastPathSegment();            try {                subId = Integer.parseInt(subIdString);            } catch (NumberFormatException e) {                loge("NumberFormatException" + e);                return null;            }            if (DBG) log("subIdString = " + subIdString + " subId = " + subId);        }        //intentional fall through from above case        case URL_PREFERAPN:        case URL_PREFERAPN_NO_UPDATE: {            qb.appendWhere("_id = " + getPreferredApnId(subId));            break;        }        ... ...        default: {            return null;        }    }    if (match != URL_SIMINFO) {        if (projectionIn != null) {            for (String column : projectionIn) {                if (Telephony.Carriers.TYPE.equals(column) ||                        Telephony.Carriers.MMSC.equals(column) ||                        Telephony.Carriers.MMSPROXY.equals(column) ||                        Telephony.Carriers.MMSPORT.equals(column) ||                        Telephony.Carriers.APN.equals(column)) {                    // noop                } else {                    checkPermission();                    break;                }            }        } else {            // null returns all columns, so need permission check            checkPermission();        }    }    // 获取操作数据库的对象    SQLiteDatabase db = mOpenHelper.getReadableDatabase();    Cursor ret = null;    try {        // Exclude entries marked deleted        if (CARRIERS_TABLE.equals(qb.getTables())) {            if (TextUtils.isEmpty(selection)) {                selection = "";            } else {                selection += " and ";            }            selection += "edited!=" + Telephony.Carriers.USER_DELETED + " and edited!="                    + Telephony.Carriers.USER_DELETED_BUT_PRESENT_IN_XML + " and edited!="                    + Telephony.Carriers.CARRIER_DELETED + " and edited!="                    + Telephony.Carriers.CARRIER_DELETED_BUT_PRESENT_IN_XML;            if (VDBG) log("query: selection modified to " + selection);        }        // 从数据库中查询_id = getPreferredApnId(subId)的对象        ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);    } catch (SQLException e) {        loge("got exception when querying: " + e);    }    if (ret != null)        ret.setNotificationUri(getContext().getContentResolver(), url);    return ret;}

// 从preferred-apn.xml中读取出键"apn_id"+subId的值private long getPreferredApnId(int subId) {    SharedPreferences sp = getContext().getSharedPreferences(            PREF_FILE, Context.MODE_PRIVATE);    return sp.getLong(COLUMN_APN_ID + subId, -1);}

在查询preferredapn的过程中,先从preferred-apn.xml中读取出键值,该键值存储的是preferredapn的id号。然后在将id号作为where条件,从数据库的carriers表中读取出符合要求的对象,然后返回。

preferred-apn.xml文件的默认路径为:/data/data/com.android.providers.telephony/shared_prefs/
该xml文件的内容为:


上面的是从模拟器中导出来的,因为是单卡,所以不带id。

delete方法:
@Overridepublic synchronized int delete(Uri url, String where, String[] whereArgs){    // 复位,用于告知监听器数据库发生了变化    int count = 0;    // 获取卡的id    int subId = SubscriptionManager.getDefaultSubId();    String userOrCarrierEdited = ") and (" +            Telephony.Carriers.EDITED + "=" + Telephony.Carriers.USER_EDITED +  " or " +            Telephony.Carriers.EDITED + "=" + Telephony.Carriers.CARRIER_EDITED + ")";    String notUserOrCarrierEdited = ") and (" +            Telephony.Carriers.EDITED + "!=" + Telephony.Carriers.USER_EDITED +  " and " +            Telephony.Carriers.EDITED + "!=" + Telephony.Carriers.CARRIER_EDITED + ")";    ContentValues cv = new ContentValues();    cv.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_DELETED);    checkPermission();    SQLiteDatabase db = mOpenHelper.getWritableDatabase();    // 匹配URI    int match = s_urlMatcher.match(url);    switch (match)    {        ... ...        // preferredapn的操作在此处        case URL_PREFERAPN_USING_SUBID:        case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {            // 获取subid            String subIdString = url.getLastPathSegment();            try {                subId = Integer.parseInt(subIdString);            } catch (NumberFormatException e) {                loge("NumberFormatException" + e);                throw new IllegalArgumentException("Invalid subId " + url);            }            if (DBG) log("subIdString = " + subIdString + " subId = " + subId);        }        //intentional fall through from above case        // 中间无break,继续执行        case URL_PREFERAPN:        case URL_PREFERAPN_NO_UPDATE:        {            // 将xml文件中preferredapn的id置为-1            setPreferredApnId((long)-1, subId);            // 如果url匹配到的是URL_PREFERAPN或者URL_PREFERAPN_USING_SUBID时,说明需要通知相应的监听器,数据库发生了变化            if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1;            break;        }        ... ...        default: {            throw new UnsupportedOperationException("Cannot delete that URL: " + url);        }    }    // 通知相应的监听器,数据库发生变化    if (count > 0) {        getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,                true, UserHandle.USER_ALL);    }    return count;}

// 将preferred-apn.xml中的preferredapn的id置为idprivate void setPreferredApnId(Long id, int subId) {    SharedPreferences sp = getContext().getSharedPreferences(            PREF_FILE, Context.MODE_PRIVATE);    SharedPreferences.Editor editor = sp.edit();    // COLUMN_APN_ID = "apn_id"    editor.putLong(COLUMN_APN_ID + subId, id != null ? id.longValue() : -1);    editor.apply();}

delete方法是将preferred-apn.xml中的键值修改为-1,-1肯定不是数据库中的id。

insert方法
@Overridepublic synchronized Uri insert(Uri url, ContentValues initialValues){    Uri result = null;    int subId = SubscriptionManager.getDefaultSubId();    checkPermission();    SQLiteDatabase db = mOpenHelper.getWritableDatabase();    // 匹配URI    int match = s_urlMatcher.match(url);    boolean notify = false;    switch (match)    {        ... ...        // preferredapn的操作        case URL_PREFERAPN_USING_SUBID:        case URL_PREFERAPN_NO_UPDATE_USING_SUBID:        {            // 获取subid            String subIdString = url.getLastPathSegment();            try {                subId = Integer.parseInt(subIdString);            } catch (NumberFormatException e) {                loge("NumberFormatException" + e);                return result;            }            if (DBG) log("subIdString = " + subIdString + " subId = " + subId);        }        //intentional fall through from above case        case URL_PREFERAPN:        case URL_PREFERAPN_NO_UPDATE:        {            if (initialValues != null) {                // 查看ContentValues中是否包含"apn_id"的键                if(initialValues.containsKey(COLUMN_APN_ID)) {                    // 将preferredapn的id更新到数据库中                    setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId);                }            }            break;        }        ... ...    }    if (notify) {        getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,                true, UserHandle.USER_ALL);    }    return result;}

同样也是调用setPreferredApnId方法,将键值修改为指定的id。

在DcTracker.java中,
static final Uri PREFERAPN_NO_UPDATE_URI_USING_SUBID =        Uri.parse("content://telephony/carriers/preferapn_no_update/subId/");

在TelephonyProvider.java中,

s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN);s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update", URL_PREFERAPN_NO_UPDATE);s_urlMatcher.addURI("telephony", "carriers/preferapn/subId/*", URL_PREFERAPN_USING_SUBID);s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*", URL_PREFERAPN_NO_UPDATE_USING_SUBID);

总结:
在获取和修改preferredapn时,最终都是通过处理preferred-apn.xml中的键值来做到的,Google给这个处理加了一个很好的封装。


原创粉丝点击