FileDocCategorySizeDatePackage
SubscriptionController.javaAPI DocAndroid 5.1 API62473Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony

SubscriptionController

public class SubscriptionController extends ISub.Stub
SubscriptionController to provide an inter-process communication to access Sms in Icc. Any setters which take subId, slotId or phoneId as a parameter will throw an exception if the parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID. All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()). Finally, any getters which perform the mapping between subscriptions, slots and phones will return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters will fail and return the appropriate error value. Ie calling getSlotId(INVALID_SUBSCRIPTION_ID) will return INVALID_SLOT_ID and calling getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID) will return null.

Fields Summary
static final String
LOG_TAG
static final boolean
DBG
static final boolean
VDBG
static final int
MAX_LOCAL_LOG_LINES
private ScLocalLog
mLocalLog
protected final Object
mLock
private static SubscriptionController
sInstance
The singleton instance.
protected static PhoneProxy[]
sProxyPhones
protected android.content.Context
mContext
protected android.telephony.TelephonyManager
mTelephonyManager
protected CallManager
mCM
private static HashMap
mSlotIdxToSubId
private static int
mDefaultFallbackSubId
private static int
mDefaultPhoneId
private int[]
colorArr
Constructors Summary
private SubscriptionController(android.content.Context c)

        mContext = c;
        mCM = CallManager.getInstance();
        mTelephonyManager = TelephonyManager.from(mContext);

        if(ServiceManager.getService("isub") == null) {
                ServiceManager.addService("isub", this);
        }

        if (DBG) logdl("[SubscriptionController] init by Context");
    
private SubscriptionController(Phone phone)

        mContext = phone.getContext();
        mCM = CallManager.getInstance();

        if(ServiceManager.getService("isub") == null) {
                ServiceManager.addService("isub", this);
        }

        if (DBG) logdl("[SubscriptionController] init by Phone");
    
Methods Summary
public intaddSubInfoRecord(java.lang.String iccId, int slotId)
Add a new SubInfoRecord to subinfo database if needed

param
iccId the IccId of the SIM card
param
slotId the slot which the SIM is inserted
return
0 if success, < 0 on error.

        if (DBG) logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
        enforceSubscriptionPermission();

        if (iccId == null) {
            if (DBG) logdl("[addSubInfoRecord]- null iccId");
            return -1;
        }

        int[] subIds = getSubId(slotId);
        if (subIds == null || subIds.length == 0) {
            if (DBG) {
                logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
                    + subIds);
            }
            return -1;
        }

        String nameToSet;
        String CarrierName = mTelephonyManager.getSimOperatorNumericForSubscription(subIds[0]);
        if (DBG) logdl("[addSubInfoRecord] CarrierName = " + CarrierName);
        String simCarrierName = mTelephonyManager.getSimOperatorNameForSubscription(subIds[0]);

        if (!TextUtils.isEmpty(simCarrierName)) {
            nameToSet = simCarrierName;
        } else {
            nameToSet = "CARD " + Integer.toString(slotId + 1);
        }
        if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
        if (DBG) logdl("[addSubInfoRecord] carrier name = " + simCarrierName);

        ContentResolver resolver = mContext.getContentResolver();
        Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
                new String[] {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
                SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
                SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null);

        int color = getUnusedColor();

        try {
            if (cursor == null || !cursor.moveToFirst()) {
                ContentValues value = new ContentValues();
                value.put(SubscriptionManager.ICC_ID, iccId);
                // default SIM color differs between slots
                value.put(SubscriptionManager.COLOR, color);
                value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
                value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
                value.put(SubscriptionManager.CARRIER_NAME,
                        !TextUtils.isEmpty(simCarrierName) ? simCarrierName :
                        mContext.getString(com.android.internal.R.string.unknownName));
                Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
                if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
            } else {
                int subId = cursor.getInt(0);
                int oldSimInfoId = cursor.getInt(1);
                int nameSource = cursor.getInt(2);
                ContentValues value = new ContentValues();

                if (slotId != oldSimInfoId) {
                    value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
                }

                if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
                    value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
                }

                if (!TextUtils.isEmpty(simCarrierName)) {
                    value.put(SubscriptionManager.CARRIER_NAME, simCarrierName);
                }

                if (value.size() > 0) {
                    resolver.update(SubscriptionManager.CONTENT_URI, value,
                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
                            "=" + Long.toString(subId), null);
                }

                if (DBG) logdl("[addSubInfoRecord] Record already exists");
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
                SubscriptionManager.SIM_SLOT_INDEX + "=?",
                new String[] {String.valueOf(slotId)}, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {
                do {
                    int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
                    // If mSlotIdToSubIdMap already has a valid subId for a slotId/phoneId,
                    // do not add another subId for same slotId/phoneId.
                    Integer currentSubId = mSlotIdxToSubId.get(slotId);
                    if (currentSubId == null
                            || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
                        // TODO While two subs active, if user deactivats first
                        // one, need to update the default subId with second one.

                        // FIXME: Currently we assume phoneId == slotId which in the future
                        // may not be true, for instance with multiple subs per slot.
                        // But is true at the moment.
                        mSlotIdxToSubId.put(slotId, subId);
                        int subIdCountMax = getActiveSubInfoCountMax();
                        int defaultSubId = getDefaultSubId();
                        if (DBG) {
                            logdl("[addSubInfoRecord]"
                                + " mSlotIdxToSubId.size=" + mSlotIdxToSubId.size()
                                + " slotId=" + slotId + " subId=" + subId
                                + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
                        }

                        // Set the default sub if not set or if single sim device
                        if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
                                || subIdCountMax == 1) {
                            setDefaultFallbackSubId(subId);
                        }
                        // If single sim device, set this subscription as the default for everything
                        if (subIdCountMax == 1) {
                            if (DBG) {
                                logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
                            }
                            setDefaultDataSubId(subId);
                            setDefaultSmsSubId(subId);
                            setDefaultVoiceSubId(subId);
                        }
                    } else {
                        if (DBG) {
                            logdl("[addSubInfoRecord] currentSubId != null"
                                + " && currentSubId is valid, IGNORE");
                        }
                    }
                    if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
                } while (cursor.moveToNext());
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        // Once the records are loaded, notify DcTracker
        updateAllDataConnectionTrackers();

        if (DBG) logdl("[addSubInfoRecord]- info size=" + mSlotIdxToSubId.size());
        return 0;
    
private voidbroadcastDefaultDataSubIdChanged(int subId)

        // Broadcast an Intent for default data sub change
        if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    
private voidbroadcastDefaultSmsSubIdChanged(int subId)

        // Broadcast an Intent for default sms sub change
        if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    
private voidbroadcastDefaultVoiceSubIdChanged(int subId)

        // Broadcast an Intent for default voice sub change
        if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    
private voidbroadcastSimInfoContentChanged()
Broadcast when SubscriptionInfo has changed FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener

        Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
        mContext.sendBroadcast(intent);
        intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
        mContext.sendBroadcast(intent);
     
private booleancheckNotifyPermission(java.lang.String method)

         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
                     == PackageManager.PERMISSION_GRANTED) {
             return true;
         }
         if (DBG) {
             logd("checkNotifyPermission Permission Denial: " + method + " from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
         }
         return false;
     
public voidclearDefaultsForInactiveSubIds()

        final List<SubscriptionInfo> records = getActiveSubscriptionInfoList();
        if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records);
        if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
            if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
            setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        }
        if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
            if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
            setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        }
        if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
            if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
            setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        }
    
