Home | History | Annotate | Download | only in telephony
      1 /*
      2 * Copyright (C) 2011-2014 MediaTek Inc.
      3 *
      4 * Licensed under the Apache License, Version 2.0 (the "License");
      5 * you may not use this file except in compliance with the License.
      6 * You may obtain a copy of the License at
      7 *
      8 *      http://www.apache.org/licenses/LICENSE-2.0
      9 *
     10 * Unless required by applicable law or agreed to in writing, software
     11 * distributed under the License is distributed on an "AS IS" BASIS,
     12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 * See the License for the specific language governing permissions and
     14 * limitations under the License.
     15 */
     16 
     17 package com.android.internal.telephony;
     18 
     19 import android.content.Context;
     20 import android.os.AsyncResult;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.os.ServiceManager;
     24 import android.os.UserHandle;
     25 import android.telephony.Rlog;
     26 import android.util.Log;
     27 import android.net.Uri;
     28 import android.database.Cursor;
     29 import android.content.Intent;
     30 import android.provider.BaseColumns;
     31 import android.provider.Settings;
     32 import android.content.ContentResolver;
     33 import android.content.ContentValues;
     34 
     35 import com.android.internal.telephony.ISub;
     36 import com.android.internal.telephony.uicc.SpnOverride;
     37 
     38 import android.telephony.SubscriptionManager;
     39 import android.telephony.SubInfoRecord;
     40 import android.telephony.TelephonyManager;
     41 import android.text.format.Time;
     42 
     43 import java.io.FileDescriptor;
     44 import java.io.PrintWriter;
     45 import java.util.ArrayList;
     46 import java.util.Iterator;
     47 import java.util.LinkedList;
     48 import java.util.List;
     49 import java.util.HashMap;
     50 import java.util.Map.Entry;
     51 import java.util.Set;
     52 
     53 /**
     54  * SubscriptionController to provide an inter-process communication to
     55  * access Sms in Icc.
     56  *
     57  * Any setters which take subId, slotId or phoneId as a parameter will throw an exception if the
     58  * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID.
     59  *
     60  * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling
     61  * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()).
     62  *
     63  * Finally, any getters which perform the mapping between subscriptions, slots and phones will
     64  * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters
     65  * will fail and return the appropriate error value. Ie calling getSlotId(INVALID_SUB_ID) will
     66  * return INVALID_SLOT_ID and calling getSubInfoForSubscriber(INVALID_SUB_ID) will return null.
     67  *
     68  */
     69 public class SubscriptionController extends ISub.Stub {
     70     static final String LOG_TAG = "SubController";
     71     static final boolean DBG = true;
     72     static final boolean VDBG = false;
     73     static final int MAX_LOCAL_LOG_LINES = 500; // TODO: Reduce to 100 when 17678050 is fixed
     74     private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);
     75 
     76     /**
     77      * Copied from android.util.LocalLog with flush() adding flush and line number
     78      * TODO: Update LocalLog
     79      */
     80     static class ScLocalLog {
     81 
     82         private LinkedList<String> mLog;
     83         private int mMaxLines;
     84         private Time mNow;
     85 
     86         public ScLocalLog(int maxLines) {
     87             mLog = new LinkedList<String>();
     88             mMaxLines = maxLines;
     89             mNow = new Time();
     90         }
     91 
     92         public synchronized void log(String msg) {
     93             if (mMaxLines > 0) {
     94                 int pid = android.os.Process.myPid();
     95                 int tid = android.os.Process.myTid();
     96                 mNow.setToNow();
     97                 mLog.add(mNow.format("%m-%d %H:%M:%S") + " pid=" + pid + " tid=" + tid + " " + msg);
     98                 while (mLog.size() > mMaxLines) mLog.remove();
     99             }
    100         }
    101 
    102         public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    103             final int LOOPS_PER_FLUSH = 10; // Flush every N loops.
    104             Iterator<String> itr = mLog.listIterator(0);
    105             int i = 0;
    106             while (itr.hasNext()) {
    107                 pw.println(Integer.toString(i++) + ": " + itr.next());
    108                 // Flush periodically so we don't drop lines
    109                 if ((i % LOOPS_PER_FLUSH) == 0) pw.flush();
    110             }
    111         }
    112     }
    113 
    114     protected final Object mLock = new Object();
    115     protected boolean mSuccess;
    116 
    117     /** The singleton instance. */
    118     private static SubscriptionController sInstance = null;
    119     protected static PhoneProxy[] sProxyPhones;
    120     protected Context mContext;
    121     protected CallManager mCM;
    122 
    123     private static final int RES_TYPE_BACKGROUND_DARK = 0;
    124     private static final int RES_TYPE_BACKGROUND_LIGHT = 1;
    125 
    126     private static final int[] sSimBackgroundDarkRes = setSimResource(RES_TYPE_BACKGROUND_DARK);
    127     private static final int[] sSimBackgroundLightRes = setSimResource(RES_TYPE_BACKGROUND_LIGHT);
    128 
    129     //FIXME this does not allow for multiple subs in a slot
    130     private static HashMap<Integer, Long> mSimInfo = new HashMap<Integer, Long>();
    131     private static long mDefaultVoiceSubId = SubscriptionManager.INVALID_SUB_ID;
    132     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_ID;
    133 
    134     private static final int EVENT_WRITE_MSISDN_DONE = 1;
    135 
    136     protected Handler mHandler = new Handler() {
    137         @Override
    138         public void handleMessage(Message msg) {
    139             AsyncResult ar;
    140 
    141             switch (msg.what) {
    142                 case EVENT_WRITE_MSISDN_DONE:
    143                     ar = (AsyncResult) msg.obj;
    144                     synchronized (mLock) {
    145                         mSuccess = (ar.exception == null);
    146                         logd("EVENT_WRITE_MSISDN_DONE, mSuccess = "+mSuccess);
    147                         mLock.notifyAll();
    148                     }
    149                     break;
    150             }
    151         }
    152     };
    153 
    154 
    155     public static SubscriptionController init(Phone phone) {
    156         synchronized (SubscriptionController.class) {
    157             if (sInstance == null) {
    158                 sInstance = new SubscriptionController(phone);
    159             } else {
    160                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    161             }
    162             return sInstance;
    163         }
    164     }
    165 
    166     public static SubscriptionController init(Context c, CommandsInterface[] ci) {
    167         synchronized (SubscriptionController.class) {
    168             if (sInstance == null) {
    169                 sInstance = new SubscriptionController(c);
    170             } else {
    171                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    172             }
    173             return sInstance;
    174         }
    175     }
    176 
    177     public static SubscriptionController getInstance() {
    178         if (sInstance == null)
    179         {
    180            Log.wtf(LOG_TAG, "getInstance null");
    181         }
    182 
    183         return sInstance;
    184     }
    185 
    186     private SubscriptionController(Context c) {
    187         mContext = c;
    188         mCM = CallManager.getInstance();
    189 
    190         if(ServiceManager.getService("isub") == null) {
    191                 ServiceManager.addService("isub", this);
    192         }
    193 
    194         logdl("[SubscriptionController] init by Context");
    195     }
    196 
    197     private boolean isSubInfoReady() {
    198         return mSimInfo.size() > 0;
    199     }
    200 
    201     private SubscriptionController(Phone phone) {
    202         mContext = phone.getContext();
    203         mCM = CallManager.getInstance();
    204 
    205         if(ServiceManager.getService("isub") == null) {
    206                 ServiceManager.addService("isub", this);
    207         }
    208 
    209         logdl("[SubscriptionController] init by Phone");
    210     }
    211 
    212     /**
    213      * Make sure the caller has the READ_PHONE_STATE permission.
    214      *
    215      * @throws SecurityException if the caller does not have the required permission
    216      */
    217     private void enforceSubscriptionPermission() {
    218         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
    219                 "Requires READ_PHONE_STATE");
    220     }
    221 
    222     /**
    223      * Broadcast when subinfo settings has chanded
    224      * @SubId The unique SubInfoRecord index in database
    225      * @param columnName The column that is updated
    226      * @param intContent The updated integer value
    227      * @param stringContent The updated string value
    228      */
    229      private void broadcastSimInfoContentChanged(long subId,
    230             String columnName, int intContent, String stringContent) {
    231 
    232         Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
    233         intent.putExtra(BaseColumns._ID, subId);
    234         intent.putExtra(TelephonyIntents.EXTRA_COLUMN_NAME, columnName);
    235         intent.putExtra(TelephonyIntents.EXTRA_INT_CONTENT, intContent);
    236         intent.putExtra(TelephonyIntents.EXTRA_STRING_CONTENT, stringContent);
    237         if (intContent != SubscriptionManager.DEFAULT_INT_VALUE) {
    238             logd("[broadcastSimInfoContentChanged] subId" + subId
    239                     + " changed, " + columnName + " -> " +  intContent);
    240         } else {
    241             logd("[broadcastSimInfoContentChanged] subId" + subId
    242                     + " changed, " + columnName + " -> " +  stringContent);
    243         }
    244         mContext.sendBroadcast(intent);
    245     }
    246 
    247 
    248     /**
    249      * New SubInfoRecord instance and fill in detail info
    250      * @param cursor
    251      * @return the query result of desired SubInfoRecord
    252      */
    253     private SubInfoRecord getSubInfoRecord(Cursor cursor) {
    254             SubInfoRecord info = new SubInfoRecord();
    255             info.subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
    256             info.iccId = cursor.getString(cursor.getColumnIndexOrThrow(
    257                     SubscriptionManager.ICC_ID));
    258             info.slotId = cursor.getInt(cursor.getColumnIndexOrThrow(
    259                     SubscriptionManager.SIM_ID));
    260             info.displayName = cursor.getString(cursor.getColumnIndexOrThrow(
    261                     SubscriptionManager.DISPLAY_NAME));
    262             info.nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
    263                     SubscriptionManager.NAME_SOURCE));
    264             info.color = cursor.getInt(cursor.getColumnIndexOrThrow(
    265                     SubscriptionManager.COLOR));
    266             info.number = cursor.getString(cursor.getColumnIndexOrThrow(
    267                     SubscriptionManager.NUMBER));
    268             info.displayNumberFormat = cursor.getInt(cursor.getColumnIndexOrThrow(
    269                     SubscriptionManager.DISPLAY_NUMBER_FORMAT));
    270             info.dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
    271                     SubscriptionManager.DATA_ROAMING));
    272 
    273             int size = sSimBackgroundDarkRes.length;
    274             if (info.color >= 0 && info.color < size) {
    275                 info.simIconRes[RES_TYPE_BACKGROUND_DARK] = sSimBackgroundDarkRes[info.color];
    276                 info.simIconRes[RES_TYPE_BACKGROUND_LIGHT] = sSimBackgroundLightRes[info.color];
    277             }
    278             info.mcc = cursor.getInt(cursor.getColumnIndexOrThrow(
    279                     SubscriptionManager.MCC));
    280             info.mnc = cursor.getInt(cursor.getColumnIndexOrThrow(
    281                     SubscriptionManager.MNC));
    282 
    283             logd("[getSubInfoRecord] SubId:" + info.subId + " iccid:" + info.iccId + " slotId:" +
    284                     info.slotId + " displayName:" + info.displayName + " color:" + info.color +
    285                     " mcc/mnc:" + info.mcc + "/" + info.mnc);
    286 
    287             return info;
    288     }
    289 
    290     /**
    291      * Query SubInfoRecord(s) from subinfo database
    292      * @param selection A filter declaring which rows to return
    293      * @param queryKey query key content
    294      * @return Array list of queried result from database
    295      */
    296      private List<SubInfoRecord> getSubInfo(String selection, Object queryKey) {
    297         logd("selection:" + selection + " " + queryKey);
    298         String[] selectionArgs = null;
    299         if (queryKey != null) {
    300             selectionArgs = new String[] {queryKey.toString()};
    301         }
    302         ArrayList<SubInfoRecord> subList = null;
    303         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
    304                 null, selection, selectionArgs, null);
    305         try {
    306             if (cursor != null) {
    307                 while (cursor.moveToNext()) {
    308                     SubInfoRecord subInfo = getSubInfoRecord(cursor);
    309                     if (subInfo != null)
    310                     {
    311                         if (subList == null)
    312                         {
    313                             subList = new ArrayList<SubInfoRecord>();
    314                         }
    315                         subList.add(subInfo);
    316                 }
    317                 }
    318             } else {
    319                 logd("Query fail");
    320             }
    321         } finally {
    322             if (cursor != null) {
    323                 cursor.close();
    324             }
    325         }
    326 
    327         return subList;
    328     }
    329 
    330 
    331 
    332     /**
    333      * Get the SubInfoRecord according to an index
    334      * @param subId The unique SubInfoRecord index in database
    335      * @return SubInfoRecord, maybe null
    336      */
    337     @Override
    338     public SubInfoRecord getSubInfoForSubscriber(long subId) {
    339         logd("[getSubInfoForSubscriberx]+ subId:" + subId);
    340         enforceSubscriptionPermission();
    341 
    342         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
    343             subId = getDefaultSubId();
    344         }
    345         if (!SubscriptionManager.isValidSubId(subId) || !isSubInfoReady()) {
    346             logd("[getSubInfoForSubscriberx]- invalid subId or not ready");
    347             return null;
    348         }
    349         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
    350                 null, BaseColumns._ID + "=?", new String[] {Long.toString(subId)}, null);
    351         try {
    352             if (cursor != null) {
    353                 if (cursor.moveToFirst()) {
    354                     logd("[getSubInfoForSubscriberx]- Info detail:");
    355                     return getSubInfoRecord(cursor);
    356                 }
    357             }
    358         } finally {
    359             if (cursor != null) {
    360                 cursor.close();
    361             }
    362         }
    363         logd("[getSubInfoForSubscriber]- null info return");
    364 
    365         return null;
    366     }
    367 
    368     /**
    369      * Get the SubInfoRecord according to an IccId
    370      * @param iccId the IccId of SIM card
    371      * @return SubInfoRecord, maybe null
    372      */
    373     @Override
    374     public List<SubInfoRecord> getSubInfoUsingIccId(String iccId) {
    375         logd("[getSubInfoUsingIccId]+ iccId:" + iccId);
    376         enforceSubscriptionPermission();
    377 
    378         if (iccId == null || !isSubInfoReady()) {
    379             logd("[getSubInfoUsingIccId]- null iccid or not ready");
    380             return null;
    381         }
    382         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
    383                 null, SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null);
    384         ArrayList<SubInfoRecord> subList = null;
    385         try {
    386             if (cursor != null) {
    387                 while (cursor.moveToNext()) {
    388                     SubInfoRecord subInfo = getSubInfoRecord(cursor);
    389                     if (subInfo != null)
    390                     {
    391                         if (subList == null)
    392                         {
    393                             subList = new ArrayList<SubInfoRecord>();
    394                         }
    395                         subList.add(subInfo);
    396                 }
    397                 }
    398             } else {
    399                 logd("Query fail");
    400             }
    401         } finally {
    402             if (cursor != null) {
    403                 cursor.close();
    404             }
    405         }
    406 
    407         return subList;
    408     }
    409 
    410     /**
    411      * Get the SubInfoRecord according to slotId
    412      * @param slotId the slot which the SIM is inserted
    413      * @return SubInfoRecord, maybe null
    414      */
    415     @Override
    416     public List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) {
    417         return getSubInfoUsingSlotIdWithCheck(slotId, true);
    418     }
    419 
    420     /**
    421      * Get all the SubInfoRecord(s) in subinfo database
    422      * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before
    423      */
    424     @Override
    425     public List<SubInfoRecord> getAllSubInfoList() {
    426         logd("[getAllSubInfoList]+");
    427         enforceSubscriptionPermission();
    428 
    429         List<SubInfoRecord> subList = null;
    430         subList = getSubInfo(null, null);
    431         if (subList != null) {
    432             logd("[getAllSubInfoList]- " + subList.size() + " infos return");
    433         } else {
    434             logd("[getAllSubInfoList]- no info return");
    435         }
    436 
    437         return subList;
    438     }
    439 
    440     /**
    441      * Get the SubInfoRecord(s) of the currently inserted SIM(s)
    442      * @return Array list of currently inserted SubInfoRecord(s)
    443      */
    444     @Override
    445     public List<SubInfoRecord> getActiveSubInfoList() {
    446         enforceSubscriptionPermission();
    447         logdl("[getActiveSubInfoList]+");
    448 
    449         List<SubInfoRecord> subList = null;
    450 
    451         if (!isSubInfoReady()) {
    452             logdl("[getActiveSubInfoList] Sub Controller not ready");
    453             return subList;
    454         }
    455 
    456         subList = getSubInfo(SubscriptionManager.SIM_ID
    457                 + "!=" + SubscriptionManager.INVALID_SLOT_ID, null);
    458         if (subList != null) {
    459             logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
    460         } else {
    461             logdl("[getActiveSubInfoList]- no info return");
    462         }
    463 
    464         return subList;
    465     }
    466 
    467     /**
    468      * Get the SUB count of active SUB(s)
    469      * @return active SIM count
    470      */
    471     @Override
    472     public int getActiveSubInfoCount() {
    473         logd("[getActiveSubInfoCount]+");
    474         List<SubInfoRecord> records = getActiveSubInfoList();
    475         if (records == null) {
    476             logd("[getActiveSubInfoCount] records null");
    477             return 0;
    478         }
    479         logd("[getActiveSubInfoCount]- count: " + records.size());
    480         return records.size();
    481     }
    482 
    483     /**
    484      * Get the SUB count of all SUB(s) in subinfo database
    485      * @return all SIM count in database, include what was inserted before
    486      */
    487     @Override
    488     public int getAllSubInfoCount() {
    489         logd("[getAllSubInfoCount]+");
    490         enforceSubscriptionPermission();
    491 
    492         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
    493                 null, null, null, null);
    494         try {
    495             if (cursor != null) {
    496                 int count = cursor.getCount();
    497                 logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
    498                 return count;
    499             }
    500         } finally {
    501             if (cursor != null) {
    502                 cursor.close();
    503             }
    504         }
    505         logd("[getAllSubInfoCount]- no SUB in DB");
    506 
    507         return 0;
    508     }
    509 
    510     /**
    511      * Add a new SubInfoRecord to subinfo database if needed
    512      * @param iccId the IccId of the SIM card
    513      * @param slotId the slot which the SIM is inserted
    514      * @return the URL of the newly created row or the updated row
    515      */
    516     @Override
    517     public int addSubInfoRecord(String iccId, int slotId) {
    518         logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
    519         enforceSubscriptionPermission();
    520 
    521         if (iccId == null) {
    522             logdl("[addSubInfoRecord]- null iccId");
    523         }
    524 
    525         long[] subIds = getSubId(slotId);
    526         if (subIds == null || subIds.length == 0) {
    527             logdl("[addSubInfoRecord]- getSubId fail");
    528             return 0;
    529         }
    530 
    531         String nameToSet;
    532         SpnOverride mSpnOverride = new SpnOverride();
    533 
    534         String CarrierName = TelephonyManager.getDefault().getSimOperator(subIds[0]);
    535         logdl("[addSubInfoRecord] CarrierName = " + CarrierName);
    536 
    537         if (mSpnOverride.containsCarrier(CarrierName)) {
    538             nameToSet = mSpnOverride.getSpn(CarrierName) + " 0" + Integer.toString(slotId + 1);
    539             logdl("[addSubInfoRecord] Found, name = " + nameToSet);
    540         } else {
    541             nameToSet = "SUB 0" + Integer.toString(slotId + 1);
    542             logdl("[addSubInfoRecord] Not found, name = " + nameToSet);
    543         }
    544 
    545         ContentResolver resolver = mContext.getContentResolver();
    546         Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
    547                 new String[] {BaseColumns._ID, SubscriptionManager.SIM_ID,
    548                 SubscriptionManager.NAME_SOURCE}, SubscriptionManager.ICC_ID + "=?",
    549                 new String[] {iccId}, null);
    550 
    551         try {
    552             if (cursor == null || !cursor.moveToFirst()) {
    553                 ContentValues value = new ContentValues();
    554                 value.put(SubscriptionManager.ICC_ID, iccId);
    555                 // default SIM color differs between slots
    556                 value.put(SubscriptionManager.COLOR, slotId);
    557                 value.put(SubscriptionManager.SIM_ID, slotId);
    558                 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
    559                 Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
    560                 logdl("[addSubInfoRecord]- New record created: " + uri);
    561             } else {
    562                 long subId = cursor.getLong(0);
    563                 int oldSimInfoId = cursor.getInt(1);
    564                 int nameSource = cursor.getInt(2);
    565                 ContentValues value = new ContentValues();
    566 
    567                 if (slotId != oldSimInfoId) {
    568                     value.put(SubscriptionManager.SIM_ID, slotId);
    569                 }
    570 
    571                 if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
    572                     value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
    573                 }
    574 
    575                 if (value.size() > 0) {
    576                     resolver.update(SubscriptionManager.CONTENT_URI, value,
    577                             BaseColumns._ID + "=" + Long.toString(subId), null);
    578                 }
    579 
    580                 logdl("[addSubInfoRecord]- Record already exist");
    581             }
    582         } finally {
    583             if (cursor != null) {
    584                 cursor.close();
    585             }
    586         }
    587 
    588         cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
    589                 SubscriptionManager.SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null);
    590 
    591         try {
    592             if (cursor != null && cursor.moveToFirst()) {
    593                 do {
    594                     long subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
    595                     // If mSimInfo already has a valid subId for a slotId/phoneId,
    596                     // do not add another subId for same slotId/phoneId.
    597                     Long currentSubId = mSimInfo.get(slotId);
    598                     if (currentSubId == null || !SubscriptionManager.isValidSubId(currentSubId)) {
    599                         // TODO While two subs active, if user deactivats first
    600                         // one, need to update the default subId with second one.
    601 
    602                         // FIXME: Currently we assume phoneId and slotId may not be true
    603                         // when we cross map modem or when multiple subs per slot.
    604                         // But is true at the moment.
    605                         mSimInfo.put(slotId, subId);
    606                         int simCount = TelephonyManager.getDefault().getSimCount();
    607                         long defaultSubId = getDefaultSubId();
    608                         logdl("[addSubInfoRecord] mSimInfo.size=" + mSimInfo.size()
    609                                 + " slotId=" + slotId + " subId=" + subId
    610                                 + " defaultSubId=" + defaultSubId + " simCount=" + simCount);
    611 
    612                         // Set the default sub if not set or if single sim device
    613                         if (!SubscriptionManager.isValidSubId(defaultSubId) || simCount == 1) {
    614                             setDefaultSubId(subId);
    615                         }
    616                         // If single sim device, set this subscription as the default for everything
    617                         if (simCount == 1) {
    618                             logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
    619                             setDefaultDataSubId(subId);
    620                             setDefaultSmsSubId(subId);
    621                             setDefaultVoiceSubId(subId);
    622                         }
    623                     } else {
    624                         logdl("[addSubInfoRecord] currentSubId != null && currentSubId is valid, IGNORE");
    625                     }
    626                     logdl("[addSubInfoRecord]- hashmap("+slotId+","+subId+")");
    627                 } while (cursor.moveToNext());
    628             }
    629         } finally {
    630             if (cursor != null) {
    631                 cursor.close();
    632             }
    633         }
    634 
    635         int size = mSimInfo.size();
    636         logdl("[addSubInfoRecord]- info size="+size);
    637 
    638         // Once the records are loaded, notify DcTracker
    639         updateAllDataConnectionTrackers();
    640 
    641         // FIXME this does not match the javadoc
    642         return 1;
    643     }
    644 
    645     /**
    646      * Set SIM color by simInfo index
    647      * @param color the color of the SIM
    648      * @param subId the unique SubInfoRecord index in database
    649      * @return the number of records updated
    650      */
    651     @Override
    652     public int setColor(int color, long subId) {
    653         logd("[setColor]+ color:" + color + " subId:" + subId);
    654         enforceSubscriptionPermission();
    655 
    656         validateSubId(subId);
    657         int size = sSimBackgroundDarkRes.length;
    658         if (color < 0 || color >= size) {
    659             logd("[setColor]- fail");
    660             return -1;
    661         }
    662         ContentValues value = new ContentValues(1);
    663         value.put(SubscriptionManager.COLOR, color);
    664         logd("[setColor]- color:" + color + " set");
    665 
    666         int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
    667                 BaseColumns._ID + "=" + Long.toString(subId), null);
    668         broadcastSimInfoContentChanged(subId, SubscriptionManager.COLOR,
    669                 color, SubscriptionManager.DEFAULT_STRING_VALUE);
    670 
    671         return result;
    672     }
    673 
    674     /**
    675      * Set display name by simInfo index
    676      * @param displayName the display name of SIM card
    677      * @param subId the unique SubInfoRecord index in database
    678      * @return the number of records updated
    679      */
    680     @Override
    681     public int setDisplayName(String displayName, long subId) {
    682         return setDisplayNameUsingSrc(displayName, subId, -1);
    683     }
    684 
    685     /**
    686      * Set display name by simInfo index with name source
    687      * @param displayName the display name of SIM card
    688      * @param subId the unique SubInfoRecord index in database
    689      * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
    690      *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
    691      * @return the number of records updated
    692      */
    693     @Override
    694     public int setDisplayNameUsingSrc(String displayName, long subId, long nameSource) {
    695         logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
    696                 + " nameSource:" + nameSource);
    697         enforceSubscriptionPermission();
    698 
    699         validateSubId(subId);
    700         String nameToSet;
    701         if (displayName == null) {
    702             nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
    703         } else {
    704             nameToSet = displayName;
    705         }
    706         ContentValues value = new ContentValues(1);
    707         value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
    708         if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
    709             logd("Set nameSource=" + nameSource);
    710             value.put(SubscriptionManager.NAME_SOURCE, nameSource);
    711         }
    712         logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
    713 
    714         int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
    715                 BaseColumns._ID + "=" + Long.toString(subId), null);
    716         broadcastSimInfoContentChanged(subId, SubscriptionManager.DISPLAY_NAME,
    717                 SubscriptionManager.DEFAULT_INT_VALUE, nameToSet);
    718 
    719         return result;
    720     }
    721 
    722     /**
    723      * Set phone number by subId
    724      * @param number the phone number of the SIM
    725      * @param subId the unique SubInfoRecord index in database
    726      * @return the number of records updated
    727      */
    728     @Override
    729     public int setDisplayNumber(String number, long subId) {
    730         logd("[setDisplayNumber]+ number:" + number + " subId:" + subId);
    731         enforceSubscriptionPermission();
    732 
    733         validateSubId(subId);
    734         int result = 0;
    735         int phoneId = getPhoneId(subId);
    736 
    737         if (number == null || phoneId < 0 ||
    738                 phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
    739             logd("[setDispalyNumber]- fail");
    740             return -1;
    741         }
    742         ContentValues value = new ContentValues(1);
    743         value.put(SubscriptionManager.NUMBER, number);
    744         logd("[setDisplayNumber]- number:" + number + " set");
    745 
    746         Phone phone = sProxyPhones[phoneId];
    747         String alphaTag = TelephonyManager.getDefault().getLine1AlphaTagForSubscriber(subId);
    748 
    749         synchronized(mLock) {
    750             mSuccess = false;
    751             Message response = mHandler.obtainMessage(EVENT_WRITE_MSISDN_DONE);
    752 
    753             phone.setLine1Number(alphaTag, number, response);
    754 
    755             try {
    756                 mLock.wait();
    757             } catch (InterruptedException e) {
    758                 loge("interrupted while trying to write MSISDN");
    759             }
    760         }
    761 
    762         if (mSuccess) {
    763             result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
    764                     BaseColumns._ID + "=" + Long.toString(subId), null);
    765             logd("[setDisplayNumber]- update result :" + result);
    766             broadcastSimInfoContentChanged(subId, SubscriptionManager.NUMBER,
    767                     SubscriptionManager.DEFAULT_INT_VALUE, number);
    768         }
    769 
    770         return result;
    771     }
    772 
    773     /**
    774      * Set number display format. 0: none, 1: the first four digits, 2: the last four digits
    775      * @param format the display format of phone number
    776      * @param subId the unique SubInfoRecord index in database
    777      * @return the number of records updated
    778      */
    779     @Override
    780     public int setDisplayNumberFormat(int format, long subId) {
    781         logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId);
    782         enforceSubscriptionPermission();
    783 
    784         validateSubId(subId);
    785         if (format < 0) {
    786             logd("[setDisplayNumberFormat]- fail, return -1");
    787             return -1;
    788         }
    789         ContentValues value = new ContentValues(1);
    790         value.put(SubscriptionManager.DISPLAY_NUMBER_FORMAT, format);
    791         logd("[setDisplayNumberFormat]- format:" + format + " set");
    792 
    793         int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
    794                 BaseColumns._ID + "=" + Long.toString(subId), null);
    795         broadcastSimInfoContentChanged(subId, SubscriptionManager.DISPLAY_NUMBER_FORMAT,
    796                 format, SubscriptionManager.DEFAULT_STRING_VALUE);
    797 
    798         return result;
    799     }
    800 
    801     /**
    802      * Set data roaming by simInfo index
    803      * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
    804      * @param subId the unique SubInfoRecord index in database
    805      * @return the number of records updated
    806      */
    807     @Override
    808     public int setDataRoaming(int roaming, long subId) {
    809         logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
    810         enforceSubscriptionPermission();
    811 
    812         validateSubId(subId);
    813         if (roaming < 0) {
    814             logd("[setDataRoaming]- fail");
    815             return -1;
    816         }
    817         ContentValues value = new ContentValues(1);
    818         value.put(SubscriptionManager.DATA_ROAMING, roaming);
    819         logd("[setDataRoaming]- roaming:" + roaming + " set");
    820 
    821         int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
    822                 BaseColumns._ID + "=" + Long.toString(subId), null);
    823         broadcastSimInfoContentChanged(subId, SubscriptionManager.DATA_ROAMING,
    824                 roaming, SubscriptionManager.DEFAULT_STRING_VALUE);
    825 
    826         return result;
    827     }
    828 
    829     /**
    830      * Set MCC/MNC by subscription ID
    831      * @param mccMnc MCC/MNC associated with the subscription
    832      * @param subId the unique SubInfoRecord index in database
    833      * @return the number of records updated
    834      */
    835     public int setMccMnc(String mccMnc, long subId) {
    836         int mcc = 0;
    837         int mnc = 0;
    838         try {
    839             mcc = Integer.parseInt(mccMnc.substring(0,3));
    840             mnc = Integer.parseInt(mccMnc.substring(3));
    841         } catch (NumberFormatException e) {
    842             logd("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
    843         }
    844         logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
    845         ContentValues value = new ContentValues(2);
    846         value.put(SubscriptionManager.MCC, mcc);
    847         value.put(SubscriptionManager.MNC, mnc);
    848 
    849         int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
    850                 BaseColumns._ID + "=" + Long.toString(subId), null);
    851         broadcastSimInfoContentChanged(subId, SubscriptionManager.MCC, mcc, null);
    852 
    853         return result;
    854     }
    855 
    856 
    857     @Override
    858     public int getSlotId(long subId) {
    859         if (VDBG) printStackTrace("[getSlotId] subId=" + subId);
    860 
    861         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
    862             subId = getDefaultSubId();
    863         }
    864         if (!SubscriptionManager.isValidSubId(subId)) {
    865             logd("[getSlotId]- subId invalid");
    866             return SubscriptionManager.INVALID_SLOT_ID;
    867         }
    868 
    869         int size = mSimInfo.size();
    870 
    871         if (size == 0)
    872         {
    873             logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead");
    874             return SubscriptionManager.SIM_NOT_INSERTED;
    875         }
    876 
    877         for (Entry<Integer, Long> entry: mSimInfo.entrySet()) {
    878             int sim = entry.getKey();
    879             long sub = entry.getValue();
    880 
    881             if (subId == sub)
    882             {
    883                 if (VDBG) logv("[getSlotId]- return = " + sim);
    884                 return sim;
    885             }
    886         }
    887 
    888         logd("[getSlotId]- return fail");
    889         return SubscriptionManager.INVALID_SLOT_ID;
    890     }
    891 
    892     /**
    893      * Return the subId for specified slot Id.
    894      * @deprecated
    895      */
    896     @Override
    897     @Deprecated
    898     public long[] getSubId(int slotId) {
    899         if (VDBG) printStackTrace("[getSubId] slotId=" + slotId);
    900 
    901         if (slotId == SubscriptionManager.DEFAULT_SLOT_ID) {
    902             logd("[getSubId]- default slotId");
    903             slotId = getSlotId(getDefaultSubId());
    904         }
    905 
    906         //FIXME remove this
    907         final long[] DUMMY_VALUES = {-1 - slotId, -1 - slotId};
    908 
    909         if (!SubscriptionManager.isValidSlotId(slotId)) {
    910             logd("[getSubId]- invalid slotId");
    911             return null;
    912         }
    913 
    914         //FIXME remove this
    915         if (slotId < 0) {
    916             logd("[getSubId]- slotId < 0, return dummy instead");
    917             return DUMMY_VALUES;
    918         }
    919 
    920         int size = mSimInfo.size();
    921 
    922         if (size == 0) {
    923             logd("[getSubId]- size == 0, return dummy instead");
    924             //FIXME return null
    925             return DUMMY_VALUES;
    926         }
    927 
    928         ArrayList<Long> subIds = new ArrayList<Long>();
    929         for (Entry<Integer, Long> entry: mSimInfo.entrySet()) {
    930             int slot = entry.getKey();
    931             long sub = entry.getValue();
    932             if (slotId == slot) {
    933                 subIds.add(sub);
    934             }
    935         }
    936 
    937         if (VDBG) logd("[getSubId]-, subIds = " + subIds);
    938         int numSubIds = subIds.size();
    939 
    940         if (numSubIds == 0) {
    941             logd("[getSubId]- numSubIds == 0, return dummy instead");
    942             return DUMMY_VALUES;
    943         }
    944 
    945         long[] subIdArr = new long[numSubIds];
    946         for (int i = 0; i < numSubIds; i++) {
    947             subIdArr[i] = subIds.get(i);
    948         }
    949 
    950         return subIdArr;
    951     }
    952 
    953     @Override
    954     public int getPhoneId(long subId) {
    955         if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
    956         int phoneId;
    957 
    958         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
    959             subId = getDefaultSubId();
    960             logdl("[getPhoneId] asked for default subId=" + subId);
    961         }
    962 
    963         if (!SubscriptionManager.isValidSubId(subId)) {
    964             logdl("[getPhoneId]- invalid subId return=" + SubscriptionManager.INVALID_PHONE_ID);
    965             return SubscriptionManager.INVALID_PHONE_ID;
    966         }
    967 
    968         //FIXME remove this
    969         if (subId < 0) {
    970             phoneId = (int) (-1 - subId);
    971             if (VDBG) logdl("[getPhoneId]- map subId=" + subId + " phoneId=" + phoneId);
    972             return phoneId;
    973         }
    974 
    975         int size = mSimInfo.size();
    976 
    977         if (size == 0) {
    978             phoneId = mDefaultPhoneId;
    979             logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
    980             return phoneId;
    981         }
    982 
    983         // FIXME: Assumes phoneId == slotId
    984         for (Entry<Integer, Long> entry: mSimInfo.entrySet()) {
    985             int sim = entry.getKey();
    986             long sub = entry.getValue();
    987 
    988             if (subId == sub) {
    989                 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
    990                 return sim;
    991             }
    992         }
    993 
    994         phoneId = mDefaultPhoneId;
    995         logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId);
    996         return phoneId;
    997 
    998     }
    999 
   1000     /**
   1001      * @return the number of records cleared
   1002      */
   1003     @Override
   1004     public int clearSubInfo() {
   1005         enforceSubscriptionPermission();
   1006         logd("[clearSubInfo]+");
   1007 
   1008         int size = mSimInfo.size();
   1009 
   1010         if (size == 0) {
   1011             logdl("[clearSubInfo]- no simInfo size=" + size);
   1012             return 0;
   1013         }
   1014 
   1015         mSimInfo.clear();
   1016         logdl("[clearSubInfo]- clear size=" + size);
   1017         return size;
   1018     }
   1019 
   1020     private static int[] setSimResource(int type) {
   1021         int[] simResource = null;
   1022 
   1023         switch (type) {
   1024             case RES_TYPE_BACKGROUND_DARK:
   1025                 simResource = new int[] {
   1026                     com.android.internal.R.drawable.sim_dark_blue,
   1027                     com.android.internal.R.drawable.sim_dark_orange,
   1028                     com.android.internal.R.drawable.sim_dark_green,
   1029                     com.android.internal.R.drawable.sim_dark_purple
   1030                 };
   1031                 break;
   1032             case RES_TYPE_BACKGROUND_LIGHT:
   1033                 simResource = new int[] {
   1034                     com.android.internal.R.drawable.sim_light_blue,
   1035                     com.android.internal.R.drawable.sim_light_orange,
   1036                     com.android.internal.R.drawable.sim_light_green,
   1037                     com.android.internal.R.drawable.sim_light_purple
   1038                 };
   1039                 break;
   1040         }
   1041 
   1042         return simResource;
   1043     }
   1044 
   1045     private void logvl(String msg) {
   1046         logv(msg);
   1047         mLocalLog.log(msg);
   1048     }
   1049 
   1050     private void logv(String msg) {
   1051         Rlog.v(LOG_TAG, msg);
   1052     }
   1053 
   1054     private void logdl(String msg) {
   1055         logd(msg);
   1056         mLocalLog.log(msg);
   1057     }
   1058 
   1059     private static void slogd(String msg) {
   1060         Rlog.d(LOG_TAG, msg);
   1061     }
   1062 
   1063     private void logd(String msg) {
   1064         Rlog.d(LOG_TAG, msg);
   1065     }
   1066 
   1067     private void logel(String msg) {
   1068         loge(msg);
   1069         mLocalLog.log(msg);
   1070     }
   1071 
   1072     private void loge(String msg) {
   1073         Rlog.e(LOG_TAG, msg);
   1074     }
   1075 
   1076     @Override
   1077     @Deprecated
   1078     public long getDefaultSubId() {
   1079         //FIXME: Make this smarter, need to handle data only and voice devices
   1080         long subId = mDefaultVoiceSubId;
   1081         if (VDBG) logv("[getDefaultSubId] value = " + subId);
   1082         return subId;
   1083     }
   1084 
   1085     @Override
   1086     public void setDefaultSmsSubId(long subId) {
   1087         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
   1088             throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
   1089         }
   1090         logdl("[setDefaultSmsSubId] subId=" + subId);
   1091         Settings.Global.putLong(mContext.getContentResolver(),
   1092                 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
   1093         broadcastDefaultSmsSubIdChanged(subId);
   1094     }
   1095 
   1096     private void broadcastDefaultSmsSubIdChanged(long subId) {
   1097         // Broadcast an Intent for default sms sub change
   1098         logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
   1099         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
   1100         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1101         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
   1102         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1103     }
   1104 
   1105     @Override
   1106     public long getDefaultSmsSubId() {
   1107         long subId = Settings.Global.getLong(mContext.getContentResolver(),
   1108                 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
   1109                 SubscriptionManager.INVALID_SUB_ID);
   1110         if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId);
   1111         return subId;
   1112     }
   1113 
   1114     @Override
   1115     public void setDefaultVoiceSubId(long subId) {
   1116         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
   1117             throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
   1118         }
   1119         logdl("[setDefaultVoiceSubId] subId=" + subId);
   1120         Settings.Global.putLong(mContext.getContentResolver(),
   1121                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
   1122         broadcastDefaultVoiceSubIdChanged(subId);
   1123     }
   1124 
   1125     private void broadcastDefaultVoiceSubIdChanged(long subId) {
   1126         // Broadcast an Intent for default voice sub change
   1127         logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
   1128         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
   1129         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1130         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
   1131         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1132     }
   1133 
   1134     @Override
   1135     public long getDefaultVoiceSubId() {
   1136         long subId = Settings.Global.getLong(mContext.getContentResolver(),
   1137                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
   1138                 SubscriptionManager.INVALID_SUB_ID);
   1139         if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
   1140         return subId;
   1141     }
   1142 
   1143     @Override
   1144     public long getDefaultDataSubId() {
   1145         long subId = Settings.Global.getLong(mContext.getContentResolver(),
   1146                 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
   1147                 SubscriptionManager.INVALID_SUB_ID);
   1148         if (VDBG) logd("[getDefaultDataSubId] subId= " + subId);
   1149         return subId;
   1150     }
   1151 
   1152     @Override
   1153     public void setDefaultDataSubId(long subId) {
   1154         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
   1155             throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
   1156         }
   1157         logdl("[setDefaultDataSubId] subId=" + subId);
   1158 
   1159         Settings.Global.putLong(mContext.getContentResolver(),
   1160                 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
   1161         broadcastDefaultDataSubIdChanged(subId);
   1162 
   1163         // FIXME is this still needed?
   1164         updateAllDataConnectionTrackers();
   1165     }
   1166 
   1167     private void updateAllDataConnectionTrackers() {
   1168         // Tell Phone Proxies to update data connection tracker
   1169         int len = sProxyPhones.length;
   1170         logdl("[updateAllDataConnectionTrackers] sProxyPhones.length=" + len);
   1171         for (int phoneId = 0; phoneId < len; phoneId++) {
   1172             logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId);
   1173             sProxyPhones[phoneId].updateDataConnectionTracker();
   1174         }
   1175     }
   1176 
   1177     private void broadcastDefaultDataSubIdChanged(long subId) {
   1178         // Broadcast an Intent for default data sub change
   1179         logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
   1180         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
   1181         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1182         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
   1183         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1184     }
   1185 
   1186     /* Sets the default subscription. If only one sub is active that
   1187      * sub is set as default subId. If two or more  sub's are active
   1188      * the first sub is set as default subscription
   1189      */
   1190     // FIXME
   1191     public void setDefaultSubId(long subId) {
   1192         if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
   1193             throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
   1194         }
   1195         logdl("[setDefaultSubId] subId=" + subId);
   1196         if (SubscriptionManager.isValidSubId(subId)) {
   1197             int phoneId = getPhoneId(subId);
   1198             if (phoneId >= 0 && (phoneId < TelephonyManager.getDefault().getPhoneCount()
   1199                     || TelephonyManager.getDefault().getSimCount() == 1)) {
   1200                 logdl("[setDefaultSubId] set mDefaultVoiceSubId=" + subId);
   1201                 mDefaultVoiceSubId = subId;
   1202                 // Update MCC MNC device configuration information
   1203                 String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId);
   1204                 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);
   1205 
   1206                 // Broadcast an Intent for default sub change
   1207                 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
   1208                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1209                 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
   1210                 if (VDBG) {
   1211                     logdl("[setDefaultSubId] broadcast default subId changed phoneId=" + phoneId
   1212                             + " subId=" + subId);
   1213                 }
   1214                 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1215             } else {
   1216                 if (VDBG) {
   1217                     logdl("[setDefaultSubId] not set invalid phoneId=" + phoneId + " subId=" + subId);
   1218                 }
   1219             }
   1220         }
   1221     }
   1222 
   1223     @Override
   1224     public void clearDefaultsForInactiveSubIds() {
   1225         final List<SubInfoRecord> records = getActiveSubInfoList();
   1226         logdl("[clearDefaultsForInactiveSubIds] records: " + records);
   1227         if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
   1228             logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
   1229             setDefaultDataSubId(SubscriptionManager.INVALID_SUB_ID);
   1230         }
   1231         if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
   1232             logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
   1233             setDefaultSmsSubId(SubscriptionManager.INVALID_SUB_ID);
   1234         }
   1235         if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
   1236             logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
   1237             setDefaultVoiceSubId(SubscriptionManager.INVALID_SUB_ID);
   1238         }
   1239     }
   1240 
   1241     private boolean shouldDefaultBeCleared(List<SubInfoRecord> records, long subId) {
   1242         logdl("[shouldDefaultBeCleared: subId] " + subId);
   1243         if (records == null) {
   1244             logdl("[shouldDefaultBeCleared] return true no records subId=" + subId);
   1245             return true;
   1246         }
   1247         if (subId == SubscriptionManager.ASK_USER_SUB_ID && records.size() > 1) {
   1248             // Only allow ASK_USER_SUB_ID if there is more than 1 subscription.
   1249             logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId);
   1250             return false;
   1251         }
   1252         for (SubInfoRecord record : records) {
   1253             logdl("[shouldDefaultBeCleared] Record.subId: " + record.subId);
   1254             if (record.subId == subId) {
   1255                 logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId);
   1256                 return false;
   1257             }
   1258         }
   1259         logdl("[shouldDefaultBeCleared] return true not active subId=" + subId);
   1260         return true;
   1261     }
   1262 
   1263     /* This should return long and not long [] since each phone has
   1264      * exactly 1 sub id for now, it could return the 0th element
   1265      * returned from getSubId()
   1266      */
   1267     // FIXME will design a mechanism to manage the relationship between PhoneId/SlotId/SubId
   1268     // since phoneId = SlotId is not always true
   1269     public long getSubIdUsingPhoneId(int phoneId) {
   1270         long[] subIds = getSubId(phoneId);
   1271         if (subIds == null || subIds.length == 0) {
   1272             return SubscriptionManager.INVALID_SUB_ID;
   1273         }
   1274         return subIds[0];
   1275     }
   1276 
   1277     public long[] getSubIdUsingSlotId(int slotId) {
   1278         return getSubId(slotId);
   1279     }
   1280 
   1281     public List<SubInfoRecord> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) {
   1282         logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
   1283         enforceSubscriptionPermission();
   1284 
   1285         if (slotId == SubscriptionManager.DEFAULT_SLOT_ID) {
   1286             slotId = getSlotId(getDefaultSubId());
   1287         }
   1288         if (!SubscriptionManager.isValidSlotId(slotId)) {
   1289             logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
   1290             return null;
   1291         }
   1292 
   1293         if (needCheck && !isSubInfoReady()) {
   1294             logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
   1295             return null;
   1296         }
   1297 
   1298         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
   1299                 null, SubscriptionManager.SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null);
   1300         ArrayList<SubInfoRecord> subList = null;
   1301         try {
   1302             if (cursor != null) {
   1303                 while (cursor.moveToNext()) {
   1304                     SubInfoRecord subInfo = getSubInfoRecord(cursor);
   1305                     if (subInfo != null)
   1306                     {
   1307                         if (subList == null)
   1308                         {
   1309                             subList = new ArrayList<SubInfoRecord>();
   1310                         }
   1311                         subList.add(subInfo);
   1312                     }
   1313                 }
   1314             }
   1315         } finally {
   1316             if (cursor != null) {
   1317                 cursor.close();
   1318             }
   1319         }
   1320         logd("[getSubInfoUsingSlotId]- null info return");
   1321 
   1322         return subList;
   1323     }
   1324 
   1325     private void validateSubId(long subId) {
   1326         logd("validateSubId subId: " + subId);
   1327         if (!SubscriptionManager.isValidSubId(subId)) {
   1328             throw new RuntimeException("Invalid sub id passed as parameter");
   1329         } else if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
   1330             throw new RuntimeException("Default sub id passed as parameter");
   1331         }
   1332     }
   1333 
   1334     public void updatePhonesAvailability(PhoneProxy[] phones) {
   1335         sProxyPhones = phones;
   1336     }
   1337 
   1338     /**
   1339      * @return the list of subId's that are active, is never null but the length maybe 0.
   1340      */
   1341     @Override
   1342     public long[] getActiveSubIdList() {
   1343         Set<Entry<Integer, Long>> simInfoSet = mSimInfo.entrySet();
   1344         logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet);
   1345 
   1346         long[] subIdArr = new long[simInfoSet.size()];
   1347         int i = 0;
   1348         for (Entry<Integer, Long> entry: simInfoSet) {
   1349             long sub = entry.getValue();
   1350             subIdArr[i] = sub;
   1351             i++;
   1352         }
   1353 
   1354         logdl("[getActiveSubIdList] X subIdArr.length=" + subIdArr.length);
   1355         return subIdArr;
   1356     }
   1357 
   1358     private static void printStackTrace(String msg) {
   1359         RuntimeException re = new RuntimeException();
   1360         slogd("StackTrace - " + msg);
   1361         StackTraceElement[] st = re.getStackTrace();
   1362         boolean first = true;
   1363         for (StackTraceElement ste : st) {
   1364             if (first) {
   1365                 first = false;
   1366             } else {
   1367                 slogd(ste.toString());
   1368             }
   1369         }
   1370     }
   1371 
   1372     @Override
   1373     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1374         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
   1375                 "Requires DUMP");
   1376         pw.println("SubscriptionController:");
   1377         pw.println(" defaultSubId=" + getDefaultSubId());
   1378         pw.println(" defaultDataSubId=" + getDefaultDataSubId());
   1379         pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
   1380         pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());
   1381 
   1382         pw.println(" defaultDataPhoneId=" + SubscriptionManager.getDefaultDataPhoneId());
   1383         pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
   1384         pw.println(" defaultSmsPhoneId=" + SubscriptionManager.getDefaultSmsPhoneId());
   1385         pw.flush();
   1386 
   1387         for (Entry<Integer, Long> entry : mSimInfo.entrySet()) {
   1388             pw.println(" mSimInfo[" + entry.getKey() + "]: subId=" + entry.getValue());
   1389         }
   1390         pw.flush();
   1391         pw.println("++++++++++++++++++++++++++++++++");
   1392 
   1393         List<SubInfoRecord> sirl = getActiveSubInfoList();
   1394         if (sirl != null) {
   1395             pw.println(" ActiveSubInfoList:");
   1396             for (SubInfoRecord entry : sirl) {
   1397                 pw.println("  " + entry.toString());
   1398             }
   1399         } else {
   1400             pw.println(" ActiveSubInfoList: is null");
   1401         }
   1402         pw.flush();
   1403         pw.println("++++++++++++++++++++++++++++++++");
   1404 
   1405         sirl = getAllSubInfoList();
   1406         if (sirl != null) {
   1407             pw.println(" AllSubInfoList:");
   1408             for (SubInfoRecord entry : sirl) {
   1409                 pw.println("  " + entry.toString());
   1410             }
   1411         } else {
   1412             pw.println(" AllSubInfoList: is null");
   1413         }
   1414         pw.flush();
   1415         pw.println("++++++++++++++++++++++++++++++++");
   1416 
   1417         mLocalLog.dump(fd, pw, args);
   1418         pw.flush();
   1419         pw.println("++++++++++++++++++++++++++++++++");
   1420         pw.flush();
   1421     }
   1422 }
   1423