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