public intclearSubInfo()

return
the number of records cleared

        enforceSubscriptionPermission();
        if (DBG) logd("[clearSubInfo]+");

        int size = mSlotIdxToSubId.size();

        if (size == 0) {
            if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
            return 0;
        }

        mSlotIdxToSubId.clear();
        if (DBG) logdl("[clearSubInfo]- clear size=" + size);
        return size;
    
public voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
                "Requires DUMP");
        final long token = Binder.clearCallingIdentity();
        try {
            pw.println("SubscriptionController:");
            pw.println(" defaultSubId=" + getDefaultSubId());
            pw.println(" defaultDataSubId=" + getDefaultDataSubId());
            pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
            pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());

            pw.println(" defaultDataPhoneId=" + SubscriptionManager
                    .from(mContext).getDefaultDataPhoneId());
            pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
            pw.println(" defaultSmsPhoneId=" + SubscriptionManager
                    .from(mContext).getDefaultSmsPhoneId());
            pw.flush();

            for (Entry<Integer, Integer> entry : mSlotIdxToSubId.entrySet()) {
                pw.println(" mSlotIdToSubIdMap[" + entry.getKey() + "]: subId=" + entry.getValue());
            }
            pw.flush();
            pw.println("++++++++++++++++++++++++++++++++");

            List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList();
            if (sirl != null) {
                pw.println(" ActiveSubInfoList:");
                for (SubscriptionInfo entry : sirl) {
                    pw.println("  " + entry.toString());
                }
            } else {
                pw.println(" ActiveSubInfoList: is null");
            }
            pw.flush();
            pw.println("++++++++++++++++++++++++++++++++");

            sirl = getAllSubInfoList();
            if (sirl != null) {
                pw.println(" AllSubInfoList:");
                for (SubscriptionInfo entry : sirl) {
                    pw.println("  " + entry.toString());
                }
            } else {
                pw.println(" AllSubInfoList: is null");
            }
            pw.flush();
            pw.println("++++++++++++++++++++++++++++++++");

            mLocalLog.dump(fd, pw, args);
            pw.flush();
            pw.println("++++++++++++++++++++++++++++++++");
            pw.flush();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    
