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