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