private voidenforceSubscriptionPermission()
Make sure the caller has the READ_PHONE_STATE permission.

throws
SecurityException if the caller does not have the required permission

        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
                "Requires READ_PHONE_STATE");
    
public int[]getActiveSubIdList()

return
the list of subId's that are active, is never null but the length maybe 0.

        Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet();
        if (DBG) logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet);

        int[] subIdArr = new int[simInfoSet.size()];
        int i = 0;
        for (Entry<Integer, Integer> entry: simInfoSet) {
            int sub = entry.getValue();
            subIdArr[i] = sub;
            i++;
        }

        if (DBG) logdl("[getActiveSubIdList] X subIdArr.length=" + subIdArr.length);
        return subIdArr;
    
public intgetActiveSubInfoCount()
Get the SUB count of active SUB(s)

return
active SIM count

        if (DBG) logd("[getActiveSubInfoCount]+");
        List<SubscriptionInfo> records = getActiveSubscriptionInfoList();
        if (records == null) {
            if (DBG) logd("[getActiveSubInfoCount] records null");
            return 0;
        }
        if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
        return records.size();
    
public intgetActiveSubInfoCountMax()

return
the maximum number of subscriptions this device will support at any one time.

        // FIXME: This valid now but change to use TelephonyDevController in the future
        return mTelephonyManager.getSimCount();
    
public android.telephony.SubscriptionInfogetActiveSubscriptionInfo(int subId)
Get the active SubscriptionInfo with the subId key

param
subId The unique SubscriptionInfo key in database
return
SubscriptionInfo, maybe null if its not active

        enforceSubscriptionPermission();

        List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
        if (subList != null) {
            for (SubscriptionInfo si : subList) {
                if (si.getSubscriptionId() == subId) {
                    if (DBG) logd("[getActiveSubInfoForSubscriber]+ subId=" + subId + " subInfo=" + si);
                    return si;
                }
            }
        }
        if (DBG) {
            logd("[getActiveSubInfoForSubscriber]- subId=" + subId
                    + " subList=" + subList + " subInfo=null");
        }
        return null;
    
public android.telephony.SubscriptionInfogetActiveSubscriptionInfoForIccId(java.lang.String iccId)
Get the active SubscriptionInfo associated with the iccId

param
iccId the IccId of SIM card
return
SubscriptionInfo, maybe null if its not active

        enforceSubscriptionPermission();

        List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
        if (subList != null) {
            for (SubscriptionInfo si : subList) {
                if (si.getIccId() == iccId) {
                    if (DBG) logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
                    return si;
                }
            }
        }
        if (DBG) {
            logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
                    + " subList=" + subList + " subInfo=null");
        }
        return null;
    
public android.telephony.SubscriptionInfogetActiveSubscriptionInfoForSimSlotIndex(int slotIdx)
Get the active SubscriptionInfo associated with the slotIdx

param
slotIdx the slot which the subscription is inserted
return
SubscriptionInfo, maybe null if its not active

        enforceSubscriptionPermission();

        List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
        if (subList != null) {
            for (SubscriptionInfo si : subList) {
                if (si.getSimSlotIndex() == slotIdx) {
                    if (DBG) {
                        logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
                            + " subId=" + si);
                    }
                    return si;
                }
            }
            if (DBG) {
                logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
                    + " subId=null");
            }
        } else {
            if (DBG) {
                logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
            }
        }
        return null;
    
public java.util.ListgetActiveSubscriptionInfoList()
Get the SubInfoRecord(s) of the currently inserted SIM(s)

return
Array list of currently inserted SubInfoRecord(s)

        enforceSubscriptionPermission();
        if (DBG) logdl("[getActiveSubInfoList]+");

        List<SubscriptionInfo> subList = null;

        if (!isSubInfoReady()) {
            if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
            return subList;
        }

        subList = getSubInfo(SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
        if (subList != null) {
            // FIXME: Unnecessary when an insertion sort is used!
            Collections.sort(subList, new Comparator<SubscriptionInfo>() {
                @Override
                public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) {
                    // Primary sort key on SimSlotIndex
                    int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
                    if (flag == 0) {
                        // Secondary sort on SubscriptionId
                        return arg0.getSubscriptionId() - arg1.getSubscriptionId();
                    }
                    return flag;
                }
            });

            if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
        } else {
            if (DBG) logdl("[getActiveSubInfoList]- no info return");
        }

        return subList;
    
public intgetAllSubInfoCount()
Get the SUB count of all SUB(s) in SubscriptoinInfo database

