| /* |
| * Copyright (C) 2011-2014 MediaTek Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import android.Manifest; |
| import android.app.AppOpsManager; |
| import android.app.PendingIntent; |
| import android.content.Context; |
| import android.os.AsyncResult; |
| import android.os.Binder; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.ServiceManager; |
| import android.os.UserHandle; |
| import android.telephony.Rlog; |
| import android.util.Log; |
| import android.net.Uri; |
| import android.database.Cursor; |
| import android.content.Intent; |
| import android.provider.BaseColumns; |
| import android.provider.Settings; |
| import android.provider.Settings.SettingNotFoundException; |
| import android.content.ContentResolver; |
| import android.content.ContentValues; |
| |
| import com.android.internal.telephony.ISub; |
| import com.android.internal.telephony.dataconnection.DctController; |
| import com.android.internal.telephony.uicc.IccConstants; |
| import com.android.internal.telephony.uicc.IccFileHandler; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.SubInfoRecord; |
| import android.telephony.TelephonyManager; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map.Entry; |
| /** |
| * SubscriptionController to provide an inter-process communication to |
| * access Sms in Icc. |
| */ |
| public class SubscriptionController extends ISub.Stub { |
| static final String LOG_TAG = "SUB"; |
| static final boolean DBG = true; |
| static final boolean VDBG = false; |
| |
| protected final Object mLock = new Object(); |
| protected boolean mSuccess; |
| |
| /** The singleton instance. */ |
| private static SubscriptionController sInstance = null; |
| protected static Phone mPhone; |
| protected static Context mContext; |
| |
| private DataConnectionHandler mDataConnectionHandler; |
| |
| public static final Uri CONTENT_URI = |
| Uri.parse("content://telephony/siminfo"); |
| |
| public static final int DEFAULT_INT_VALUE = -100; |
| |
| public static final String DEFAULT_STRING_VALUE = "N/A"; |
| |
| private static final int EVENT_SET_DEFAULT_DATA_DONE = 1; |
| |
| public static final int EXTRA_VALUE_NEW_SIM = 1; |
| public static final int EXTRA_VALUE_REMOVE_SIM = 2; |
| public static final int EXTRA_VALUE_REPOSITION_SIM = 3; |
| public static final int EXTRA_VALUE_NOCHANGE = 4; |
| |
| public static final String INTENT_KEY_DETECT_STATUS = "simDetectStatus"; |
| public static final String INTENT_KEY_SIM_COUNT = "simCount"; |
| public static final String INTENT_KEY_NEW_SIM_SLOT = "newSIMSlot"; |
| public static final String INTENT_KEY_NEW_SIM_STATUS = "newSIMStatus"; |
| |
| /** |
| * The ICC ID of a SIM. |
| * <P>Type: TEXT (String)</P> |
| */ |
| public static final String ICC_ID = "icc_id"; |
| |
| /** |
| * <P>Type: INTEGER (int)</P> |
| */ |
| public static final String SIM_ID = "sim_id"; |
| |
| public static final int SIM_NOT_INSERTED = -1; |
| |
| /** |
| * The display name of a SIM. |
| * <P>Type: TEXT (String)</P> |
| */ |
| public static final String DISPLAY_NAME = "display_name"; |
| |
| public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName; |
| |
| /** |
| * The display name source of a SIM. |
| * <P>Type: INT (int)</P> |
| */ |
| public static final String NAME_SOURCE = "name_source"; |
| |
| public static final int DEFAULT_SOURCE = 0; |
| |
| public static final int SIM_SOURCE = 1; |
| |
| public static final int USER_INPUT = 2; |
| |
| /** |
| * The color of a SIM. |
| * <P>Type: INTEGER (int)</P> |
| */ |
| public static final String COLOR = "color"; |
| |
| public static final int COLOR_1 = 0; |
| |
| public static final int COLOR_2 = 1; |
| |
| public static final int COLOR_3 = 2; |
| |
| public static final int COLOR_4 = 3; |
| |
| public static final int COLOR_DEFAULT = COLOR_1; |
| |
| /** |
| * The phone number of a SIM. |
| * <P>Type: TEXT (String)</P> |
| */ |
| public static final String NUMBER = "number"; |
| |
| /** |
| * The number display format of a SIM. |
| * <P>Type: INTEGER (int)</P> |
| */ |
| public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; |
| |
| public static final int DISPALY_NUMBER_NONE = 0; |
| |
| public static final int DISPLAY_NUMBER_FIRST = 1; |
| |
| public static final int DISPLAY_NUMBER_LAST = 2; |
| |
| public static final int DISLPAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST; |
| |
| /** |
| * Permission for data roaming of a SIM. |
| * <P>Type: INTEGER (int)</P> |
| */ |
| public static final String DATA_ROAMING = "data_roaming"; |
| |
| public static final int DATA_ROAMING_ENABLE = 1; |
| |
| public static final int DATA_ROAMING_DISABLE = 0; |
| |
| public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; |
| |
| private static final int RES_TYPE_BACKGROUND_DARK = 0; |
| |
| private static final int RES_TYPE_BACKGROUND_LIGHT = 1; |
| |
| private static final int[] sSimBackgroundDarkRes = setSimResource(RES_TYPE_BACKGROUND_DARK); |
| |
| private static final int[] sSimBackgroundLightRes = setSimResource(RES_TYPE_BACKGROUND_LIGHT); |
| |
| private static HashMap<Integer, Long> mSimInfo = new HashMap<Integer, Long>(); |
| // FIXME define an invalid SUB_ID, and use that below instead of "1". |
| private static long mDefaultVoiceSubId = 1; |
| private static int mDefaultPhoneId = 0; |
| |
| public static SubscriptionController init(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 SubscriptionController init(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; |
| } |
| } |
| |
| public static SubscriptionController getInstance() { |
| if (sInstance == null) |
| { |
| Log.wtf(LOG_TAG, "getInstance null"); |
| } |
| |
| return sInstance; |
| } |
| |
| private SubscriptionController(Context c) { |
| mContext = c; |
| |
| if(ServiceManager.getService("isub") == null) { |
| ServiceManager.addService("isub", this); |
| } |
| |
| mDataConnectionHandler = new DataConnectionHandler(); |
| logd("SubscriptionController init by Context"); |
| } |
| |
| private boolean isSubInfoReady() { |
| return (mSimInfo.size() > 0) ? true : false; |
| } |
| |
| private SubscriptionController(Phone phone) { |
| mContext = phone.getContext(); |
| |
| if(ServiceManager.getService("isub") == null) { |
| ServiceManager.addService("isub", this); |
| } |
| |
| logd("SubscriptionController init by Phone"); |
| } |
| |
| /** |
| * Broadcast when subinfo settings has chanded |
| * @SubId The unique SubInfoRecord index in database |
| * @param columnName The column that is updated |
| * @param intContent The updated integer value |
| * @param stringContent The updated string value |
| */ |
| private void broadcastSimInfoContentChanged(long subId, |
| String columnName, int intContent, String stringContent) { |
| Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE); |
| intent.putExtra(BaseColumns._ID, subId); |
| intent.putExtra(TelephonyIntents.EXTRA_COLUMN_NAME, columnName); |
| intent.putExtra(TelephonyIntents.EXTRA_INT_CONTENT, intContent); |
| intent.putExtra(TelephonyIntents.EXTRA_STRING_CONTENT, stringContent); |
| if (intContent != DEFAULT_INT_VALUE) { |
| logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + intContent); |
| } else { |
| logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + stringContent); |
| } |
| mContext.sendBroadcast(intent); |
| } |
| |
| |
| /** |
| * New SubInfoRecord instance and fill in detail info |
| * @param cursor |
| * @return the query result of desired SubInfoRecord |
| */ |
| private SubInfoRecord getSubInfoRecord(Cursor cursor) { |
| SubInfoRecord info = new SubInfoRecord(); |
| info.mSubId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); |
| info.mIccId = cursor.getString(cursor.getColumnIndexOrThrow(ICC_ID)); |
| info.mSlotId = cursor.getInt(cursor.getColumnIndexOrThrow(SIM_ID)); |
| info.mDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME)); |
| info.mNameSource = cursor.getInt(cursor.getColumnIndexOrThrow(NAME_SOURCE)); |
| info.mColor = cursor.getInt(cursor.getColumnIndexOrThrow(COLOR)); |
| info.mNumber = cursor.getString(cursor.getColumnIndexOrThrow(NUMBER)); |
| info.mDispalyNumberFormat = cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_NUMBER_FORMAT)); |
| info.mDataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(DATA_ROAMING)); |
| |
| int size = sSimBackgroundDarkRes.length; |
| if (info.mColor >= 0 && info.mColor < size) { |
| info.mSimIconRes[RES_TYPE_BACKGROUND_DARK] = sSimBackgroundDarkRes[info.mColor]; |
| info.mSimIconRes[RES_TYPE_BACKGROUND_LIGHT] = sSimBackgroundLightRes[info.mColor]; |
| } |
| logd("[getSubInfoRecord] SubId:" + info.mSubId + " iccid:" + info.mIccId + " slotId:" + info.mSlotId |
| + " displayName:" + info.mDisplayName + " color:" + info.mColor); |
| |
| return info; |
| } |
| |
| /** |
| * 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 |
| */ |
| private List<SubInfoRecord> getSubInfo(String selection, Object queryKey) { |
| logd("selection:" + selection + " " + queryKey); |
| String[] selectionArgs = null; |
| if (queryKey != null) { |
| selectionArgs = new String[] {queryKey.toString()}; |
| } |
| ArrayList<SubInfoRecord> subList = null; |
| Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, |
| null, selection, selectionArgs, null); |
| try { |
| if (cursor != null) { |
| while (cursor.moveToNext()) { |
| SubInfoRecord subInfo = getSubInfoRecord(cursor); |
| if (subInfo != null) |
| { |
| if (subList == null) |
| { |
| subList = new ArrayList<SubInfoRecord>(); |
| } |
| subList.add(subInfo); |
| } |
| } |
| } else { |
| logd("Query fail"); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| return subList; |
| } |
| |
| |
| |
| /** |
| * Get the SubInfoRecord according to an index |
| * @param subId The unique SubInfoRecord index in database |
| * @return SubInfoRecord, maybe null |
| */ |
| @Override |
| public SubInfoRecord getSubInfoUsingSubId(long subId) { |
| logd("[getSubInfoUsingSubIdx]+ subId:" + subId); |
| if (subId <= 0 || !isSubInfoReady()) { |
| logd("[getSubInfoUsingSubIdx]- subId <= 0 or not ready"); |
| return null; |
| } |
| Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, |
| null, BaseColumns._ID + "=?", new String[] {Long.toString(subId)}, null); |
| try { |
| if (cursor != null) { |
| if (cursor.moveToFirst()) { |
| logd("[getSubInfoUsingSubIdx]- Info detail:"); |
| return getSubInfoRecord(cursor); |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| logd("[getSubInfoUsingSubIdx]- null info return"); |
| |
| return null; |
| } |
| |
| /** |
| * Get the SubInfoRecord according to an IccId |
| * @param iccId the IccId of SIM card |
| * @return SubInfoRecord, maybe null |
| */ |
| @Override |
| public List<SubInfoRecord> getSubInfoUsingIccId(String iccId) { |
| logd("[getSubInfoUsingIccId]+ iccId:" + iccId); |
| if (iccId == null || !isSubInfoReady()) { |
| logd("[getSubInfoUsingIccId]- null iccid or not ready"); |
| return null; |
| } |
| Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, |
| null, ICC_ID + "=?", new String[] {iccId}, null); |
| ArrayList<SubInfoRecord> subList = null; |
| try { |
| if (cursor != null) { |
| while (cursor.moveToNext()) { |
| SubInfoRecord subInfo = getSubInfoRecord(cursor); |
| if (subInfo != null) |
| { |
| if (subList == null) |
| { |
| subList = new ArrayList<SubInfoRecord>(); |
| } |
| subList.add(subInfo); |
| } |
| } |
| } else { |
| logd("Query fail"); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| return subList; |
| } |
| |
| /** |
| * Get the SubInfoRecord according to slotId |
| * @param slotId the slot which the SIM is inserted |
| * @return SubInfoRecord, maybe null |
| */ |
| @Override |
| public List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) { |
| return getSubInfoUsingSlotIdWithCheck(slotId, true); |
| } |
| |
| /** |
| * Get all the SubInfoRecord(s) in subinfo database |
| * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before |
| */ |
| @Override |
| public List<SubInfoRecord> getAllSubInfoList() { |
| logd("[getAllSubInfoList]+"); |
| List<SubInfoRecord> subList = null; |
| subList = getSubInfo(null, null); |
| if (subList != null) { |
| logd("[getAllSubInfoList]- " + subList.size() + " infos return"); |
| } else { |
| logd("[getAllSubInfoList]- no info return"); |
| } |
| |
| return subList; |
| } |
| |
| /** |
| * Get the SubInfoRecord(s) of the currently inserted SIM(s) |
| * @return Array list of currently inserted SubInfoRecord(s) |
| */ |
| @Override |
| public List<SubInfoRecord> getActivatedSubInfoList() { |
| logd("[getActivatedSubInfoList]+"); |
| List<SubInfoRecord> subList = null; |
| |
| if (!isSubInfoReady()) { |
| logd("[getActivatedSubInfoList] Sub Controller not ready"); |
| return subList; |
| } |
| |
| subList = getSubInfo(SIM_ID + "!=" + SIM_NOT_INSERTED, null); |
| if (subList != null) { |
| logd("[getActivatedSubInfoList]- " + subList.size() + " infos return"); |
| } else { |
| logd("[getActivatedSubInfoList]- no info return"); |
| } |
| |
| return subList; |
| } |
| |
| /** |
| * Get the SUB count of all SUB(s) in subinfo database |
| * @return all SIM count in database, include what was inserted before |
| */ |
| @Override |
| public int getAllSubInfoCount() { |
| logd("[getAllSubInfoCount]+"); |
| Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, |
| null, null, null, null); |
| try { |
| if (cursor != null) { |
| int count = cursor.getCount(); |
| logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB"); |
| return count; |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| logd("[getAllSubInfoCount]- no SUB in DB"); |
| |
| return 0; |
| } |
| |
| /** |
| * 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 the URL of the newly created row or the updated row |
| */ |
| @Override |
| public int addSubInfoRecord(String iccId, int slotId) { |
| logd("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId); |
| if (iccId == null) { |
| logd("[addSubInfoRecord]- null iccId"); |
| } |
| Uri uri = null; |
| String nameToSet; |
| nameToSet = "SUB 0"+Integer.toString(slotId+1); |
| ContentResolver resolver = mContext.getContentResolver(); |
| Cursor cursor = resolver.query(CONTENT_URI, new String[] {BaseColumns._ID, SIM_ID, NAME_SOURCE}, |
| ICC_ID + "=?", new String[] {iccId}, null); |
| try { |
| if (cursor == null || !cursor.moveToFirst()) { |
| ContentValues value = new ContentValues(); |
| value.put(ICC_ID, iccId); |
| // default SIM color differs between slots |
| value.put(COLOR, slotId); |
| value.put(SIM_ID, slotId); |
| value.put(DISPLAY_NAME, nameToSet); |
| uri = resolver.insert(CONTENT_URI, value); |
| logd("[addSubInfoRecord] New record creating values=" + value); |
| logd("[addSubInfoRecord] New record result uri=" + uri); |
| } else { |
| long subId = cursor.getLong(0); |
| int oldSimInfoId = cursor.getInt(1); |
| int nameSource = cursor.getInt(2); |
| ContentValues value = new ContentValues(); |
| |
| if (slotId != oldSimInfoId) { |
| value.put(SIM_ID, slotId); |
| } |
| |
| if (nameSource != USER_INPUT) { |
| value.put(DISPLAY_NAME, nameToSet); |
| } |
| |
| if (value.size() > 0) { |
| String where = BaseColumns._ID + "=" + Long.toString(subId); |
| logd("[addSubInfoRecord] resolver.update value=" + value + " where=" + where); |
| resolver.update(CONTENT_URI, value, where, null); |
| } |
| |
| logd("[addSubInfoRecord] Record already exist"); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| cursor = resolver.query(CONTENT_URI, |
| null, SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null); |
| if (cursor == null) logd("[addSubInfoRecord] 1 cursor is null"); |
| |
| try { |
| if (cursor != null && cursor.moveToFirst()) { |
| do { |
| long subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); |
| logd("[addSubInfoRecord] subId=" + subId + " mSimInfo.size" + mSimInfo.size()); |
| // If mSinInfo is already having a valid subId for a slotId/phoneId, |
| // do not add another subId for same slotId/phoneId. |
| long[] sub = getSubId(slotId); |
| |
| // FIXME: This is only adding records if there isn't any or sub is returning "dummy" values. |
| // this should probably be updating if the recorddoesn't contain the specific slotId/subId mapping. |
| // But that will be for another time and likely there will be other changes in the mean time. |
| if ((mSimInfo.size() == 0) || (sub != null && sub.length > 0 && sub[0] <= 0)) { |
| // set the first entry as default sub |
| // TODO While two subs active, if user deactivats first |
| // one, need to update the default subId with second |
| // one. |
| if (mSimInfo.size() == 0) { |
| logd("[addSubInfoRecord] call setDefaultSubId subId=" + subId); |
| setDefaultSubId(subId); |
| } |
| mSimInfo.put(slotId, subId); |
| logd("[addSubInfoRecord] mSimInfo.size=" + mSimInfo.size() |
| + " slotId = " + slotId + " subId = " + subId); |
| } else { |
| logd("[addSubInfoRecord] size != 0 && sub[] isn't valid, IGNORE"); |
| } |
| } while (cursor.moveToNext()); |
| } else { |
| logd("[addSubInfoRecord] no records for " + BaseColumns._ID); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| int size = mSimInfo.size(); |
| logd("[addSubInfoRecord]- info size=" + size); |
| |
| return 1; |
| } |
| |
| /** |
| * Set SIM color by simInfo index |
| * @param color the color of the SIM |
| * @param subId the unique SubInfoRecord index in database |
| * @return the number of records updated |
| */ |
| @Override |
| public int setColor(int color, long subId) { |
| logd("[setColor]+ color:" + color + " subId:" + subId); |
| int size = sSimBackgroundDarkRes.length; |
| if (subId <= 0 || color < 0 || color >= size) { |
| logd("[setColor]- fail"); |
| return -1; |
| } |
| ContentValues value = new ContentValues(1); |
| value.put(COLOR, color); |
| logd("[setColor]- color:" + color + " set"); |
| |
| int result = mContext.getContentResolver().update(CONTENT_URI, value, |
| BaseColumns._ID + "=" + Long.toString(subId), null); |
| broadcastSimInfoContentChanged(subId, COLOR, color, DEFAULT_STRING_VALUE); |
| |
| return result; |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public int setDisplayName(String displayName, long subId) { |
| return setDisplayNameUsingSrc(displayName, subId, -1); |
| } |
| |
| /** |
| * 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: DEFAULT_SOURCE, 1: SIM_SOURCE, 2: USER_INPUT |
| * @return the number of records updated |
| */ |
| @Override |
| public int setDisplayNameUsingSrc(String displayName, long subId, long nameSource) { |
| logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId + " nameSource:" + nameSource); |
| if (subId <= 0) { |
| logd("[setDisplayName]- fail"); |
| return -1; |
| } |
| String nameToSet; |
| if (displayName == null) { |
| nameToSet = mContext.getString(DEFAULT_NAME_RES); |
| } else { |
| nameToSet = displayName; |
| } |
| ContentValues value = new ContentValues(1); |
| value.put(DISPLAY_NAME, nameToSet); |
| if (nameSource >= DEFAULT_SOURCE) { |
| logd("Set nameSource=" + nameSource); |
| value.put(NAME_SOURCE, nameSource); |
| } |
| logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); |
| |
| int result = mContext.getContentResolver().update(CONTENT_URI, value, |
| BaseColumns._ID + "=" + Long.toString(subId), null); |
| broadcastSimInfoContentChanged(subId, DISPLAY_NAME, DEFAULT_INT_VALUE, nameToSet); |
| |
| return result; |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public int setDispalyNumber(String number, long subId) { |
| logd("[setDispalyNumber]+ number:" + number + " subId:" + subId); |
| if (number == null || subId <= 0) { |
| logd("[setDispalyNumber]- fail"); |
| return -1; |
| } |
| ContentValues value = new ContentValues(1); |
| value.put(NUMBER, number); |
| logd("[setDispalyNumber]- number:" + number + " set"); |
| |
| int result = mContext.getContentResolver().update(CONTENT_URI, value, |
| BaseColumns._ID + "=" + Long.toString(subId), null); |
| broadcastSimInfoContentChanged(subId, NUMBER, DEFAULT_INT_VALUE, number); |
| |
| return result; |
| } |
| |
| /** |
| * Set number display format. 0: none, 1: the first four digits, 2: the last four digits |
| * @param format the display format of phone number |
| * @param subId the unique SubInfoRecord index in database |
| * @return the number of records updated |
| */ |
| @Override |
| public int setDisplayNumberFormat(int format, long subId) { |
| logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId); |
| if (format < 0 || subId <= 0) { |
| logd("[setDisplayNumberFormat]- fail, return -1"); |
| return -1; |
| } |
| ContentValues value = new ContentValues(1); |
| value.put(DISPLAY_NUMBER_FORMAT, format); |
| logd("[setDisplayNumberFormat]- format:" + format + " set"); |
| |
| int result = mContext.getContentResolver().update(CONTENT_URI, value, |
| BaseColumns._ID + "=" + Long.toString(subId), null); |
| broadcastSimInfoContentChanged(subId, DISPLAY_NUMBER_FORMAT, format, DEFAULT_STRING_VALUE); |
| |
| return result; |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public int setDataRoaming(int roaming, long subId) { |
| logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); |
| if (roaming < 0 || subId <= 0) { |
| logd("[setDataRoaming]- fail"); |
| return -1; |
| } |
| ContentValues value = new ContentValues(1); |
| value.put(DATA_ROAMING, roaming); |
| logd("[setDataRoaming]- roaming:" + roaming + " set"); |
| |
| int result = mContext.getContentResolver().update(CONTENT_URI, value, |
| BaseColumns._ID + "=" + Long.toString(subId), null); |
| broadcastSimInfoContentChanged(subId, DATA_ROAMING, roaming, DEFAULT_STRING_VALUE); |
| |
| return result; |
| } |
| |
| @Override |
| public int getSlotId(long subId) { |
| logd("[getSlotId]+ subId:" + subId); |
| if (subId <= 0) { |
| logd("[getSlotId]- subId <= 0"); |
| return SIM_NOT_INSERTED; |
| } |
| |
| int size = mSimInfo.size(); |
| logd("[getSlotId]- info size="+size); |
| |
| if (size == 0) |
| { |
| return SIM_NOT_INSERTED; |
| } |
| |
| for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { |
| int sim = entry.getKey(); |
| long sub = entry.getValue(); |
| logd("[getSlotId]- entry, sim ="+sim+", sub="+ sub); |
| |
| if (subId == sub) |
| { |
| logd("[getSlotId]- return ="+sim); |
| return sim; |
| } |
| } |
| |
| logd("[getSlotId]- return fail"); |
| return (int)(subId-1); |
| |
| } |
| |
| /** |
| * Return the subId for specified sim Id. |
| * @deprecated |
| */ |
| @Override |
| @Deprecated |
| public long[] getSubId(int slotId) { |
| if (VDBG) logd("[getSubId]+ slotId:" + slotId); |
| |
| // FIXME this should return the subIds associated with the PhoneIds |
| // using {1, 2} for now, to workaround problem with data classes |
| // not being able to find a subscription that has value set to |
| // default data during bootup, since SubscriptionController has not |
| // discovered all the subs when this query is made. |
| |
| long[] subId = new long[] {-1-slotId, -1-slotId}; |
| |
| if (slotId < 0) { |
| logd("[getSubId]- slotId < 0, return dummy instead"); |
| return subId; |
| } |
| |
| int size = mSimInfo.size(); |
| if (VDBG) logd("[getSubId]- info size="+size); |
| |
| if (size == 0) |
| { |
| logd("[getSubId]- size == 0, return dummy instead"); |
| return subId; |
| } |
| |
| int subIdx = 0; |
| |
| for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { |
| int sim = entry.getKey(); |
| long sub = entry.getValue(); |
| |
| if (VDBG) logd("[getSubId]- entry, sim ="+sim+", sub="+ sub); |
| if (slotId == sim) |
| { |
| subId[subIdx] = sub; |
| if (VDBG) logd("[getSubId]- subId["+subIdx+"] = "+subId[subIdx]); |
| subIdx++; |
| } |
| } |
| |
| if (VDBG) logd("[getSubId]-, subId = "+subId[0]); |
| return subId; |
| } |
| |
| @Override |
| public int getPhoneId(long subId) { |
| if (VDBG) logd("[getPhoneId]+ subId:" + subId); |
| if (subId <= 0) { |
| // FIXME Do not auto map subId to phoneId |
| // May be we shoud add dummy/default subId's during |
| // initialization of subscription controller ?? |
| if (subId == -1) { |
| logd("[getPhoneId]- subId == -1 return =" + 0); |
| return 0; |
| } else if (subId == -2) { |
| logd("[getPhoneId]- subId == -2 return =" + 1); |
| return 1; |
| } |
| } |
| |
| int size = mSimInfo.size(); |
| |
| if (size == 0) { |
| if (VDBG) logd("[getPhoneId]- returning defaultPhoneId=" + mDefaultPhoneId); |
| return mDefaultPhoneId; |
| } |
| |
| for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { |
| int sim = entry.getKey(); |
| long sub = entry.getValue(); |
| if (VDBG) logd("[getPhoneId]- entry, sim="+sim+", sub="+ sub); |
| |
| if (subId == sub) |
| { |
| if (VDBG) logd("[getPhoneId]- return="+sim); |
| return sim; |
| } |
| } |
| |
| if (VDBG) logd("[getPhoneId]- return=" + (int)(subId-1)); |
| return (int)(subId-1); |
| |
| } |
| |
| @Override |
| public int clearSubInfo() |
| { |
| if (VDBG) logd("[clearSubInfo]+"); |
| |
| int size = mSimInfo.size(); |
| if (VDBG) logd("[getSubId]- info size="+size); |
| |
| if (size == 0) |
| { |
| return 0; |
| } |
| |
| mSimInfo.clear(); |
| if (VDBG) logd("[clearSubInfo]-"); |
| return 0; |
| |
| } |
| |
| private static int[] setSimResource(int type) { |
| int[] simResource = null; |
| |
| switch (type) { |
| case RES_TYPE_BACKGROUND_DARK: |
| simResource = new int[] { |
| com.android.internal.R.drawable.sim_dark_blue, |
| com.android.internal.R.drawable.sim_dark_orange, |
| com.android.internal.R.drawable.sim_dark_green, |
| com.android.internal.R.drawable.sim_dark_purple |
| }; |
| break; |
| case RES_TYPE_BACKGROUND_LIGHT: |
| simResource = new int[] { |
| com.android.internal.R.drawable.sim_light_blue, |
| com.android.internal.R.drawable.sim_light_orange, |
| com.android.internal.R.drawable.sim_light_green, |
| com.android.internal.R.drawable.sim_light_purple |
| }; |
| break; |
| } |
| |
| return simResource; |
| } |
| |
| private void logd(String msg) { |
| Rlog.d(LOG_TAG, "[SubController]" + msg); |
| } |
| |
| private void loge(String msg) { |
| Rlog.e(LOG_TAG, "[SubController]" + msg); |
| } |
| |
| @Override |
| public long getDefaultSubId() { |
| if (VDBG) logd("getDefaultSubId: value=" + mDefaultVoiceSubId); |
| return mDefaultVoiceSubId; |
| } |
| |
| @Override |
| public void setDefaultVoiceSubId(long subId) { |
| Settings.Global.putLong(mContext.getContentResolver(), |
| Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); |
| broadcastDefaultVoiceSubIdChanged(subId); |
| if (VDBG) logd("setDefaultVoiceSubId, subId=" + subId); |
| } |
| |
| private static void broadcastDefaultVoiceSubIdChanged(long subId) { |
| // Broadcast an Intent for default voice sub change |
| 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); |
| } |
| |
| @Override |
| public long getDefaultVoiceSubId() { |
| long subId = SubscriptionManager.INVALID_SUB_ID; |
| |
| try { |
| subId = Settings.Global.getLong(mContext.getContentResolver(), |
| Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION); |
| } catch (SettingNotFoundException snfe) { |
| loge("Settings Exception Reading Dual Sim Voice Call Values"); |
| } |
| |
| if (VDBG) logd("getDefaultVoiceSubId, value = " + subId); |
| return subId; |
| } |
| |
| @Override |
| public long getDefaultDataSubId() { |
| long subId = SubscriptionManager.INVALID_SUB_ID; |
| try { |
| subId = Settings.Global.getLong(mContext.getContentResolver(), |
| Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION); |
| } catch (SettingNotFoundException snfe) { |
| loge("Settings Exception Reading Dual Sim Data Call Values"); |
| } |
| |
| return subId; |
| } |
| |
| public void setDefaultDataSubId(long subId) { |
| loge("setDataSubId: subId=" + subId + " FIXME NOP right now"); |
| DctController dctController = DctController.getInstance(); |
| dctController.setDataSubId(subId); |
| dctController.registerForDataSwitchInfo(mDataConnectionHandler, |
| EVENT_SET_DEFAULT_DATA_DONE, null); |
| } |
| |
| private void updateDataSubId(long subId) { |
| if (VDBG) logd(" updateDataSubId, subId=" + subId); |
| Settings.Global.putLong(mContext.getContentResolver(), |
| Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); |
| broadcastDefaultDataSubIdChanged(subId); |
| } |
| |
| private static void broadcastDefaultDataSubIdChanged(long subId) { |
| // Broadcast an Intent for default data sub change |
| 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); |
| } |
| |
| /* Sets the default subscription. If only one sub is active that |
| * sub is set as default subId. If two or more sub's are active |
| * the first sub is set as default subscription |
| */ |
| // FIXME Modify/rename this method name as part of |
| // refactoring other subscription changes |
| public void setDefaultSubId(long subId) { |
| if (subId > 0 && subId != SubscriptionManager.INVALID_SUB_ID |
| && subId != SubscriptionManager.DEFAULT_SUB_ID) { |
| int phoneId = getPhoneId(subId); |
| if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) { |
| mDefaultVoiceSubId = subId; // returned by getFirstActiveSubId() |
| if (VDBG) logd("setDefaultSubId: mDefaultVoiceSubId=subId=" + subId); |
| // Update MCC MNC device configuration information |
| String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId); |
| if (VDBG) logd("setDefaultSubId: call update mccmnc=" + defaultMccMnc); |
| 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 (VDBG) { |
| logd("setDefaultSubId: broadcast default subId changed phoneId=" + phoneId |
| + " subId=" + subId); |
| } |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| } else { |
| if (VDBG) { |
| logd("setDefaultSubId: not set invalid phoneId=" + phoneId + " subId=" + subId); |
| } |
| } |
| } else { |
| if (VDBG) logd("setDefaultSubId: not set invalid subId=" + subId); |
| } |
| } |
| |
| // FIXME SubscriptionController should register (perhaps using Registrants) |
| // with SimInfoUpdater for any updates to the subscriptions. When it is |
| // notified of any updates, this API should be called. Rather than |
| // SimInfoUpdater calling into updateDefaultSubId. |
| public void updateDefaultSubId() { |
| if (!isSubActive(getDefaultDataSubId())) { |
| updateDataSubId(getFirstActiveSubId()); |
| } |
| |
| if (!isSubActive(getDefaultVoiceSubId())) { |
| setDefaultVoiceSubId(getFirstActiveSubId()); |
| } |
| } |
| |
| // FIXME As part of getDefaultSubId method cleanup, modify |
| // the mDefaultVoiceSubId to mFirstActiveSubId. |
| private long getFirstActiveSubId() { |
| if (VDBG) logd("getFirstActiveSubId, value = " + mDefaultVoiceSubId); |
| return mDefaultVoiceSubId; |
| } |
| |
| private boolean isSubActive(long subId) { |
| boolean subActive = false; |
| List<SubInfoRecord> activeSubList = getActivatedSubInfoList(); |
| |
| if (activeSubList != null) { |
| for (SubInfoRecord subInfoRecord : activeSubList) { |
| if (subInfoRecord.mSubId == subId) { |
| if (VDBG) logd("isSubActive, found active sub " + subId); |
| subActive = true; |
| break; |
| } |
| } |
| } |
| return subActive; |
| } |
| |
| private class DataConnectionHandler extends Handler { |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar = (AsyncResult) msg.obj; |
| switch (msg.what) { |
| case EVENT_SET_DEFAULT_DATA_DONE: |
| Long subId = (Long)ar.result; |
| if (VDBG) logd("EVENT_SET_DEFAULT_DATA_DONE subId:" + subId); |
| updateDataSubId(subId); |
| break; |
| } |
| } |
| } |
| |
| /* This should return long and not long [] since each phone has |
| * exactly 1 sub id for now, it could return the 0th element |
| * returned from getSubId() |
| */ |
| // FIXME will design a mechanism to manage the relationship between PhoneId/SlotId/SubId |
| // since phoneId = SlotId is not always true |
| public long getSubIdUsingPhoneId(int phoneId) { |
| long[] subId = getSubId(phoneId); |
| return subId[0]; |
| } |
| |
| public long[] getSubIdUsingSlotId(int slotId) { |
| return getSubId(slotId); |
| } |
| |
| public List<SubInfoRecord> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) { |
| if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId=" + slotId); |
| if (slotId < 0 ) { |
| if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]- return null, slotId < 0"); |
| return null; |
| } |
| |
| if (needCheck && !isSubInfoReady()) { |
| if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready return null"); |
| return null; |
| } |
| |
| Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, |
| null, SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null); |
| ArrayList<SubInfoRecord> subList = null; |
| try { |
| if (cursor != null) { |
| while (cursor.moveToNext()) { |
| SubInfoRecord subInfo = getSubInfoRecord(cursor); |
| if (subInfo != null) |
| { |
| if (subList == null) |
| { |
| subList = new ArrayList<SubInfoRecord>(); |
| } |
| subList.add(subInfo); |
| } |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| if (VDBG) logd("[getSubInfoUsingSlotId]- subList=" + subList); |
| return subList; |
| |
| } |
| } |