Home | History | Annotate | Download | only in telephony
      1 /*
      2 * Copyright (C) 2014 The Android Open Source Project
      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.app.AppOpsManager;
     20 import android.content.ContentResolver;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.PackageManager;
     25 import android.database.Cursor;
     26 import android.graphics.Bitmap;
     27 import android.graphics.BitmapFactory;
     28 import android.net.Uri;
     29 import android.os.Binder;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.os.UserHandle;
     33 import android.provider.Settings;
     34 import android.telephony.RadioAccessFamily;
     35 import android.telephony.Rlog;
     36 import android.telephony.SubscriptionInfo;
     37 import android.telephony.SubscriptionManager;
     38 import android.telephony.TelephonyManager;
     39 import android.text.TextUtils;
     40 import android.text.format.Time;
     41 import android.util.Log;
     42 import java.util.Objects;
     43 import com.android.internal.telephony.IccCardConstants.State;
     44 
     45 import java.io.FileDescriptor;
     46 import java.io.PrintWriter;
     47 import java.util.ArrayList;
     48 import java.util.Collections;
     49 import java.util.Comparator;
     50 import java.util.Iterator;
     51 import java.util.LinkedList;
     52 import java.util.List;
     53 import java.util.Map;
     54 import java.util.Map.Entry;
     55 import java.util.Set;
     56 import java.util.concurrent.ConcurrentHashMap;
     57 
     58 /**
     59  * SubscriptionController to provide an inter-process communication to
     60  * access Sms in Icc.
     61  *
     62  * Any setters which take subId, slotId or phoneId as a parameter will throw an exception if the
     63  * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID.
     64  *
     65  * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling
     66  * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()).
     67  *
     68  * Finally, any getters which perform the mapping between subscriptions, slots and phones will
     69  * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters
     70  * will fail and return the appropriate error value. Ie calling getSlotId(INVALID_SUBSCRIPTION_ID)
     71  * will return INVALID_SLOT_ID and calling getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID)
     72  * will return null.
     73  *
     74  */
     75 public class SubscriptionController extends ISub.Stub {
     76     static final String LOG_TAG = "SubscriptionController";
     77     static final boolean DBG = true;
     78     static final boolean VDBG = false;
     79     static final int MAX_LOCAL_LOG_LINES = 500; // TODO: Reduce to 100 when 17678050 is fixed
     80     private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);
     81 
     82     /**
     83      * Copied from android.util.LocalLog with flush() adding flush and line number
     84      * TODO: Update LocalLog
     85      */
     86     static class ScLocalLog {
     87 
     88         private LinkedList<String> mLog;
     89         private int mMaxLines;
     90         private Time mNow;
     91 
     92         public ScLocalLog(int maxLines) {
     93             mLog = new LinkedList<String>();
     94             mMaxLines = maxLines;
     95             mNow = new Time();
     96         }
     97 
     98         public synchronized void log(String msg) {
     99             if (mMaxLines > 0) {
    100                 int pid = android.os.Process.myPid();
    101                 int tid = android.os.Process.myTid();
    102                 mNow.setToNow();
    103                 mLog.add(mNow.format("%m-%d %H:%M:%S") + " pid=" + pid + " tid=" + tid + " " + msg);
    104                 while (mLog.size() > mMaxLines) mLog.remove();
    105             }
    106         }
    107 
    108         public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    109             final int LOOPS_PER_FLUSH = 10; // Flush every N loops.
    110             Iterator<String> itr = mLog.listIterator(0);
    111             int i = 0;
    112             while (itr.hasNext()) {
    113                 pw.println(Integer.toString(i++) + ": " + itr.next());
    114                 // Flush periodically so we don't drop lines
    115                 if ((i % LOOPS_PER_FLUSH) == 0) pw.flush();
    116             }
    117         }
    118     }
    119 
    120     protected final Object mLock = new Object();
    121 
    122     /** The singleton instance. */
    123     private static SubscriptionController sInstance = null;
    124     protected static PhoneProxy[] sProxyPhones;
    125     protected Context mContext;
    126     protected TelephonyManager mTelephonyManager;
    127     protected CallManager mCM;
    128 
    129     private final AppOpsManager mAppOps;
    130 
    131     // FIXME: Does not allow for multiple subs in a slot and change to SparseArray
    132     private static Map<Integer, Integer> sSlotIdxToSubId =
    133             new ConcurrentHashMap<Integer, Integer>();
    134     private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    135     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
    136 
    137     private int[] colorArr;
    138 
    139     public static SubscriptionController init(Phone phone) {
    140         synchronized (SubscriptionController.class) {
    141             if (sInstance == null) {
    142                 sInstance = new SubscriptionController(phone);
    143             } else {
    144                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    145             }
    146             return sInstance;
    147         }
    148     }
    149 
    150     public static SubscriptionController init(Context c, CommandsInterface[] ci) {
    151         synchronized (SubscriptionController.class) {
    152             if (sInstance == null) {
    153                 sInstance = new SubscriptionController(c);
    154             } else {
    155                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    156             }
    157             return sInstance;
    158         }
    159     }
    160 
    161     public static SubscriptionController getInstance() {
    162         if (sInstance == null)
    163         {
    164            Log.wtf(LOG_TAG, "getInstance null");
    165         }
    166 
    167         return sInstance;
    168     }
    169 
    170     private SubscriptionController(Context c) {
    171         mContext = c;
    172         mCM = CallManager.getInstance();
    173         mTelephonyManager = TelephonyManager.from(mContext);
    174         mAppOps = mContext.getSystemService(AppOpsManager.class);
    175 
    176         if(ServiceManager.getService("isub") == null) {
    177                 ServiceManager.addService("isub", this);
    178         }
    179 
    180         if (DBG) logdl("[SubscriptionController] init by Context");
    181     }
    182 
    183     private boolean isSubInfoReady() {
    184         return sSlotIdxToSubId.size() > 0;
    185     }
    186 
    187     private SubscriptionController(Phone phone) {
    188         mContext = phone.getContext();
    189         mCM = CallManager.getInstance();
    190         mAppOps = mContext.getSystemService(AppOpsManager.class);
    191 
    192         if(ServiceManager.getService("isub") == null) {
    193                 ServiceManager.addService("isub", this);
    194         }
    195 
    196         if (DBG) logdl("[SubscriptionController] init by Phone");
    197     }
    198 
    199     /**
    200      * Make sure the caller can read phone state which requires holding the
    201      * READ_PHONE_STATE permission and the OP_READ_PHONE_STATE app op being
    202      * set to MODE_ALLOWED.
    203      *
    204      * @param callingPackage The package claiming to make the IPC.
    205      * @param message The name of the access protected method.
    206      *
    207      * @throws SecurityException if the caller does not have READ_PHONE_STATE permission.
    208      */
    209     private boolean canReadPhoneState(String callingPackage, String message) {
    210         try {
    211             mContext.enforceCallingOrSelfPermission(
    212                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
    213 
    214             // SKIP checking run-time permission since self or using PRIVILEDGED permission
    215             return true;
    216         } catch (SecurityException e) {
    217             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
    218                     message);
    219         }
    220 
    221         return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
    222                 callingPackage) == AppOpsManager.MODE_ALLOWED;
    223     }
    224 
    225     private void enforceModifyPhoneState(String message) {
    226         mContext.enforceCallingOrSelfPermission(
    227                 android.Manifest.permission.MODIFY_PHONE_STATE, message);
    228     }
    229 
    230     /**
    231      * Broadcast when SubscriptionInfo has changed
    232      * FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener
    233      */
    234      private void broadcastSimInfoContentChanged() {
    235         Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
    236         mContext.sendBroadcast(intent);
    237         intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
    238         mContext.sendBroadcast(intent);
    239      }
    240 
    241      public void notifySubscriptionInfoChanged() {
    242          ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
    243                  "telephony.registry"));
    244          try {
    245              if (DBG) logd("notifySubscriptionInfoChanged:");
    246              tr.notifySubscriptionInfoChanged();
    247          } catch (RemoteException ex) {
    248              // Should never happen because its always available.
    249          }
    250 
    251          // FIXME: Remove if listener technique accepted.
    252          broadcastSimInfoContentChanged();
    253      }
    254 
    255     /**
    256      * New SubInfoRecord instance and fill in detail info
    257      * @param cursor
    258      * @return the query result of desired SubInfoRecord
    259      */
    260     private SubscriptionInfo getSubInfoRecord(Cursor cursor) {
    261         int id = cursor.getInt(cursor.getColumnIndexOrThrow(
    262                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
    263         String iccId = cursor.getString(cursor.getColumnIndexOrThrow(
    264                 SubscriptionManager.ICC_ID));
    265         int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
    266                 SubscriptionManager.SIM_SLOT_INDEX));
    267         String displayName = cursor.getString(cursor.getColumnIndexOrThrow(
    268                 SubscriptionManager.DISPLAY_NAME));
    269         String carrierName = cursor.getString(cursor.getColumnIndexOrThrow(
    270                 SubscriptionManager.CARRIER_NAME));
    271         int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
    272                 SubscriptionManager.NAME_SOURCE));
    273         int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow(
    274                 SubscriptionManager.COLOR));
    275         String number = cursor.getString(cursor.getColumnIndexOrThrow(
    276                 SubscriptionManager.NUMBER));
    277         int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
    278                 SubscriptionManager.DATA_ROAMING));
    279         // Get the blank bitmap for this SubInfoRecord
    280         Bitmap iconBitmap = BitmapFactory.decodeResource(mContext.getResources(),
    281                 com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
    282         int mcc = cursor.getInt(cursor.getColumnIndexOrThrow(
    283                 SubscriptionManager.MCC));
    284         int mnc = cursor.getInt(cursor.getColumnIndexOrThrow(
    285                 SubscriptionManager.MNC));
    286         // FIXME: consider stick this into database too
    287         String countryIso = getSubscriptionCountryIso(id);
    288 
    289         if (DBG) {
    290             logd("[getSubInfoRecord] id:" + id + " iccid:" + iccId + " simSlotIndex:" + simSlotIndex
    291                 + " displayName:" + displayName + " nameSource:" + nameSource
    292                 + " iconTint:" + iconTint + " dataRoaming:" + dataRoaming
    293                 + " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso);
    294         }
    295 
    296         // If line1number has been set to a different number, use it instead.
    297         String line1Number = mTelephonyManager.getLine1NumberForSubscriber(id);
    298         if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
    299             number = line1Number;
    300         }
    301         return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
    302                 nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso);
    303     }
    304 
    305     /**
    306      * Get ISO country code for the subscription's provider
    307      *
    308      * @param subId The subscription ID
    309      * @return The ISO country code for the subscription's provider
    310      */
    311     private String getSubscriptionCountryIso(int subId) {
    312         final int phoneId = getPhoneId(subId);
    313         if (phoneId < 0) {
    314             return "";
    315         }
    316         return mTelephonyManager.getSimCountryIsoForPhone(phoneId);
    317     }
    318 
    319     /**
    320      * Query SubInfoRecord(s) from subinfo database
    321      * @param selection A filter declaring which rows to return
    322      * @param queryKey query key content
    323      * @return Array list of queried result from database
    324      */
    325      private List<SubscriptionInfo> getSubInfo(String selection, Object queryKey) {
    326         if (DBG) logd("selection:" + selection + " " + queryKey);
    327         String[] selectionArgs = null;
    328         if (queryKey != null) {
    329             selectionArgs = new String[] {queryKey.toString()};
    330         }
    331         ArrayList<SubscriptionInfo> subList = null;
    332         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
    333                 null, selection, selectionArgs, null);
    334         try {
    335             if (cursor != null) {
    336                 while (cursor.moveToNext()) {
    337                     SubscriptionInfo subInfo = getSubInfoRecord(cursor);
    338                     if (subInfo != null)
    339                     {
    340                         if (subList == null)
    341                         {
    342                             subList = new ArrayList<SubscriptionInfo>();
    343                         }
    344                         subList.add(subInfo);
    345                 }
    346                 }
    347             } else {
    348                 if (DBG) logd("Query fail");
    349             }
    350         } finally {
    351             if (cursor != null) {
    352                 cursor.close();
    353             }
    354         }
    355 
    356         return subList;
    357     }
    358 
    359     /**
    360      * Find unused color to be set for new SubInfoRecord
    361      * @param callingPackage The package making the IPC.
    362      * @return RGB integer value of color
    363      */
    364     private int getUnusedColor(String callingPackage) {
    365         List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage);
    366         colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
    367         int colorIdx = 0;
    368 
    369         if (availableSubInfos != null) {
    370             for (int i = 0; i < colorArr.length; i++) {
    371                 int j;
    372                 for (j = 0; j < availableSubInfos.size(); j++) {
    373                     if (colorArr[i] == availableSubInfos.get(j).getIconTint()) {
    374                         break;
    375                     }
    376                 }
    377                 if (j == availableSubInfos.size()) {
    378                     return colorArr[i];
    379                 }
    380             }
    381             colorIdx = availableSubInfos.size() % colorArr.length;
    382         }
    383         return colorArr[colorIdx];
    384     }
    385 
    386     /**
    387      * Get the active SubscriptionInfo with the subId key
    388      * @param subId The unique SubscriptionInfo key in database
    389      * @param callingPackage The package making the IPC.
    390      * @return SubscriptionInfo, maybe null if its not active
    391      */
    392     @Override
    393     public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
    394         if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfo")) {
    395             return null;
    396         }
    397 
    398         // Now that all security checks passes, perform the operation as ourselves.
    399         final long identity = Binder.clearCallingIdentity();
    400         try {
    401             List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
    402                     mContext.getOpPackageName());
    403             if (subList != null) {
    404                 for (SubscriptionInfo si : subList) {
    405                     if (si.getSubscriptionId() == subId) {
    406                         if (DBG)
    407                             logd("[getActiveSubInfoForSubscriber]+ subId=" + subId
    408                                     + " subInfo=" + si);
    409                         return si;
    410                     }
    411                 }
    412             }
    413             if (DBG) {
    414                 logd("[getActiveSubInfoForSubscriber]- subId=" + subId
    415                         + " subList=" + subList + " subInfo=null");
    416             }
    417         } finally {
    418             Binder.restoreCallingIdentity(identity);
    419         }
    420 
    421         return null;
    422     }
    423 
    424     /**
    425      * Get the active SubscriptionInfo associated with the iccId
    426      * @param iccId the IccId of SIM card
    427      * @param callingPackage The package making the IPC.
    428      * @return SubscriptionInfo, maybe null if its not active
    429      */
    430     @Override
    431     public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage) {
    432         if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForIccId")) {
    433             return null;
    434         }
    435 
    436         // Now that all security checks passes, perform the operation as ourselves.
    437         final long identity = Binder.clearCallingIdentity();
    438         try {
    439             List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
    440                     mContext.getOpPackageName());
    441             if (subList != null) {
    442                 for (SubscriptionInfo si : subList) {
    443                     if (si.getIccId() == iccId) {
    444                         if (DBG)
    445                             logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
    446                         return si;
    447                     }
    448                 }
    449             }
    450             if (DBG) {
    451                 logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
    452                         + " subList=" + subList + " subInfo=null");
    453             }
    454         } finally {
    455             Binder.restoreCallingIdentity(identity);
    456         }
    457 
    458         return null;
    459     }
    460 
    461     /**
    462      * Get the active SubscriptionInfo associated with the slotIdx
    463      * @param slotIdx the slot which the subscription is inserted
    464      * @param callingPackage The package making the IPC.
    465      * @return SubscriptionInfo, maybe null if its not active
    466      */
    467     @Override
    468     public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx,
    469             String callingPackage) {
    470         if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForSimSlotIndex")) {
    471             return null;
    472         }
    473 
    474         // Now that all security checks passes, perform the operation as ourselves.
    475         final long identity = Binder.clearCallingIdentity();
    476         try {
    477             List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
    478                     mContext.getOpPackageName());
    479             if (subList != null) {
    480                 for (SubscriptionInfo si : subList) {
    481                     if (si.getSimSlotIndex() == slotIdx) {
    482                         if (DBG) {
    483                             logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
    484                                     + " subId=" + si);
    485                         }
    486                         return si;
    487                     }
    488                 }
    489                 if (DBG) {
    490                     logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
    491                             + " subId=null");
    492                 }
    493             } else {
    494                 if (DBG) {
    495                     logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
    496                 }
    497             }
    498         } finally {
    499             Binder.restoreCallingIdentity(identity);
    500         }
    501 
    502         return null;
    503     }
    504 
    505     /**
    506      * @param callingPackage The package making the IPC.
    507      * @return List of all SubscriptionInfo records in database,
    508      * include those that were inserted before, maybe empty but not null.
    509      * @hide
    510      */
    511     @Override
    512     public List<SubscriptionInfo> getAllSubInfoList(String callingPackage) {
    513         if (DBG) logd("[getAllSubInfoList]+");
    514 
    515         if (!canReadPhoneState(callingPackage, "getAllSubInfoList")) {
    516             return null;
    517         }
    518 
    519         // Now that all security checks passes, perform the operation as ourselves.
    520         final long identity = Binder.clearCallingIdentity();
    521         try {
    522             List<SubscriptionInfo> subList = null;
    523             subList = getSubInfo(null, null);
    524             if (subList != null) {
    525                 if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
    526             } else {
    527                 if (DBG) logd("[getAllSubInfoList]- no info return");
    528             }
    529             return subList;
    530         } finally {
    531             Binder.restoreCallingIdentity(identity);
    532         }
    533     }
    534 
    535     /**
    536      * Get the SubInfoRecord(s) of the currently inserted SIM(s)
    537      * @param callingPackage The package making the IPC.
    538      * @return Array list of currently inserted SubInfoRecord(s)
    539      */
    540     @Override
    541     public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
    542         if (DBG) logdl("[getActiveSubInfoList]+");
    543 
    544         if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoList")) {
    545             return null;
    546         }
    547 
    548         // Now that all security checks passes, perform the operation as ourselves.
    549         final long identity = Binder.clearCallingIdentity();
    550         try {
    551             if (!isSubInfoReady()) {
    552                 if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
    553                 return null;
    554             }
    555 
    556             List<SubscriptionInfo> subList = getSubInfo(
    557                     SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
    558 
    559             if (subList != null) {
    560                 // FIXME: Unnecessary when an insertion sort is used!
    561                 Collections.sort(subList, new Comparator<SubscriptionInfo>() {
    562                     @Override
    563                     public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) {
    564                         // Primary sort key on SimSlotIndex
    565                         int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
    566                         if (flag == 0) {
    567                             // Secondary sort on SubscriptionId
    568                             return arg0.getSubscriptionId() - arg1.getSubscriptionId();
    569                         }
    570                         return flag;
    571                     }
    572                 });
    573 
    574                 if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
    575             } else {
    576                 if (DBG) logdl("[getActiveSubInfoList]- no info return");
    577             }
    578 
    579             return subList;
    580         } finally {
    581             Binder.restoreCallingIdentity(identity);
    582         }
    583     }
    584 
    585     /**
    586      * Get the SUB count of active SUB(s)
    587      * @param callingPackage The package making the IPC.
    588      * @return active SIM count
    589      */
    590     @Override
    591     public int getActiveSubInfoCount(String callingPackage) {
    592         if (DBG) logd("[getActiveSubInfoCount]+");
    593 
    594         if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
    595             return 0;
    596         }
    597 
    598         // Now that all security checks passes, perform the operation as ourselves.
    599         final long identity = Binder.clearCallingIdentity();
    600         try {
    601             List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
    602                     mContext.getOpPackageName());
    603             if (records == null) {
    604                 if (DBG) logd("[getActiveSubInfoCount] records null");
    605                 return 0;
    606             }
    607             if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
    608             return records.size();
    609         } finally {
    610             Binder.restoreCallingIdentity(identity);
    611         }
    612     }
    613 
    614     /**
    615      * Get the SUB count of all SUB(s) in SubscriptoinInfo database
    616      * @param callingPackage The package making the IPC.
    617      * @return all SIM count in database, include what was inserted before
    618      */
    619     @Override
    620     public int getAllSubInfoCount(String callingPackage) {
    621         if (DBG) logd("[getAllSubInfoCount]+");
    622 
    623         if (!canReadPhoneState(callingPackage, "getAllSubInfoCount")) {
    624             return 0;
    625         }
    626 
    627         // Now that all security checks passes, perform the operation as ourselves.
    628         final long identity = Binder.clearCallingIdentity();
    629         try {
    630             Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
    631                     null, null, null, null);
    632             try {
    633                 if (cursor != null) {
    634                     int count = cursor.getCount();
    635                     if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
    636                     return count;
    637                 }
    638             } finally {
    639                 if (cursor != null) {
    640                     cursor.close();
    641                 }
    642             }
    643             if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
    644 
    645             return 0;
    646         } finally {
    647             Binder.restoreCallingIdentity(identity);
    648         }
    649     }
    650 
    651     /**
    652      * @return the maximum number of subscriptions this device will support at any one time.
    653      */
    654     @Override
    655     public int getActiveSubInfoCountMax() {
    656         // FIXME: This valid now but change to use TelephonyDevController in the future
    657         return mTelephonyManager.getSimCount();
    658     }
    659 
    660     /**
    661      * Add a new SubInfoRecord to subinfo database if needed
    662      * @param iccId the IccId of the SIM card
    663      * @param slotId the slot which the SIM is inserted
    664      * @return 0 if success, < 0 on error.
    665      */
    666     @Override
    667     public int addSubInfoRecord(String iccId, int slotId) {
    668         if (DBG) logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
    669 
    670         enforceModifyPhoneState("addSubInfoRecord");
    671 
    672         // Now that all security checks passes, perform the operation as ourselves.
    673         final long identity = Binder.clearCallingIdentity();
    674         try {
    675             if (iccId == null) {
    676                 if (DBG) logdl("[addSubInfoRecord]- null iccId");
    677                 return -1;
    678             }
    679 
    680             ContentResolver resolver = mContext.getContentResolver();
    681             Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
    682                     new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
    683                             SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
    684                     SubscriptionManager.ICC_ID + "=?", new String[]{iccId}, null);
    685 
    686             int color = getUnusedColor(mContext.getOpPackageName());
    687             boolean setDisplayName = false;
    688             try {
    689                 if (cursor == null || !cursor.moveToFirst()) {
    690                     setDisplayName = true;
    691                     ContentValues value = new ContentValues();
    692                     value.put(SubscriptionManager.ICC_ID, iccId);
    693                     // default SIM color differs between slots
    694                     value.put(SubscriptionManager.COLOR, color);
    695                     value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
    696                     value.put(SubscriptionManager.CARRIER_NAME, "");
    697                     Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
    698                     if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
    699                 } else {
    700                     int subId = cursor.getInt(0);
    701                     int oldSimInfoId = cursor.getInt(1);
    702                     int nameSource = cursor.getInt(2);
    703                     ContentValues value = new ContentValues();
    704 
    705                     if (slotId != oldSimInfoId) {
    706                         value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
    707                     }
    708 
    709                     if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
    710                         setDisplayName = true;
    711                     }
    712 
    713                     if (value.size() > 0) {
    714                         resolver.update(SubscriptionManager.CONTENT_URI, value,
    715                                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
    716                                         "=" + Long.toString(subId), null);
    717                     }
    718 
    719                     if (DBG) logdl("[addSubInfoRecord] Record already exists");
    720                 }
    721             } finally {
    722                 if (cursor != null) {
    723                     cursor.close();
    724                 }
    725             }
    726 
    727             cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
    728                     SubscriptionManager.SIM_SLOT_INDEX + "=?",
    729                     new String[] {String.valueOf(slotId)}, null);
    730             try {
    731                 if (cursor != null && cursor.moveToFirst()) {
    732                     do {
    733                         int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
    734                                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
    735                         // If sSlotIdxToSubId already has a valid subId for a slotId/phoneId,
    736                         // do not add another subId for same slotId/phoneId.
    737                         Integer currentSubId = sSlotIdxToSubId.get(slotId);
    738                         if (currentSubId == null
    739                                 || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
    740                             // TODO While two subs active, if user deactivats first
    741                             // one, need to update the default subId with second one.
    742 
    743                             // FIXME: Currently we assume phoneId == slotId which in the future
    744                             // may not be true, for instance with multiple subs per slot.
    745                             // But is true at the moment.
    746                             sSlotIdxToSubId.put(slotId, subId);
    747                             int subIdCountMax = getActiveSubInfoCountMax();
    748                             int defaultSubId = getDefaultSubId();
    749                             if (DBG) {
    750                                 logdl("[addSubInfoRecord]"
    751                                         + " sSlotIdxToSubId.size=" + sSlotIdxToSubId.size()
    752                                         + " slotId=" + slotId + " subId=" + subId
    753                                         + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
    754                             }
    755 
    756                             // Set the default sub if not set or if single sim device
    757                             if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
    758                                     || subIdCountMax == 1) {
    759                                 setDefaultFallbackSubId(subId);
    760                             }
    761                             // If single sim device, set this subscription as the default for everything
    762                             if (subIdCountMax == 1) {
    763                                 if (DBG) {
    764                                     logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
    765                                 }
    766                                 setDefaultDataSubId(subId);
    767                                 setDefaultSmsSubId(subId);
    768                                 setDefaultVoiceSubId(subId);
    769                             }
    770                         } else {
    771                             if (DBG) {
    772                                 logdl("[addSubInfoRecord] currentSubId != null"
    773                                         + " && currentSubId is valid, IGNORE");
    774                             }
    775                         }
    776                         if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
    777                     } while (cursor.moveToNext());
    778                 }
    779             } finally {
    780                 if (cursor != null) {
    781                     cursor.close();
    782                 }
    783             }
    784 
    785             // Set Display name after sub id is set above so as to get valid simCarrierName
    786             int[] subIds = getSubId(slotId);
    787             if (subIds == null || subIds.length == 0) {
    788                 if (DBG) {
    789                     logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
    790                             + subIds);
    791                 }
    792                 return -1;
    793             }
    794             if (setDisplayName) {
    795                 String simCarrierName = mTelephonyManager.getSimOperatorNameForSubscription(subIds[0]);
    796                 String nameToSet;
    797 
    798                 if (!TextUtils.isEmpty(simCarrierName)) {
    799                     nameToSet = simCarrierName;
    800                 } else {
    801                     nameToSet = "CARD " + Integer.toString(slotId + 1);
    802                 }
    803 
    804                 ContentValues value = new ContentValues();
    805                 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
    806                 resolver.update(SubscriptionManager.CONTENT_URI, value,
    807                         SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
    808                                 "=" + Long.toString(subIds[0]), null);
    809 
    810                 if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
    811             }
    812 
    813             // Once the records are loaded, notify DcTracker
    814             updateAllDataConnectionTrackers();
    815 
    816             if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIdxToSubId.size());
    817 
    818         } finally {
    819             Binder.restoreCallingIdentity(identity);
    820         }
    821         return 0;
    822     }
    823 
    824     /**
    825      * Generate and set carrier text based on input parameters
    826      * @param showPlmn flag to indicate if plmn should be included in carrier text
    827      * @param plmn plmn to be included in carrier text
    828      * @param showSpn flag to indicate if spn should be included in carrier text
    829      * @param spn spn to be included in carrier text
    830      * @return true if carrier text is set, false otherwise
    831      */
    832     public boolean setPlmnSpn(int slotId, boolean showPlmn, String plmn, boolean showSpn,
    833                               String spn) {
    834         synchronized (mLock) {
    835             int[] subIds = getSubId(slotId);
    836             if (mContext.getPackageManager().resolveContentProvider(
    837                     SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||
    838                     subIds == null ||
    839                     !SubscriptionManager.isValidSubscriptionId(subIds[0])) {
    840                 // No place to store this info. Notify registrants of the change anyway as they
    841                 // might retrieve the SPN/PLMN text from the SST sticky broadcast.
    842                 // TODO: This can be removed once SubscriptionController is not running on devices
    843                 // that don't need it, such as TVs.
    844                 if (DBG) logd("[setPlmnSpn] No valid subscription to store info");
    845                 notifySubscriptionInfoChanged();
    846                 return false;
    847             }
    848             String carrierText = "";
    849             if (showPlmn) {
    850                 carrierText = plmn;
    851                 if (showSpn) {
    852                     // Need to show both plmn and spn if both are not same.
    853                     if(!Objects.equals(spn, plmn)) {
    854                         String separator = mContext.getString(
    855                                 com.android.internal.R.string.kg_text_message_separator).toString();
    856                         carrierText = new StringBuilder().append(carrierText).append(separator)
    857                                 .append(spn).toString();
    858                     }
    859                 }
    860             } else if (showSpn) {
    861                 carrierText = spn;
    862             }
    863             for (int i = 0; i < subIds.length; i++) {
    864                 setCarrierText(carrierText, subIds[i]);
    865             }
    866             return true;
    867         }
    868     }
    869 
    870     /**
    871      * Set carrier text by simInfo index
    872      * @param text new carrier text
    873      * @param subId the unique SubInfoRecord index in database
    874      * @return the number of records updated
    875      */
    876     private int setCarrierText(String text, int subId) {
    877         if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);
    878 
    879         enforceModifyPhoneState("setCarrierText");
    880 
    881         // Now that all security checks passes, perform the operation as ourselves.
    882         final long identity = Binder.clearCallingIdentity();
    883         try {
    884             ContentValues value = new ContentValues(1);
    885             value.put(SubscriptionManager.CARRIER_NAME, text);
    886 
    887             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
    888                     value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
    889                     Long.toString(subId), null);
    890             notifySubscriptionInfoChanged();
    891 
    892             return result;
    893         } finally {
    894             Binder.restoreCallingIdentity(identity);
    895         }
    896     }
    897 
    898     /**
    899      * Set SIM color tint by simInfo index
    900      * @param tint the tint color of the SIM
    901      * @param subId the unique SubInfoRecord index in database
    902      * @return the number of records updated
    903      */
    904     @Override
    905     public int setIconTint(int tint, int subId) {
    906         if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
    907 
    908         enforceModifyPhoneState("setIconTint");
    909 
    910         // Now that all security checks passes, perform the operation as ourselves.
    911         final long identity = Binder.clearCallingIdentity();
    912         try {
    913             validateSubId(subId);
    914             ContentValues value = new ContentValues(1);
    915             value.put(SubscriptionManager.COLOR, tint);
    916             if (DBG) logd("[setIconTint]- tint:" + tint + " set");
    917 
    918             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
    919                     value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
    920                     Long.toString(subId), null);
    921             notifySubscriptionInfoChanged();
    922 
    923             return result;
    924         } finally {
    925             Binder.restoreCallingIdentity(identity);
    926         }
    927     }
    928 
    929     /**
    930      * Set display name by simInfo index
    931      * @param displayName the display name of SIM card
    932      * @param subId the unique SubInfoRecord index in database
    933      * @return the number of records updated
    934      */
    935     @Override
    936     public int setDisplayName(String displayName, int subId) {
    937         return setDisplayNameUsingSrc(displayName, subId, -1);
    938     }
    939 
    940     /**
    941      * Set display name by simInfo index with name source
    942      * @param displayName the display name of SIM card
    943      * @param subId the unique SubInfoRecord index in database
    944      * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
    945      *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
    946      * @return the number of records updated
    947      */
    948     @Override
    949     public int setDisplayNameUsingSrc(String displayName, int subId, long nameSource) {
    950         if (DBG) {
    951             logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
    952                 + " nameSource:" + nameSource);
    953         }
    954 
    955         enforceModifyPhoneState("setDisplayNameUsingSrc");
    956 
    957         // Now that all security checks passes, perform the operation as ourselves.
    958         final long identity = Binder.clearCallingIdentity();
    959         try {
    960             validateSubId(subId);
    961             String nameToSet;
    962             if (displayName == null) {
    963                 nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
    964             } else {
    965                 nameToSet = displayName;
    966             }
    967             ContentValues value = new ContentValues(1);
    968             value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
    969             if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
    970                 if (DBG) logd("Set nameSource=" + nameSource);
    971                 value.put(SubscriptionManager.NAME_SOURCE, nameSource);
    972             }
    973             if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
    974 
    975             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
    976                     value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
    977                     Long.toString(subId), null);
    978             notifySubscriptionInfoChanged();
    979 
    980             return result;
    981         } finally {
    982             Binder.restoreCallingIdentity(identity);
    983         }
    984     }
    985 
    986     /**
    987      * Set phone number by subId
    988      * @param number the phone number of the SIM
    989      * @param subId the unique SubInfoRecord index in database
    990      * @return the number of records updated
    991      */
    992     @Override
    993     public int setDisplayNumber(String number, int subId) {
    994         if (DBG) logd("[setDisplayNumber]+ subId:" + subId);
    995 
    996         enforceModifyPhoneState("setDisplayNumber");
    997 
    998         // Now that all security checks passes, perform the operation as ourselves.
    999         final long identity = Binder.clearCallingIdentity();
   1000         try {
   1001             validateSubId(subId);
   1002             int result;
   1003             int phoneId = getPhoneId(subId);
   1004 
   1005             if (number == null || phoneId < 0 ||
   1006                     phoneId >= mTelephonyManager.getPhoneCount()) {
   1007                 if (DBG) logd("[setDispalyNumber]- fail");
   1008                 return -1;
   1009             }
   1010             ContentValues value = new ContentValues(1);
   1011             value.put(SubscriptionManager.NUMBER, number);
   1012 
   1013             // This function had a call to update number on the SIM (Phone.setLine1Number()) but
   1014             // that was removed as there doesn't seem to be a reason for that. If it is added
   1015             // back, watch out for deadlocks.
   1016 
   1017             result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
   1018                     SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
   1019                             + "=" + Long.toString(subId), null);
   1020             if (DBG) logd("[setDisplayNumber]- update result :" + result);
   1021             notifySubscriptionInfoChanged();
   1022 
   1023             return result;
   1024         } finally {
   1025             Binder.restoreCallingIdentity(identity);
   1026         }
   1027     }
   1028 
   1029     /**
   1030      * Set data roaming by simInfo index
   1031      * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
   1032      * @param subId the unique SubInfoRecord index in database
   1033      * @return the number of records updated
   1034      */
   1035     @Override
   1036     public int setDataRoaming(int roaming, int subId) {
   1037         if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
   1038 
   1039         enforceModifyPhoneState("setDataRoaming");
   1040 
   1041         // Now that all security checks passes, perform the operation as ourselves.
   1042         final long identity = Binder.clearCallingIdentity();
   1043         try {
   1044             validateSubId(subId);
   1045             if (roaming < 0) {
   1046                 if (DBG) logd("[setDataRoaming]- fail");
   1047                 return -1;
   1048             }
   1049             ContentValues value = new ContentValues(1);
   1050             value.put(SubscriptionManager.DATA_ROAMING, roaming);
   1051             if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
   1052 
   1053             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
   1054                     value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
   1055                     Long.toString(subId), null);
   1056             notifySubscriptionInfoChanged();
   1057 
   1058             return result;
   1059         } finally {
   1060             Binder.restoreCallingIdentity(identity);
   1061         }
   1062     }
   1063 
   1064     /**
   1065      * Set MCC/MNC by subscription ID
   1066      * @param mccMnc MCC/MNC associated with the subscription
   1067      * @param subId the unique SubInfoRecord index in database
   1068      * @return the number of records updated
   1069      */
   1070     public int setMccMnc(String mccMnc, int subId) {
   1071         int mcc = 0;
   1072         int mnc = 0;
   1073         try {
   1074             mcc = Integer.parseInt(mccMnc.substring(0,3));
   1075             mnc = Integer.parseInt(mccMnc.substring(3));
   1076         } catch (NumberFormatException e) {
   1077             loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
   1078         }
   1079         if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
   1080         ContentValues value = new ContentValues(2);
   1081         value.put(SubscriptionManager.MCC, mcc);
   1082         value.put(SubscriptionManager.MNC, mnc);
   1083 
   1084         int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
   1085                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
   1086         notifySubscriptionInfoChanged();
   1087 
   1088         return result;
   1089     }
   1090 
   1091 
   1092     @Override
   1093     public int getSlotId(int subId) {
   1094         if (VDBG) printStackTrace("[getSlotId] subId=" + subId);
   1095 
   1096         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1097             subId = getDefaultSubId();
   1098         }
   1099         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
   1100             if (DBG) logd("[getSlotId]- subId invalid");
   1101             return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
   1102         }
   1103 
   1104         int size = sSlotIdxToSubId.size();
   1105 
   1106         if (size == 0)
   1107         {
   1108             if (DBG) logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead");
   1109             return SubscriptionManager.SIM_NOT_INSERTED;
   1110         }
   1111 
   1112         for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
   1113             int sim = entry.getKey();
   1114             int sub = entry.getValue();
   1115 
   1116             if (subId == sub)
   1117             {
   1118                 if (VDBG) logv("[getSlotId]- return = " + sim);
   1119                 return sim;
   1120             }
   1121         }
   1122 
   1123         if (DBG) logd("[getSlotId]- return fail");
   1124         return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
   1125     }
   1126 
   1127     /**
   1128      * Return the subId for specified slot Id.
   1129      * @deprecated
   1130      */
   1131     @Override
   1132     @Deprecated
   1133     public int[] getSubId(int slotIdx) {
   1134         if (VDBG) printStackTrace("[getSubId]+ slotIdx=" + slotIdx);
   1135 
   1136         // Map default slotIdx to the current default subId.
   1137         // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous
   1138         // as a slot maybe used for multiple different type of "connections"
   1139         // such as: voice, data and sms. But we're doing the best we can and using
   1140         // getDefaultSubId which makes a best guess.
   1141         if (slotIdx == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
   1142             slotIdx = getSlotId(getDefaultSubId());
   1143             if (DBG) logd("[getSubId] map default slotIdx=" + slotIdx);
   1144         }
   1145 
   1146         // Check that we have a valid SlotIdx
   1147         if (!SubscriptionManager.isValidSlotId(slotIdx)) {
   1148             if (DBG) logd("[getSubId]- invalid slotIdx=" + slotIdx);
   1149             return null;
   1150         }
   1151 
   1152         // Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate.
   1153         int size = sSlotIdxToSubId.size();
   1154         if (size == 0) {
   1155             if (DBG) {
   1156                 logd("[getSubId]- sSlotIdxToSubId.size == 0, return DummySubIds slotIdx="
   1157                         + slotIdx);
   1158             }
   1159             return getDummySubIds(slotIdx);
   1160         }
   1161 
   1162         // Create an array of subIds that are in this slot?
   1163         ArrayList<Integer> subIds = new ArrayList<Integer>();
   1164         for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
   1165             int slot = entry.getKey();
   1166             int sub = entry.getValue();
   1167             if (slotIdx == slot) {
   1168                 subIds.add(sub);
   1169             }
   1170         }
   1171 
   1172         // Convert ArrayList to array
   1173         int numSubIds = subIds.size();
   1174         if (numSubIds > 0) {
   1175             int[] subIdArr = new int[numSubIds];
   1176             for (int i = 0; i < numSubIds; i++) {
   1177                 subIdArr[i] = subIds.get(i);
   1178             }
   1179             if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr);
   1180             return subIdArr;
   1181         } else {
   1182             if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIdx=" + slotIdx);
   1183             return getDummySubIds(slotIdx);
   1184         }
   1185     }
   1186 
   1187     @Override
   1188     public int getPhoneId(int subId) {
   1189         if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
   1190         int phoneId;
   1191 
   1192         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1193             subId = getDefaultSubId();
   1194             if (DBG) logdl("[getPhoneId] asked for default subId=" + subId);
   1195         }
   1196 
   1197         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
   1198             if (DBG) {
   1199                 logdl("[getPhoneId]- invalid subId return="
   1200                         + SubscriptionManager.INVALID_PHONE_INDEX);
   1201             }
   1202             return SubscriptionManager.INVALID_PHONE_INDEX;
   1203         }
   1204 
   1205         int size = sSlotIdxToSubId.size();
   1206         if (size == 0) {
   1207             phoneId = mDefaultPhoneId;
   1208             if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
   1209             return phoneId;
   1210         }
   1211 
   1212         // FIXME: Assumes phoneId == slotId
   1213         for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
   1214             int sim = entry.getKey();
   1215             int sub = entry.getValue();
   1216 
   1217             if (subId == sub) {
   1218                 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
   1219                 return sim;
   1220             }
   1221         }
   1222 
   1223         phoneId = mDefaultPhoneId;
   1224         if (DBG) {
   1225             logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId);
   1226         }
   1227         return phoneId;
   1228 
   1229     }
   1230 
   1231     private int[] getDummySubIds(int slotIdx) {
   1232         // FIXME: Remove notion of Dummy SUBSCRIPTION_ID.
   1233         // I tested this returning null as no one appears to care,
   1234         // but no connection came up on sprout with two sims.
   1235         // We need to figure out why and hopefully remove DummySubsIds!!!
   1236         int numSubs = getActiveSubInfoCountMax();
   1237         if (numSubs > 0) {
   1238             int[] dummyValues = new int[numSubs];
   1239             for (int i = 0; i < numSubs; i++) {
   1240                 dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx;
   1241             }
   1242             if (VDBG) {
   1243                 logd("getDummySubIds: slotIdx=" + slotIdx
   1244                     + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]);
   1245             }
   1246             return dummyValues;
   1247         } else {
   1248             return null;
   1249         }
   1250     }
   1251 
   1252     /**
   1253      * @return the number of records cleared
   1254      */
   1255     @Override
   1256     public int clearSubInfo() {
   1257         enforceModifyPhoneState("clearSubInfo");
   1258 
   1259         // Now that all security checks passes, perform the operation as ourselves.
   1260         final long identity = Binder.clearCallingIdentity();
   1261         try {
   1262             if (DBG) logd("[clearSubInfo]+");
   1263 
   1264             int size = sSlotIdxToSubId.size();
   1265 
   1266             if (size == 0) {
   1267                 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
   1268                 return 0;
   1269             }
   1270 
   1271             sSlotIdxToSubId.clear();
   1272             if (DBG) logdl("[clearSubInfo]- clear size=" + size);
   1273             return size;
   1274         } finally {
   1275             Binder.restoreCallingIdentity(identity);
   1276         }
   1277     }
   1278 
   1279     private void logvl(String msg) {
   1280         logv(msg);
   1281         mLocalLog.log(msg);
   1282     }
   1283 
   1284     private void logv(String msg) {
   1285         Rlog.v(LOG_TAG, msg);
   1286     }
   1287 
   1288     private void logdl(String msg) {
   1289         logd(msg);
   1290         mLocalLog.log(msg);
   1291     }
   1292 
   1293     private static void slogd(String msg) {
   1294         Rlog.d(LOG_TAG, msg);
   1295     }
   1296 
   1297     private void logd(String msg) {
   1298         Rlog.d(LOG_TAG, msg);
   1299     }
   1300 
   1301     private void logel(String msg) {
   1302         loge(msg);
   1303         mLocalLog.log(msg);
   1304     }
   1305 
   1306     private void loge(String msg) {
   1307         Rlog.e(LOG_TAG, msg);
   1308     }
   1309 
   1310     @Override
   1311     public int getDefaultSubId() {
   1312         int subId;
   1313         boolean isVoiceCapable = mContext.getResources().getBoolean(
   1314                 com.android.internal.R.bool.config_voice_capable);
   1315         if (isVoiceCapable) {
   1316             subId = getDefaultVoiceSubId();
   1317             if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId);
   1318         } else {
   1319             subId = getDefaultDataSubId();
   1320             if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId);
   1321         }
   1322         if (!isActiveSubId(subId)) {
   1323             subId = mDefaultFallbackSubId;
   1324             if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId);
   1325         }
   1326         if (VDBG) logv("[getDefaultSubId]- value = " + subId);
   1327         return subId;
   1328     }
   1329 
   1330     @Override
   1331     public void setDefaultSmsSubId(int subId) {
   1332         enforceModifyPhoneState("setDefaultSmsSubId");
   1333 
   1334         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1335             throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
   1336         }
   1337         if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId);
   1338         Settings.Global.putInt(mContext.getContentResolver(),
   1339                 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
   1340         broadcastDefaultSmsSubIdChanged(subId);
   1341     }
   1342 
   1343     private void broadcastDefaultSmsSubIdChanged(int subId) {
   1344         // Broadcast an Intent for default sms sub change
   1345         if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
   1346         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
   1347         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1348         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
   1349         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1350     }
   1351 
   1352     @Override
   1353     public int getDefaultSmsSubId() {
   1354         int subId = Settings.Global.getInt(mContext.getContentResolver(),
   1355                 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
   1356                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
   1357         if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId);
   1358         return subId;
   1359     }
   1360 
   1361     @Override
   1362     public void setDefaultVoiceSubId(int subId) {
   1363         enforceModifyPhoneState("setDefaultVoiceSubId");
   1364 
   1365         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1366             throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
   1367         }
   1368         if (DBG) logdl("[setDefaultVoiceSubId] subId=" + subId);
   1369         Settings.Global.putInt(mContext.getContentResolver(),
   1370                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
   1371         broadcastDefaultVoiceSubIdChanged(subId);
   1372     }
   1373 
   1374     private void broadcastDefaultVoiceSubIdChanged(int subId) {
   1375         // Broadcast an Intent for default voice sub change
   1376         if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
   1377         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
   1378         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1379         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
   1380         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1381     }
   1382 
   1383     @Override
   1384     public int getDefaultVoiceSubId() {
   1385         int subId = Settings.Global.getInt(mContext.getContentResolver(),
   1386                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
   1387                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
   1388         if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
   1389         return subId;
   1390     }
   1391 
   1392     @Override
   1393     public int getDefaultDataSubId() {
   1394         int subId = Settings.Global.getInt(mContext.getContentResolver(),
   1395                 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
   1396                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
   1397         if (VDBG) logd("[getDefaultDataSubId] subId= " + subId);
   1398         return subId;
   1399     }
   1400 
   1401     @Override
   1402     public void setDefaultDataSubId(int subId) {
   1403         enforceModifyPhoneState("setDefaultDataSubId");
   1404 
   1405         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1406             throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
   1407         }
   1408         if (DBG) logdl("[setDefaultDataSubId] subId=" + subId);
   1409 
   1410         ProxyController proxyController = ProxyController.getInstance();
   1411         int len = sProxyPhones.length;
   1412         logdl("[setDefaultDataSubId] num phones=" + len);
   1413 
   1414         if (SubscriptionManager.isValidSubscriptionId(subId)) {
   1415             // Only re-map modems if the new default data sub is valid
   1416             RadioAccessFamily[] rafs = new RadioAccessFamily[len];
   1417             boolean atLeastOneMatch = false;
   1418             for (int phoneId = 0; phoneId < len; phoneId++) {
   1419                 PhoneProxy phone = sProxyPhones[phoneId];
   1420                 int raf;
   1421                 int id = phone.getSubId();
   1422                 if (id == subId) {
   1423                     // TODO Handle the general case of N modems and M subscriptions.
   1424                     raf = proxyController.getMaxRafSupported();
   1425                     atLeastOneMatch = true;
   1426                 } else {
   1427                     // TODO Handle the general case of N modems and M subscriptions.
   1428                     raf = proxyController.getMinRafSupported();
   1429                 }
   1430                 logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf);
   1431                 rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
   1432             }
   1433             if (atLeastOneMatch) {
   1434                 proxyController.setRadioCapability(rafs);
   1435             } else {
   1436                 if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating.");
   1437             }
   1438         }
   1439 
   1440         // FIXME is this still needed?
   1441         updateAllDataConnectionTrackers();
   1442 
   1443         Settings.Global.putInt(mContext.getContentResolver(),
   1444                 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
   1445         broadcastDefaultDataSubIdChanged(subId);
   1446     }
   1447 
   1448     private void updateAllDataConnectionTrackers() {
   1449         // Tell Phone Proxies to update data connection tracker
   1450         int len = sProxyPhones.length;
   1451         if (DBG) logdl("[updateAllDataConnectionTrackers] sProxyPhones.length=" + len);
   1452         for (int phoneId = 0; phoneId < len; phoneId++) {
   1453             if (DBG) logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId);
   1454             sProxyPhones[phoneId].updateDataConnectionTracker();
   1455         }
   1456     }
   1457 
   1458     private void broadcastDefaultDataSubIdChanged(int subId) {
   1459         // Broadcast an Intent for default data sub change
   1460         if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
   1461         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
   1462         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1463         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
   1464         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1465     }
   1466 
   1467     /* Sets the default subscription. If only one sub is active that
   1468      * sub is set as default subId. If two or more  sub's are active
   1469      * the first sub is set as default subscription
   1470      */
   1471     private void setDefaultFallbackSubId(int subId) {
   1472         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1473             throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
   1474         }
   1475         if (DBG) logdl("[setDefaultFallbackSubId] subId=" + subId);
   1476         if (SubscriptionManager.isValidSubscriptionId(subId)) {
   1477             int phoneId = getPhoneId(subId);
   1478             if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount()
   1479                     || mTelephonyManager.getSimCount() == 1)) {
   1480                 if (DBG) logdl("[setDefaultFallbackSubId] set mDefaultFallbackSubId=" + subId);
   1481                 mDefaultFallbackSubId = subId;
   1482                 // Update MCC MNC device configuration information
   1483                 String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId);
   1484                 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);
   1485 
   1486                 // Broadcast an Intent for default sub change
   1487                 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
   1488                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1489                 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
   1490                 if (DBG) {
   1491                     logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" +
   1492                             phoneId + " subId=" + subId);
   1493                 }
   1494                 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1495             } else {
   1496                 if (DBG) {
   1497                     logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId
   1498                             + " subId=" + subId);
   1499                 }
   1500             }
   1501         }
   1502     }
   1503 
   1504     @Override
   1505     public void clearDefaultsForInactiveSubIds() {
   1506         enforceModifyPhoneState("clearDefaultsForInactiveSubIds");
   1507 
   1508         // Now that all security checks passes, perform the operation as ourselves.
   1509         final long identity = Binder.clearCallingIdentity();
   1510         try {
   1511             final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
   1512                     mContext.getOpPackageName());
   1513             if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records);
   1514             if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
   1515                 if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
   1516                 setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
   1517             }
   1518             if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
   1519                 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
   1520                 setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
   1521             }
   1522             if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
   1523                 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
   1524                 setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
   1525             }
   1526         } finally {
   1527             Binder.restoreCallingIdentity(identity);
   1528         }
   1529     }
   1530 
   1531     private boolean shouldDefaultBeCleared(List<SubscriptionInfo> records, int subId) {
   1532         if (DBG) logdl("[shouldDefaultBeCleared: subId] " + subId);
   1533         if (records == null) {
   1534             if (DBG) logdl("[shouldDefaultBeCleared] return true no records subId=" + subId);
   1535             return true;
   1536         }
   1537         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
   1538             // If the subId parameter is not valid its already cleared so return false.
   1539             if (DBG) logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId);
   1540             return false;
   1541         }
   1542         for (SubscriptionInfo record : records) {
   1543             int id = record.getSubscriptionId();
   1544             if (DBG) logdl("[shouldDefaultBeCleared] Record.id: " + id);
   1545             if (id == subId) {
   1546                 logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId);
   1547                 return false;
   1548             }
   1549         }
   1550         if (DBG) logdl("[shouldDefaultBeCleared] return true not active subId=" + subId);
   1551         return true;
   1552     }
   1553 
   1554     // FIXME: We need we should not be assuming phoneId == slotId as it will not be true
   1555     // when there are multiple subscriptions per sim and probably for other reasons.
   1556     public int getSubIdUsingPhoneId(int phoneId) {
   1557         int[] subIds = getSubId(phoneId);
   1558         if (subIds == null || subIds.length == 0) {
   1559             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
   1560         }
   1561         return subIds[0];
   1562     }
   1563 
   1564     public int[] getSubIdUsingSlotId(int slotId) {
   1565         return getSubId(slotId);
   1566     }
   1567 
   1568     public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck,
   1569             String callingPackage) {
   1570         if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
   1571 
   1572         if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
   1573             return null;
   1574         }
   1575 
   1576         // Now that all security checks passes, perform the operation as ourselves.
   1577         final long identity = Binder.clearCallingIdentity();
   1578         try {
   1579             if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
   1580                 slotId = getSlotId(getDefaultSubId());
   1581             }
   1582             if (!SubscriptionManager.isValidSlotId(slotId)) {
   1583                 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
   1584                 return null;
   1585             }
   1586 
   1587             if (needCheck && !isSubInfoReady()) {
   1588                 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
   1589                 return null;
   1590             }
   1591 
   1592             Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
   1593                     null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
   1594                     new String[]{String.valueOf(slotId)}, null);
   1595             ArrayList<SubscriptionInfo> subList = null;
   1596             try {
   1597                 if (cursor != null) {
   1598                     while (cursor.moveToNext()) {
   1599                         SubscriptionInfo subInfo = getSubInfoRecord(cursor);
   1600                         if (subInfo != null) {
   1601                             if (subList == null) {
   1602                                 subList = new ArrayList<SubscriptionInfo>();
   1603                             }
   1604                             subList.add(subInfo);
   1605                         }
   1606                     }
   1607                 }
   1608             } finally {
   1609                 if (cursor != null) {
   1610                     cursor.close();
   1611                 }
   1612             }
   1613             if (DBG) logd("[getSubInfoUsingSlotId]- null info return");
   1614 
   1615             return subList;
   1616         } finally {
   1617             Binder.restoreCallingIdentity(identity);
   1618         }
   1619     }
   1620 
   1621     private void validateSubId(int subId) {
   1622         if (DBG) logd("validateSubId subId: " + subId);
   1623         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
   1624             throw new RuntimeException("Invalid sub id passed as parameter");
   1625         } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
   1626             throw new RuntimeException("Default sub id passed as parameter");
   1627         }
   1628     }
   1629 
   1630     public void updatePhonesAvailability(PhoneProxy[] phones) {
   1631         sProxyPhones = phones;
   1632     }
   1633 
   1634     /**
   1635      * @return the list of subId's that are active, is never null but the length maybe 0.
   1636      */
   1637     @Override
   1638     public int[] getActiveSubIdList() {
   1639         Set<Entry<Integer, Integer>> simInfoSet = sSlotIdxToSubId.entrySet();
   1640         if (DBG) logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet);
   1641 
   1642         int[] subIdArr = new int[simInfoSet.size()];
   1643         int i = 0;
   1644         for (Entry<Integer, Integer> entry: simInfoSet) {
   1645             int sub = entry.getValue();
   1646             subIdArr[i] = sub;
   1647             i++;
   1648         }
   1649 
   1650         if (DBG) logdl("[getActiveSubIdList] X subIdArr.length=" + subIdArr.length);
   1651         return subIdArr;
   1652     }
   1653 
   1654     @Override
   1655     public boolean isActiveSubId(int subId) {
   1656         boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
   1657                 && sSlotIdxToSubId.containsValue(subId);
   1658 
   1659         if (VDBG) logdl("[isActiveSubId]- " + retVal);
   1660         return retVal;
   1661     }
   1662 
   1663     /**
   1664      * Get the SIM state for the slot idx
   1665      * @return SIM state as the ordinal of {@See IccCardConstants.State}
   1666      */
   1667     @Override
   1668     public int getSimStateForSlotIdx(int slotIdx) {
   1669         State simState;
   1670         String err;
   1671         if (slotIdx < 0) {
   1672             simState = IccCardConstants.State.UNKNOWN;
   1673             err = "invalid slotIdx";
   1674         } else {
   1675             Phone phone = PhoneFactory.getPhone(slotIdx);
   1676             if (phone == null) {
   1677                 simState = IccCardConstants.State.UNKNOWN;
   1678                 err = "phone == null";
   1679             } else {
   1680                 IccCard icc = phone.getIccCard();
   1681                 if (icc == null) {
   1682                     simState = IccCardConstants.State.UNKNOWN;
   1683                     err = "icc == null";
   1684                 } else {
   1685                     simState = icc.getState();
   1686                     err = "";
   1687                 }
   1688             }
   1689         }
   1690         if (DBG) logd("getSimStateForSlotIdx: " + err + " simState=" + simState
   1691                 + " ordinal=" + simState.ordinal());
   1692         return simState.ordinal();
   1693     }
   1694 
   1695     /**
   1696      * Store properties associated with SubscriptionInfo in database
   1697      * @param subId Subscription Id of Subscription
   1698      * @param propKey Column name in database associated with SubscriptionInfo
   1699      * @param propValue Value to store in DB for particular subId & column name
   1700      * @hide
   1701      */
   1702     @Override
   1703     public void setSubscriptionProperty(int subId, String propKey, String propValue) {
   1704         enforceModifyPhoneState("setSubscriptionProperty");
   1705         final long token = Binder.clearCallingIdentity();
   1706         ContentResolver resolver = mContext.getContentResolver();
   1707         ContentValues value = new ContentValues();
   1708         switch (propKey) {
   1709             case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
   1710             case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
   1711             case SubscriptionManager.CB_AMBER_ALERT:
   1712             case SubscriptionManager.CB_EMERGENCY_ALERT:
   1713             case SubscriptionManager.CB_ALERT_SOUND_DURATION:
   1714             case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
   1715             case SubscriptionManager.CB_ALERT_VIBRATE:
   1716             case SubscriptionManager.CB_ALERT_SPEECH:
   1717             case SubscriptionManager.CB_ETWS_TEST_ALERT:
   1718             case SubscriptionManager.CB_CHANNEL_50_ALERT:
   1719             case SubscriptionManager.CB_CMAS_TEST_ALERT:
   1720             case SubscriptionManager.CB_OPT_OUT_DIALOG:
   1721                 value.put(propKey, Integer.parseInt(propValue));
   1722                 break;
   1723             default:
   1724                 if(DBG) logd("Invalid column name");
   1725                 break;
   1726         }
   1727 
   1728         resolver.update(SubscriptionManager.CONTENT_URI, value,
   1729                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
   1730                         "=" + Integer.toString(subId), null);
   1731         Binder.restoreCallingIdentity(token);
   1732     }
   1733 
   1734     /**
   1735      * Store properties associated with SubscriptionInfo in database
   1736      * @param subId Subscription Id of Subscription
   1737      * @param propKey Column name in SubscriptionInfo database
   1738      * @return Value associated with subId and propKey column in database
   1739      * @hide
   1740      */
   1741     @Override
   1742     public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
   1743         if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
   1744             return null;
   1745         }
   1746         String resultValue = null;
   1747         ContentResolver resolver = mContext.getContentResolver();
   1748         Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
   1749                 new String[]{propKey},
   1750                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
   1751                 new String[]{subId + ""}, null);
   1752 
   1753         try {
   1754             if (cursor != null) {
   1755                 if (cursor.moveToFirst()) {
   1756                     switch (propKey) {
   1757                         case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
   1758                         case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
   1759                         case SubscriptionManager.CB_AMBER_ALERT:
   1760                         case SubscriptionManager.CB_EMERGENCY_ALERT:
   1761                         case SubscriptionManager.CB_ALERT_SOUND_DURATION:
   1762                         case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
   1763                         case SubscriptionManager.CB_ALERT_VIBRATE:
   1764                         case SubscriptionManager.CB_ALERT_SPEECH:
   1765                         case SubscriptionManager.CB_ETWS_TEST_ALERT:
   1766                         case SubscriptionManager.CB_CHANNEL_50_ALERT:
   1767                         case SubscriptionManager.CB_CMAS_TEST_ALERT:
   1768                         case SubscriptionManager.CB_OPT_OUT_DIALOG:
   1769                             resultValue = cursor.getInt(0) + "";
   1770                             break;
   1771                         default:
   1772                             if(DBG) logd("Invalid column name");
   1773                             break;
   1774                     }
   1775                 } else {
   1776                     if(DBG) logd("Valid row not present in db");
   1777                 }
   1778             } else {
   1779                 if(DBG) logd("Query failed");
   1780             }
   1781         } finally {
   1782             if (cursor != null) {
   1783                 cursor.close();
   1784             }
   1785         }
   1786         if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
   1787         return resultValue;
   1788     }
   1789 
   1790     private static void printStackTrace(String msg) {
   1791         RuntimeException re = new RuntimeException();
   1792         slogd("StackTrace - " + msg);
   1793         StackTraceElement[] st = re.getStackTrace();
   1794         boolean first = true;
   1795         for (StackTraceElement ste : st) {
   1796             if (first) {
   1797                 first = false;
   1798             } else {
   1799                 slogd(ste.toString());
   1800             }
   1801         }
   1802     }
   1803 
   1804     @Override
   1805     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1806         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
   1807                 "Requires DUMP");
   1808         final long token = Binder.clearCallingIdentity();
   1809         try {
   1810             pw.println("SubscriptionController:");
   1811             pw.println(" defaultSubId=" + getDefaultSubId());
   1812             pw.println(" defaultDataSubId=" + getDefaultDataSubId());
   1813             pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
   1814             pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());
   1815 
   1816             pw.println(" defaultDataPhoneId=" + SubscriptionManager
   1817                     .from(mContext).getDefaultDataPhoneId());
   1818             pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
   1819             pw.println(" defaultSmsPhoneId=" + SubscriptionManager
   1820                     .from(mContext).getDefaultSmsPhoneId());
   1821             pw.flush();
   1822 
   1823             for (Entry<Integer, Integer> entry : sSlotIdxToSubId.entrySet()) {
   1824                 pw.println(" sSlotIdxToSubId[" + entry.getKey() + "]: subId=" + entry.getValue());
   1825             }
   1826             pw.flush();
   1827             pw.println("++++++++++++++++++++++++++++++++");
   1828 
   1829             List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(
   1830                     mContext.getOpPackageName());
   1831             if (sirl != null) {
   1832                 pw.println(" ActiveSubInfoList:");
   1833                 for (SubscriptionInfo entry : sirl) {
   1834                     pw.println("  " + entry.toString());
   1835                 }
   1836             } else {
   1837                 pw.println(" ActiveSubInfoList: is null");
   1838             }
   1839             pw.flush();
   1840             pw.println("++++++++++++++++++++++++++++++++");
   1841 
   1842             sirl = getAllSubInfoList(mContext.getOpPackageName());
   1843             if (sirl != null) {
   1844                 pw.println(" AllSubInfoList:");
   1845                 for (SubscriptionInfo entry : sirl) {
   1846                     pw.println("  " + entry.toString());
   1847                 }
   1848             } else {
   1849                 pw.println(" AllSubInfoList: is null");
   1850             }
   1851             pw.flush();
   1852             pw.println("++++++++++++++++++++++++++++++++");
   1853 
   1854             mLocalLog.dump(fd, pw, args);
   1855             pw.flush();
   1856             pw.println("++++++++++++++++++++++++++++++++");
   1857             pw.flush();
   1858         } finally {
   1859             Binder.restoreCallingIdentity(token);
   1860         }
   1861     }
   1862 }
   1863