return
all SIM count in database, include what was inserted before

        if (DBG) logd("[getAllSubInfoCount]+");
        enforceSubscriptionPermission();

        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
                null, null, null, null);
        try {
            if (cursor != null) {
                int count = cursor.getCount();
                if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
                return count;
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");

        return 0;
    
public java.util.ListgetAllSubInfoList()

return
List of all SubscriptionInfo records in database, include those that were inserted before, maybe empty but not null.
hide

        if (DBG) logd("[getAllSubInfoList]+");
        enforceSubscriptionPermission();

        List<SubscriptionInfo> subList = null;
        subList = getSubInfo(null, null);
        if (subList != null) {
            if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
        } else {
            if (DBG) logd("[getAllSubInfoList]- no info return");
        }

        return subList;
    
public intgetDefaultDataSubId()

        int subId = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        if (VDBG) logd("[getDefaultDataSubId] subId= " + subId);
        return subId;
    
public intgetDefaultSmsSubId()

        int subId = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId);
        return subId;
    
public intgetDefaultSubId()

        int subId;
        boolean isVoiceCapable = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_voice_capable);
        if (isVoiceCapable) {
            subId = getDefaultVoiceSubId();
            if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId);
        } else {
            subId = getDefaultDataSubId();
            if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId);
        }
        if ( ! isActiveSubId(subId)) {
            subId = mDefaultFallbackSubId;
            if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId);
        }
        if (VDBG) logv("[getDefaultSubId]- value = " + subId);
        return subId;
    
public intgetDefaultVoiceSubId()

        int subId = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
        return subId;
    
private int[]getDummySubIds(int slotIdx)

        // FIXME: Remove notion of Dummy SUBSCRIPTION_ID.
        // I tested this returning null as no one appears to care,
        // but no connection came up on sprout with two sims.
        // We need to figure out why and hopefully remove DummySubsIds!!!
        int numSubs = getActiveSubInfoCountMax();
        if (numSubs > 0) {
            int[] dummyValues = new int[numSubs];
            for (int i = 0; i < numSubs; i++) {
                dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx;
            }
            if (DBG) {
                logd("getDummySubIds: slotIdx=" + slotIdx
                    + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]);
            }
            return dummyValues;
        } else {
            return null;
        }
    
public static com.android.internal.telephony.SubscriptionControllergetInstance()

        if (sInstance == null)
        {
           Log.wtf(LOG_TAG, "getInstance null");
        }

        return sInstance;
    
public intgetPhoneId(int subId)

        if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
        int phoneId;

        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            subId = getDefaultSubId();
            if (DBG) logdl("[getPhoneId] asked for default subId=" + subId);
        }

        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            if (DBG) {
                logdl("[getPhoneId]- invalid subId return="
                        + SubscriptionManager.INVALID_PHONE_INDEX);
            }
            return SubscriptionManager.INVALID_PHONE_INDEX;
        }

        int size = mSlotIdxToSubId.size();
        if (size == 0) {
            phoneId = mDefaultPhoneId;
            if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
            return phoneId;
        }

        // FIXME: Assumes phoneId == slotId
        for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
            int sim = entry.getKey();
            int sub = entry.getValue();

            if (subId == sub) {
                if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
                return sim;
            }
        }

        phoneId = mDefaultPhoneId;
        if (DBG) {
            logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId);
        }
        return phoneId;

    
public intgetSimStateForSubscriber(int subId)
Get the SIM state for the subscriber

return
SIM state as the ordinal of {@See IccCardConstants.State}

        State simState;
        String err;
        int phoneIdx = getPhoneId(subId);
        if (phoneIdx < 0) {
            simState = IccCardConstants.State.UNKNOWN;
            err = "invalid PhoneIdx";
        } else {
            Phone phone = PhoneFactory.getPhone(phoneIdx);
            if (phone == null) {
                simState = IccCardConstants.State.UNKNOWN;
                err = "phone == null";
            } else {
                IccCard icc = phone.getIccCard();
                if (icc == null) {
                    simState = IccCardConstants.State.UNKNOWN;
                    err = "icc == null";
                } else {
                    simState = icc.getState();
                    err = "";
                }
            }
        }
        if (DBG) logd("getSimStateForSubscriber: " + err + " simState=" + simState
                + " ordinal=" + simState.ordinal());
        return simState.ordinal();
    
public intgetSlotId(int subId)

        if (VDBG) printStackTrace("[getSlotId] subId=" + subId);

        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            subId = getDefaultSubId();
        }
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            if (DBG) logd("[getSlotId]- subId invalid");
            return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
        }

        int size = mSlotIdxToSubId.size();

        if (size == 0)
        {
            if (DBG) logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead");
            return SubscriptionManager.SIM_NOT_INSERTED;
        }

        for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
            int sim = entry.getKey();
            int sub = entry.getValue();

            if (subId == sub)
            {
                if (VDBG) logv("[getSlotId]- return = " + sim);
                return sim;
            }
        }

        if (DBG) logd("[getSlotId]- return fail");
        return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
    
