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

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的id
private 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方法:
@Override
public 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方法:
@Override
public 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置为id
private 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方法
@Override
public 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给这个处理加了一个很好的封装。


你可能感兴趣的:(【Android 数据业务解析】PreferredApn修改的源码分析)