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