public int[]getSubId(int slotIdx)
Return the subId for specified slot Id.

deprecated

        if (VDBG) printStackTrace("[getSubId]+ slotIdx=" + slotIdx);

        // Map default slotIdx to the current default subId.
        // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous
        // as a slot maybe used for multiple different type of "connections"
        // such as: voice, data and sms. But we're doing the best we can and using
        // getDefaultSubId which makes a best guess.
        if (slotIdx == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
            slotIdx = getSlotId(getDefaultSubId());
            if (DBG) logd("[getSubId] map default slotIdx=" + slotIdx);
        }

        // Check that we have a valid SlotIdx
        if (!SubscriptionManager.isValidSlotId(slotIdx)) {
            if (DBG) logd("[getSubId]- invalid slotIdx=" + slotIdx);
            return null;
        }

        // Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate.
        int size = mSlotIdxToSubId.size();
        if (size == 0) {
            if (DBG) {
                logd("[getSubId]- mSlotIdToSubIdMap.size == 0, return DummySubIds slotIdx="
                        + slotIdx);
            }
            return getDummySubIds(slotIdx);
        }

        // Create an array of subIds that are in this slot?
        ArrayList<Integer> subIds = new ArrayList<Integer>();
        for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
            int slot = entry.getKey();
            int sub = entry.getValue();
            if (slotIdx == slot) {
                subIds.add(sub);
            }
        }

        // Convert ArrayList to array
        int numSubIds = subIds.size();
        if (numSubIds > 0) {
            int[] subIdArr = new int[numSubIds];
            for (int i = 0; i < numSubIds; i++) {
                subIdArr[i] = subIds.get(i);
            }
            if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr);
            return subIdArr;
        } else {
            if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIdx=" + slotIdx);
            return getDummySubIds(slotIdx);
        }
    
public intgetSubIdUsingPhoneId(int phoneId)

        int[] subIds = getSubId(phoneId);
        if (subIds == null || subIds.length == 0) {
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        }
        return subIds[0];
    
public int[]getSubIdUsingSlotId(int slotId)

        return getSubId(slotId);
    
private java.util.ListgetSubInfo(java.lang.String selection, java.lang.Object queryKey)
Query SubInfoRecord(s) from subinfo database

param
selection A filter declaring which rows to return
param
queryKey query key content
return
Array list of queried result from database

        if (DBG) logd("selection:" + selection + " " + queryKey);
        String[] selectionArgs = null;
        if (queryKey != null) {
            selectionArgs = new String[] {queryKey.toString()};
        }
        ArrayList<SubscriptionInfo> subList = null;
        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
                null, selection, selectionArgs, null);
        try {
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    SubscriptionInfo subInfo = getSubInfoRecord(cursor);
                    if (subInfo != null)
                    {
                        if (subList == null)
                        {
                            subList = new ArrayList<SubscriptionInfo>();
                        }
                        subList.add(subInfo);
                }
                }
            } else {
                if (DBG) logd("Query fail");
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        return subList;
    
private android.telephony.SubscriptionInfogetSubInfoRecord(android.database.Cursor cursor)
New SubInfoRecord instance and fill in detail info

param
cursor
return
the query result of desired SubInfoRecord

        int id = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
        String iccId = cursor.getString(cursor.getColumnIndexOrThrow(
                SubscriptionManager.ICC_ID));
        int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.SIM_SLOT_INDEX));
        String displayName = cursor.getString(cursor.getColumnIndexOrThrow(
                SubscriptionManager.DISPLAY_NAME));
        String carrierName = cursor.getString(cursor.getColumnIndexOrThrow(
                SubscriptionManager.CARRIER_NAME));
        int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.NAME_SOURCE));
        int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.COLOR));
        String number = cursor.getString(cursor.getColumnIndexOrThrow(
                SubscriptionManager.NUMBER));
        int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.DATA_ROAMING));
        // Get the blank bitmap for this SubInfoRecord
        Bitmap iconBitmap = BitmapFactory.decodeResource(mContext.getResources(),
                com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
        int mcc = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.MCC));
        int mnc = cursor.getInt(cursor.getColumnIndexOrThrow(
                SubscriptionManager.MNC));
        // FIXME: consider stick this into database too
        String countryIso = getSubscriptionCountryIso(id);

        if (DBG) {
            logd("[getSubInfoRecord] id:" + id + " iccid:" + iccId + " simSlotIndex:" + simSlotIndex
                + " displayName:" + displayName + " nameSource:" + nameSource
                + " iconTint:" + iconTint + " dataRoaming:" + dataRoaming
                + " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso);
        }

        String line1Number = mTelephonyManager.getLine1NumberForSubscriber(id);
        if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
            logd("Line1Number is different: " + line1Number);
            number = line1Number;
        }
        return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso);
    
