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