public java.util.ListgetSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck)

        if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
        enforceSubscriptionPermission();

        if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
            slotId = getSlotId(getDefaultSubId());
        }
        if (!SubscriptionManager.isValidSlotId(slotId)) {
            if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
            return null;
        }

        if (needCheck && !isSubInfoReady()) {
            if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
            return null;
        }

        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
                null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
                new String[] {String.valueOf(slotId)}, null);
        ArrayList<SubscriptionInfo> subList = null;
        try {
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    SubscriptionInfo subInfo = getSubInfoRecord(cursor);
                    if (subInfo != null)
                    {
                        if (subList == null)
                        {
                            subList = new ArrayList<SubscriptionInfo>();
                        }
                        subList.add(subInfo);
                    }
                }
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        if (DBG) logd("[getSubInfoUsingSlotId]- null info return");

        return subList;
    
private java.lang.StringgetSubscriptionCountryIso(int subId)
Get ISO country code for the subscription's provider

param
subId The subscription ID
return
The ISO country code for the subscription's provider

        final int phoneId = getPhoneId(subId);
        if (phoneId < 0) {
            return "";
        }
        return mTelephonyManager.getSimCountryIsoForPhone(phoneId);
    
private intgetUnusedColor()
Find unused color to be set for new SubInfoRecord

return
RGB integer value of color

        List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList();
        colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
        int colorIdx = 0;

        if (availableSubInfos != null) {
            for (int i = 0; i < colorArr.length; i++) {
                int j;
                for (j = 0; j < availableSubInfos.size(); j++) {
                    if (colorArr[i] == availableSubInfos.get(j).getIconTint()) {
                        break;
                    }
                }
                if (j == availableSubInfos.size()) {
                    return colorArr[i];
                }
            }
            colorIdx = availableSubInfos.size() % colorArr.length;
        }
        return colorArr[colorIdx];
    
public static com.android.internal.telephony.SubscriptionControllerinit(Phone phone)


         
        synchronized (SubscriptionController.class) {
            if (sInstance == null) {
                sInstance = new SubscriptionController(phone);
            } else {
                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
            }
            return sInstance;
        }
    
public static com.android.internal.telephony.SubscriptionControllerinit(android.content.Context c, CommandsInterface[] ci)

        synchronized (SubscriptionController.class) {
            if (sInstance == null) {
                sInstance = new SubscriptionController(c);
            } else {
                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
            }
            return sInstance;
        }
    
private booleanisActiveSubId(int subId)

        boolean retVal = false;

        if (SubscriptionManager.isValidSubscriptionId(subId)) {
            Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet();
            if (VDBG) logdl("[isActiveSubId] simInfoSet=" + simInfoSet);

            for (Entry<Integer, Integer> entry: simInfoSet) {
                if (subId == entry.getValue()) {
                    retVal = true;
                    break;
                }
            }
        }

        if (VDBG) logdl("[isActiveSubId]- " + retVal);
        return retVal;
    
private booleanisSubInfoReady()

        return mSlotIdxToSubId.size() > 0;
    
private voidlogd(java.lang.String msg)

        Rlog.d(LOG_TAG, msg);
    
private voidlogdl(java.lang.String msg)

        logd(msg);
        mLocalLog.log(msg);
    
private voidloge(java.lang.String msg)

        Rlog.e(LOG_TAG, msg);
    
private voidlogel(java.lang.String msg)

        loge(msg);
        mLocalLog.log(msg);
    
private voidlogv(java.lang.String msg)

        Rlog.v(LOG_TAG, msg);
    
private voidlogvl(java.lang.String msg)

        logv(msg);
        mLocalLog.log(msg);
    
public voidnotifySubscriptionInfoChanged()

         if (!checkNotifyPermission("notifySubscriptionInfoChanged")) {
             return;
         }
         ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                 "telephony.registry"));
         try {
             if (DBG) logd("notifySubscriptionInfoChanged:");
             tr.notifySubscriptionInfoChanged();
         } catch (RemoteException ex) {
             // Should never happen because its always available.
         }

         // FIXME: Remove if listener technique accepted.
         broadcastSimInfoContentChanged();
     
private static voidprintStackTrace(java.lang.String msg)

        RuntimeException re = new RuntimeException();
        slogd("StackTrace - " + msg);
        StackTraceElement[] st = re.getStackTrace();
        boolean first = true;
        for (StackTraceElement ste : st) {
            if (first) {
                first = false;
            } else {
                slogd(ste.toString());
            }
        }
    
private intsetCarrierText(java.lang.String text, int subId)
Set carrier text by simInfo index

param
text new carrier text
param
subId the unique SubInfoRecord index in database
return
the number of records updated

        if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);
        enforceSubscriptionPermission();

        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.CARRIER_NAME, text);

        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
        notifySubscriptionInfoChanged();

        return result;
    
public intsetDataRoaming(int roaming, int subId)
Set data roaming by simInfo index

param
roaming 0:Don't allow data when roaming, 1:Allow data when roaming
param
subId the unique SubInfoRecord index in database
return
the number of records updated

        if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
        enforceSubscriptionPermission();

        validateSubId(subId);
        if (roaming < 0) {
            if (DBG) logd("[setDataRoaming]- fail");
            return -1;
        }
        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.DATA_ROAMING, roaming);
        if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");

        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
        notifySubscriptionInfoChanged();

        return result;
    
public voidsetDefaultDataSubId(int subId)

        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
        }
        if (DBG) logdl("[setDefaultDataSubId] subId=" + subId);

        int len = sProxyPhones.length;
        logdl("[setDefaultDataSubId] num phones=" + len);

        RadioAccessFamily[] rafs = new RadioAccessFamily[len];
        for (int phoneId = 0; phoneId < len; phoneId++) {
            PhoneProxy phone = sProxyPhones[phoneId];
            int raf = phone.getRadioAccessFamily();
            int id = phone.getSubId();
            logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf);
            raf |= RadioAccessFamily.RAF_GSM;
            if (id == subId) {
                raf |= RadioAccessFamily.RAF_UMTS;
            } else {
                raf &= ~RadioAccessFamily.RAF_UMTS;
            }
            logdl("[setDefaultDataSubId] reqRAF=" + raf);

            // Set the raf to the maximum of the requested and the user's preferred.
            int networkType = PhoneFactory.calculatePreferredNetworkType(mContext, id);
            logdl("[setDefaultDataSubId] networkType=" + networkType);
            raf &= RadioAccessFamily.getRafFromNetworkType(networkType);

            logdl("[setDefaultDataSubId] newRAF=" + raf);
            rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
        }
        ProxyController.getInstance().setRadioCapability(rafs);

        // FIXME is this still needed?
        updateAllDataConnectionTrackers();

        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
        broadcastDefaultDataSubIdChanged(subId);
    
private voidsetDefaultFallbackSubId(int subId)

        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
        }
        if (DBG) logdl("[setDefaultFallbackSubId] subId=" + subId);
        if (SubscriptionManager.isValidSubscriptionId(subId)) {
            int phoneId = getPhoneId(subId);
            if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount()
                    || mTelephonyManager.getSimCount() == 1)) {
                if (DBG) logdl("[setDefaultFallbackSubId] set mDefaultFallbackSubId=" + subId);
                mDefaultFallbackSubId = subId;
                // Update MCC MNC device configuration information
                String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId);
                MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);

                // Broadcast an Intent for default sub change
                Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
                SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
                if (DBG) {
                    logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" + phoneId
                            + " subId=" + subId);
                }
                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
            } else {
                if (DBG) {
                    logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId
                            + " subId=" + subId);
                }
            }
        }
    
public voidsetDefaultSmsSubId(int subId)

        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
        }
        if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId);
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
        broadcastDefaultSmsSubIdChanged(subId);
    
public voidsetDefaultVoiceSubId(int subId)

        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
        }
        if (DBG) logdl("[setDefaultVoiceSubId] subId=" + subId);
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
        broadcastDefaultVoiceSubIdChanged(subId);
    
public intsetDisplayName(java.lang.String displayName, int subId)
Set display name by simInfo index

param
displayName the display name of SIM card
param
subId the unique SubInfoRecord index in database
return
the number of records updated

        return setDisplayNameUsingSrc(displayName, subId, -1);
    
public intsetDisplayNameUsingSrc(java.lang.String displayName, int subId, long nameSource)
Set display name by simInfo index with name source

param
displayName the display name of SIM card
param
subId the unique SubInfoRecord index in database
param
nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
return
the number of records updated

        if (DBG) {
            logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
                + " nameSource:" + nameSource);
        }
        enforceSubscriptionPermission();

        validateSubId(subId);
        String nameToSet;
        if (displayName == null) {
            nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
        } else {
            nameToSet = displayName;
        }
        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
        if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
            if (DBG) logd("Set nameSource=" + nameSource);
            value.put(SubscriptionManager.NAME_SOURCE, nameSource);
        }
        if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");

        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
        notifySubscriptionInfoChanged();

        return result;
    
public intsetDisplayNumber(java.lang.String number, int subId)
Set phone number by subId

param
number the phone number of the SIM
param
subId the unique SubInfoRecord index in database
return
the number of records updated

        if (DBG) logd("[setDisplayNumber]+ subId:" + subId);
        enforceSubscriptionPermission();

        validateSubId(subId);
        int result;
        int phoneId = getPhoneId(subId);

        if (number == null || phoneId < 0 ||
                phoneId >= mTelephonyManager.getPhoneCount()) {
            if (DBG) logd("[setDispalyNumber]- fail");
            return -1;
        }
        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.NUMBER, number);

        // This function had a call to update number on the SIM (Phone.setLine1Number()) but that
        // was removed as there doesn't seem to be a reason for that. If it is added back, watch out
        // for deadlocks.

        result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
                    + "=" + Long.toString(subId), null);
        if (DBG) logd("[setDisplayNumber]- update result :" + result);
        notifySubscriptionInfoChanged();

        return result;
    
public intsetIconTint(int tint, int subId)
Set SIM color tint by simInfo index

param
tint the tint color of the SIM
param
subId the unique SubInfoRecord index in database
return
the number of records updated

        if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
        enforceSubscriptionPermission();

        validateSubId(subId);
        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.COLOR, tint);
        if (DBG) logd("[setIconTint]- tint:" + tint + " set");

        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
        notifySubscriptionInfoChanged();

        return result;
    
public intsetMccMnc(java.lang.String mccMnc, int subId)
Set MCC/MNC by subscription ID

param
mccMnc MCC/MNC associated with the subscription
param
subId the unique SubInfoRecord index in database
return
the number of records updated

        int mcc = 0;
        int mnc = 0;
        try {
            mcc = Integer.parseInt(mccMnc.substring(0,3));
            mnc = Integer.parseInt(mccMnc.substring(3));
        } catch (NumberFormatException e) {
            loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
        }
        if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
        ContentValues value = new ContentValues(2);
        value.put(SubscriptionManager.MCC, mcc);
        value.put(SubscriptionManager.MNC, mnc);

        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
        notifySubscriptionInfoChanged();

        return result;
    
public booleansetPlmnSpn(int slotId, boolean showPlmn, java.lang.String plmn, boolean showSpn, java.lang.String spn)
Generate and set carrier text based on input parameters

param
showPlmn flag to indicate if plmn should be included in carrier text
param
plmn plmn to be included in carrier text
param
showSpn flag to indicate if spn should be included in carrier text
param
spn spn to be included in carrier text
return
true if carrier text is set, false otherwise

        synchronized (mLock) {
            int[] subIds = getSubId(slotId);
            if (mContext.getPackageManager().resolveContentProvider(
                    SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||
                    subIds == null ||
                    !SubscriptionManager.isValidSubscriptionId(subIds[0])) {
                // No place to store this info, we are done.
                // TODO: This can be removed once SubscriptionController is not running on devices
                // that don't need it, such as TVs.
                return false;
            }
            String carrierText = "";
            if (showPlmn) {
                carrierText = plmn;
                if (showSpn) {
                    // Need to show both plmn and spn.
                    String separator = mContext.getString(
                            com.android.internal.R.string.kg_text_message_separator).toString();
                    carrierText = new StringBuilder().append(carrierText).append(separator)
                            .append(spn).toString();
                }
            } else if (showSpn) {
                carrierText = spn;
            }
            for (int i = 0; i < subIds.length; i++) {
                setCarrierText(carrierText, subIds[i]);
            }
            return true;
        }
    
private booleanshouldDefaultBeCleared(java.util.List records, int subId)

        if (DBG) logdl("[shouldDefaultBeCleared: subId] " + subId);
        if (records == null) {
            if (DBG) logdl("[shouldDefaultBeCleared] return true no records subId=" + subId);
            return true;
        }
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            // If the subId parameter is not valid its already cleared so return false.
            if (DBG) logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId);
            return false;
        }
        for (SubscriptionInfo record : records) {
            int id = record.getSubscriptionId();
            if (DBG) logdl("[shouldDefaultBeCleared] Record.id: " + id);
            if (id == subId) {
                logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId);
                return false;
            }
        }
        if (DBG) logdl("[shouldDefaultBeCleared] return true not active subId=" + subId);
        return true;
    
private static voidslogd(java.lang.String msg)

        Rlog.d(LOG_TAG, msg);
    
private voidupdateAllDataConnectionTrackers()

        // Tell Phone Proxies to update data connection tracker
        int len = sProxyPhones.length;
        if (DBG) logdl("[updateAllDataConnectionTrackers] sProxyPhones.length=" + len);
        for (int phoneId = 0; phoneId < len; phoneId++) {
            if (DBG) logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId);
            sProxyPhones[phoneId].updateDataConnectionTracker();
        }
    
public voidupdatePhonesAvailability(PhoneProxy[] phones)

        sProxyPhones = phones;
    
private voidvalidateSubId(int subId)

        if (DBG) logd("validateSubId subId: " + subId);
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new RuntimeException("Invalid sub id passed as parameter");
        } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
            throw new RuntimeException("Default sub id passed as parameter");
        }