SubscriptionController.java revision 5f208fd8c2c2ef5036dabffafc72b73b9eb3fc16
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.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.ServiceManager;
24import android.os.UserHandle;
25import android.telephony.Rlog;
26import android.util.Log;
27import android.net.Uri;
28import android.database.Cursor;
29import android.content.Intent;
30import android.provider.BaseColumns;
31import android.provider.Settings;
32import android.content.ContentResolver;
33import android.content.ContentValues;
34
35import com.android.internal.telephony.ISub;
36import com.android.internal.telephony.uicc.SpnOverride;
37
38import android.telephony.SubscriptionManager;
39import android.telephony.SubInfoRecord;
40import android.telephony.TelephonyManager;
41import android.text.format.Time;
42
43import java.io.FileDescriptor;
44import java.io.PrintWriter;
45import java.util.ArrayList;
46import java.util.Iterator;
47import java.util.LinkedList;
48import java.util.List;
49import java.util.HashMap;
50import java.util.Map.Entry;
51import java.util.Set;
52
53/**
54 * SubscriptionController to provide an inter-process communication to
55 * access Sms in Icc.
56 *
57 * Any setters which take subId, slotId or phoneId as a parameter will throw an exception if the
58 * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID.
59 *
60 * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling
61 * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()).
62 *
63 * Finally, any getters which perform the mapping between subscriptions, slots and phones will
64 * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters
65 * will fail and return the appropriate error value. Ie calling getSlotId(INVALID_SUB_ID) will
66 * return INVALID_SLOT_ID and calling getSubInfoForSubscriber(INVALID_SUB_ID) will return null.
67 *
68 */
69public class SubscriptionController extends ISub.Stub {
70    static final String LOG_TAG = "SubController";
71    static final boolean DBG = true;
72    static final boolean VDBG = false;
73    static final int MAX_LOCAL_LOG_LINES = 500; // TODO: Reduce to 100 when 17678050 is fixed
74    private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);
75
76    /**
77     * Copied from android.util.LocalLog with flush() adding flush and line number
78     * TODO: Update LocalLog
79     */
80    static class ScLocalLog {
81
82        private LinkedList<String> mLog;
83        private int mMaxLines;
84        private Time mNow;
85
86        public ScLocalLog(int maxLines) {
87            mLog = new LinkedList<String>();
88            mMaxLines = maxLines;
89            mNow = new Time();
90        }
91
92        public synchronized void log(String msg) {
93            if (mMaxLines > 0) {
94                int pid = android.os.Process.myPid();
95                int tid = android.os.Process.myTid();
96                mNow.setToNow();
97                mLog.add(mNow.format("%m-%d %H:%M:%S") + " pid=" + pid + " tid=" + tid + " " + msg);
98                while (mLog.size() > mMaxLines) mLog.remove();
99            }
100        }
101
102        public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
103            final int LOOPS_PER_FLUSH = 10; // Flush every N loops.
104            Iterator<String> itr = mLog.listIterator(0);
105            int i = 0;
106            while (itr.hasNext()) {
107                pw.println(Integer.toString(i++) + ": " + itr.next());
108                // Flush periodically so we don't drop lines
109                if ((i % LOOPS_PER_FLUSH) == 0) pw.flush();
110            }
111        }
112    }
113
114    protected final Object mLock = new Object();
115    protected boolean mSuccess;
116
117    /** The singleton instance. */
118    private static SubscriptionController sInstance = null;
119    protected static PhoneProxy[] sProxyPhones;
120    protected Context mContext;
121    protected CallManager mCM;
122
123    private static final int RES_TYPE_BACKGROUND_DARK = 0;
124    private static final int RES_TYPE_BACKGROUND_LIGHT = 1;
125
126    private static final int[] sSimBackgroundDarkRes = setSimResource(RES_TYPE_BACKGROUND_DARK);
127    private static final int[] sSimBackgroundLightRes = setSimResource(RES_TYPE_BACKGROUND_LIGHT);
128
129    //FIXME this does not allow for multiple subs in a slot
130    private static HashMap<Integer, Integer> mSimInfo = new HashMap<Integer, Integer>();
131    private static int mDefaultVoiceSubId = SubscriptionManager.INVALID_SUB_ID;
132    private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_ID;
133
134    private static final int EVENT_WRITE_MSISDN_DONE = 1;
135
136    protected Handler mHandler = new Handler() {
137        @Override
138        public void handleMessage(Message msg) {
139            AsyncResult ar;
140
141            switch (msg.what) {
142                case EVENT_WRITE_MSISDN_DONE:
143                    ar = (AsyncResult) msg.obj;
144                    synchronized (mLock) {
145                        mSuccess = (ar.exception == null);
146                        logd("EVENT_WRITE_MSISDN_DONE, mSuccess = "+mSuccess);
147                        mLock.notifyAll();
148                    }
149                    break;
150            }
151        }
152    };
153
154
155    public static SubscriptionController init(Phone phone) {
156        synchronized (SubscriptionController.class) {
157            if (sInstance == null) {
158                sInstance = new SubscriptionController(phone);
159            } else {
160                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
161            }
162            return sInstance;
163        }
164    }
165
166    public static SubscriptionController init(Context c, CommandsInterface[] ci) {
167        synchronized (SubscriptionController.class) {
168            if (sInstance == null) {
169                sInstance = new SubscriptionController(c);
170            } else {
171                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
172            }
173            return sInstance;
174        }
175    }
176
177    public static SubscriptionController getInstance() {
178        if (sInstance == null)
179        {
180           Log.wtf(LOG_TAG, "getInstance null");
181        }
182
183        return sInstance;
184    }
185
186    private SubscriptionController(Context c) {
187        mContext = c;
188        mCM = CallManager.getInstance();
189
190        if(ServiceManager.getService("isub") == null) {
191                ServiceManager.addService("isub", this);
192        }
193
194        logdl("[SubscriptionController] init by Context");
195    }
196
197    private boolean isSubInfoReady() {
198        return mSimInfo.size() > 0;
199    }
200
201    private SubscriptionController(Phone phone) {
202        mContext = phone.getContext();
203        mCM = CallManager.getInstance();
204
205        if(ServiceManager.getService("isub") == null) {
206                ServiceManager.addService("isub", this);
207        }
208
209        logdl("[SubscriptionController] init by Phone");
210    }
211
212    /**
213     * Make sure the caller has the READ_PHONE_STATE permission.
214     *
215     * @throws SecurityException if the caller does not have the required permission
216     */
217    private void enforceSubscriptionPermission() {
218        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
219                "Requires READ_PHONE_STATE");
220    }
221
222    /**
223     * Broadcast when subinfo settings has chanded
224     * @SubId The unique SubInfoRecord index in database
225     * @param columnName The column that is updated
226     * @param intContent The updated integer value
227     * @param stringContent The updated string value
228     */
229     private void broadcastSimInfoContentChanged(int subId,
230            String columnName, int intContent, String stringContent) {
231
232        Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
233        intent.putExtra(BaseColumns._ID, subId);
234        intent.putExtra(TelephonyIntents.EXTRA_COLUMN_NAME, columnName);
235        intent.putExtra(TelephonyIntents.EXTRA_INT_CONTENT, intContent);
236        intent.putExtra(TelephonyIntents.EXTRA_STRING_CONTENT, stringContent);
237        if (intContent != SubscriptionManager.DEFAULT_INT_VALUE) {
238            logd("[broadcastSimInfoContentChanged] subId" + subId
239                    + " changed, " + columnName + " -> " +  intContent);
240        } else {
241            logd("[broadcastSimInfoContentChanged] subId" + subId
242                    + " changed, " + columnName + " -> " +  stringContent);
243        }
244        mContext.sendBroadcast(intent);
245    }
246
247
248    /**
249     * New SubInfoRecord instance and fill in detail info
250     * @param cursor
251     * @return the query result of desired SubInfoRecord
252     */
253    private SubInfoRecord getSubInfoRecord(Cursor cursor) {
254            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BaseColumns._ID));
255            String iccId = cursor.getString(cursor.getColumnIndexOrThrow(
256                    SubscriptionManager.ICC_ID));
257            int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
258                    SubscriptionManager.SIM_ID));
259            String displayName = cursor.getString(cursor.getColumnIndexOrThrow(
260                    SubscriptionManager.DISPLAY_NAME));
261            int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
262                    SubscriptionManager.NAME_SOURCE));
263            int color = cursor.getInt(cursor.getColumnIndexOrThrow(
264                    SubscriptionManager.COLOR));
265            String number = cursor.getString(cursor.getColumnIndexOrThrow(
266                    SubscriptionManager.NUMBER));
267            int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
268                    SubscriptionManager.DATA_ROAMING));
269
270            int[] simIconRes = new int[2];
271            int size = sSimBackgroundDarkRes.length;
272            if (color >= 0 && color < size) {
273                simIconRes[RES_TYPE_BACKGROUND_DARK] = sSimBackgroundDarkRes[color];
274                simIconRes[RES_TYPE_BACKGROUND_LIGHT] = sSimBackgroundLightRes[color];
275            }
276            int mcc = cursor.getInt(cursor.getColumnIndexOrThrow(
277                    SubscriptionManager.MCC));
278            int mnc = cursor.getInt(cursor.getColumnIndexOrThrow(
279                    SubscriptionManager.MNC));
280
281            logd("[getSubInfoRecord] id:" + id + " iccid:" + iccId + " simSlotIndex:" + simSlotIndex
282                    + " displayName:" + displayName + " color:" + color + " mcc:" + mcc
283                    + " mnc:" + mnc);
284
285            return new SubInfoRecord(id, iccId, simSlotIndex, displayName, nameSource, color,
286                    number, dataRoaming, simIconRes, mcc, mnc);
287    }
288
289    /**
290     * Query SubInfoRecord(s) from subinfo database
291     * @param selection A filter declaring which rows to return
292     * @param queryKey query key content
293     * @return Array list of queried result from database
294     */
295     private List<SubInfoRecord> getSubInfo(String selection, Object queryKey) {
296        logd("selection:" + selection + " " + queryKey);
297        String[] selectionArgs = null;
298        if (queryKey != null) {
299            selectionArgs = new String[] {queryKey.toString()};
300        }
301        ArrayList<SubInfoRecord> subList = null;
302        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
303                null, selection, selectionArgs, null);
304        try {
305            if (cursor != null) {
306                while (cursor.moveToNext()) {
307                    SubInfoRecord subInfo = getSubInfoRecord(cursor);
308                    if (subInfo != null)
309                    {
310                        if (subList == null)
311                        {
312                            subList = new ArrayList<SubInfoRecord>();
313                        }
314                        subList.add(subInfo);
315                }
316                }
317            } else {
318                logd("Query fail");
319            }
320        } finally {
321            if (cursor != null) {
322                cursor.close();
323            }
324        }
325
326        return subList;
327    }
328
329
330
331    /**
332     * Get the SubInfoRecord according to an index
333     * @param subId The unique SubInfoRecord index in database
334     * @return SubInfoRecord, maybe null
335     */
336    @Override
337    public SubInfoRecord getSubInfoForSubscriber(int subId) {
338        logd("[getSubInfoForSubscriberx]+ subId:" + subId);
339        enforceSubscriptionPermission();
340
341        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
342            subId = getDefaultSubId();
343        }
344        if (!SubscriptionManager.isValidSubId(subId) || !isSubInfoReady()) {
345            logd("[getSubInfoForSubscriberx]- invalid subId or not ready");
346            return null;
347        }
348        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
349                null, BaseColumns._ID + "=?", new String[] {Long.toString(subId)}, null);
350        try {
351            if (cursor != null) {
352                if (cursor.moveToFirst()) {
353                    logd("[getSubInfoForSubscriberx]- Info detail:");
354                    return getSubInfoRecord(cursor);
355                }
356            }
357        } finally {
358            if (cursor != null) {
359                cursor.close();
360            }
361        }
362        logd("[getSubInfoForSubscriber]- null info return");
363
364        return null;
365    }
366
367    /**
368     * Get the SubInfoRecord according to an IccId
369     * @param iccId the IccId of SIM card
370     * @return SubInfoRecord, maybe null
371     */
372    @Override
373    public List<SubInfoRecord> getSubInfoUsingIccId(String iccId) {
374        logd("[getSubInfoUsingIccId]+ iccId:" + iccId);
375        enforceSubscriptionPermission();
376
377        if (iccId == null || !isSubInfoReady()) {
378            logd("[getSubInfoUsingIccId]- null iccid or not ready");
379            return null;
380        }
381        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
382                null, SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null);
383        ArrayList<SubInfoRecord> subList = null;
384        try {
385            if (cursor != null) {
386                while (cursor.moveToNext()) {
387                    SubInfoRecord subInfo = getSubInfoRecord(cursor);
388                    if (subInfo != null)
389                    {
390                        if (subList == null)
391                        {
392                            subList = new ArrayList<SubInfoRecord>();
393                        }
394                        subList.add(subInfo);
395                }
396                }
397            } else {
398                logd("Query fail");
399            }
400        } finally {
401            if (cursor != null) {
402                cursor.close();
403            }
404        }
405
406        return subList;
407    }
408
409    /**
410     * Get the SubInfoRecord according to slotId
411     * @param slotId the slot which the SIM is inserted
412     * @return SubInfoRecord, maybe null
413     */
414    @Override
415    public List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) {
416        return getSubInfoUsingSlotIdWithCheck(slotId, true);
417    }
418
419    /**
420     * Get all the SubInfoRecord(s) in subinfo database
421     * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before
422     */
423    @Override
424    public List<SubInfoRecord> getAllSubInfoList() {
425        logd("[getAllSubInfoList]+");
426        enforceSubscriptionPermission();
427
428        List<SubInfoRecord> subList = null;
429        subList = getSubInfo(null, null);
430        if (subList != null) {
431            logd("[getAllSubInfoList]- " + subList.size() + " infos return");
432        } else {
433            logd("[getAllSubInfoList]- no info return");
434        }
435
436        return subList;
437    }
438
439    /**
440     * Get the SubInfoRecord(s) of the currently inserted SIM(s)
441     * @return Array list of currently inserted SubInfoRecord(s)
442     */
443    @Override
444    public List<SubInfoRecord> getActiveSubInfoList() {
445        enforceSubscriptionPermission();
446        logdl("[getActiveSubInfoList]+");
447
448        List<SubInfoRecord> subList = null;
449
450        if (!isSubInfoReady()) {
451            logdl("[getActiveSubInfoList] Sub Controller not ready");
452            return subList;
453        }
454
455        subList = getSubInfo(SubscriptionManager.SIM_ID
456                + "!=" + SubscriptionManager.INVALID_SLOT_ID, null);
457        if (subList != null) {
458            logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
459        } else {
460            logdl("[getActiveSubInfoList]- no info return");
461        }
462
463        return subList;
464    }
465
466    /**
467     * Get the SUB count of active SUB(s)
468     * @return active SIM count
469     */
470    @Override
471    public int getActiveSubInfoCount() {
472        logd("[getActiveSubInfoCount]+");
473        List<SubInfoRecord> records = getActiveSubInfoList();
474        if (records == null) {
475            logd("[getActiveSubInfoCount] records null");
476            return 0;
477        }
478        logd("[getActiveSubInfoCount]- count: " + records.size());
479        return records.size();
480    }
481
482    /**
483     * Get the SUB count of all SUB(s) in subinfo database
484     * @return all SIM count in database, include what was inserted before
485     */
486    @Override
487    public int getAllSubInfoCount() {
488        logd("[getAllSubInfoCount]+");
489        enforceSubscriptionPermission();
490
491        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
492                null, null, null, null);
493        try {
494            if (cursor != null) {
495                int count = cursor.getCount();
496                logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
497                return count;
498            }
499        } finally {
500            if (cursor != null) {
501                cursor.close();
502            }
503        }
504        logd("[getAllSubInfoCount]- no SUB in DB");
505
506        return 0;
507    }
508
509    /**
510     * Add a new SubInfoRecord to subinfo database if needed
511     * @param iccId the IccId of the SIM card
512     * @param slotId the slot which the SIM is inserted
513     * @return the URL of the newly created row or the updated row
514     */
515    @Override
516    public int addSubInfoRecord(String iccId, int slotId) {
517        logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
518        enforceSubscriptionPermission();
519
520        if (iccId == null) {
521            logdl("[addSubInfoRecord]- null iccId");
522        }
523
524        int[] subIds = getSubId(slotId);
525        if (subIds == null || subIds.length == 0) {
526            logdl("[addSubInfoRecord]- getSubId fail");
527            return 0;
528        }
529
530        String nameToSet;
531        SpnOverride mSpnOverride = new SpnOverride();
532
533        String CarrierName = TelephonyManager.getDefault().getSimOperator(subIds[0]);
534        logdl("[addSubInfoRecord] CarrierName = " + CarrierName);
535
536        if (mSpnOverride.containsCarrier(CarrierName)) {
537            nameToSet = mSpnOverride.getSpn(CarrierName) + " 0" + Integer.toString(slotId + 1);
538            logdl("[addSubInfoRecord] SpnOverride set name=" + nameToSet);
539        } else {
540            nameToSet = "";
541            logdl("[addSubInfoRecord] no SpnOverride");
542        }
543
544        ContentResolver resolver = mContext.getContentResolver();
545        Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
546                new String[] {BaseColumns._ID, SubscriptionManager.SIM_ID,
547                SubscriptionManager.NAME_SOURCE}, SubscriptionManager.ICC_ID + "=?",
548                new String[] {iccId}, null);
549
550        try {
551            if (cursor == null || !cursor.moveToFirst()) {
552                ContentValues value = new ContentValues();
553                value.put(SubscriptionManager.ICC_ID, iccId);
554                // default SIM color differs between slots
555                value.put(SubscriptionManager.COLOR, slotId);
556                value.put(SubscriptionManager.SIM_ID, slotId);
557                value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
558                Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
559                logdl("[addSubInfoRecord]- New record created: " + uri);
560            } else {
561                int subId = cursor.getInt(0);
562                int oldSimInfoId = cursor.getInt(1);
563                int nameSource = cursor.getInt(2);
564                ContentValues value = new ContentValues();
565
566                if (slotId != oldSimInfoId) {
567                    value.put(SubscriptionManager.SIM_ID, slotId);
568                }
569
570                if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
571                    value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
572                }
573
574                if (value.size() > 0) {
575                    resolver.update(SubscriptionManager.CONTENT_URI, value,
576                            BaseColumns._ID + "=" + Long.toString(subId), null);
577                }
578
579                logdl("[addSubInfoRecord]- Record already exist");
580            }
581        } finally {
582            if (cursor != null) {
583                cursor.close();
584            }
585        }
586
587        cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
588                SubscriptionManager.SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null);
589
590        try {
591            if (cursor != null && cursor.moveToFirst()) {
592                do {
593                    int subId = cursor.getInt(cursor.getColumnIndexOrThrow(BaseColumns._ID));
594                    // If mSimInfo already has a valid subId for a slotId/phoneId,
595                    // do not add another subId for same slotId/phoneId.
596                    Integer currentSubId = mSimInfo.get(slotId);
597                    if (currentSubId == null || !SubscriptionManager.isValidSubId(currentSubId)) {
598                        // TODO While two subs active, if user deactivats first
599                        // one, need to update the default subId with second one.
600
601                        // FIXME: Currently we assume phoneId and slotId may not be true
602                        // when we cross map modem or when multiple subs per slot.
603                        // But is true at the moment.
604                        mSimInfo.put(slotId, subId);
605                        int simCount = TelephonyManager.getDefault().getSimCount();
606                        int defaultSubId = getDefaultSubId();
607                        logdl("[addSubInfoRecord] mSimInfo.size=" + mSimInfo.size()
608                                + " slotId=" + slotId + " subId=" + subId
609                                + " defaultSubId=" + defaultSubId + " simCount=" + simCount);
610
611                        // Set the default sub if not set or if single sim device
612                        if (!SubscriptionManager.isValidSubId(defaultSubId) || simCount == 1) {
613                            setDefaultSubId(subId);
614                        }
615                        // If single sim device, set this subscription as the default for everything
616                        if (simCount == 1) {
617                            logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
618                            setDefaultDataSubId(subId);
619                            setDefaultSmsSubId(subId);
620                            setDefaultVoiceSubId(subId);
621                        }
622                    } else {
623                        logdl("[addSubInfoRecord] currentSubId != null && currentSubId is valid, IGNORE");
624                    }
625                    logdl("[addSubInfoRecord]- hashmap("+slotId+","+subId+")");
626                } while (cursor.moveToNext());
627            }
628        } finally {
629            if (cursor != null) {
630                cursor.close();
631            }
632        }
633
634        int size = mSimInfo.size();
635        logdl("[addSubInfoRecord]- info size="+size);
636
637        // Once the records are loaded, notify DcTracker
638        updateAllDataConnectionTrackers();
639
640        // FIXME this does not match the javadoc
641        return 1;
642    }
643
644    /**
645     * Set SIM color by simInfo index
646     * @param color the color of the SIM
647     * @param subId the unique SubInfoRecord index in database
648     * @return the number of records updated
649     */
650    @Override
651    public int setColor(int color, int subId) {
652        logd("[setColor]+ color:" + color + " subId:" + subId);
653        enforceSubscriptionPermission();
654
655        validateSubId(subId);
656        int size = sSimBackgroundDarkRes.length;
657        ContentValues value = new ContentValues(1);
658        value.put(SubscriptionManager.COLOR, color);
659        logd("[setColor]- color:" + color + " set");
660
661        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
662                BaseColumns._ID + "=" + Long.toString(subId), null);
663        broadcastSimInfoContentChanged(subId, SubscriptionManager.COLOR,
664                color, SubscriptionManager.DEFAULT_STRING_VALUE);
665
666        return result;
667    }
668
669    /**
670     * Set display name by simInfo index
671     * @param displayName the display name of SIM card
672     * @param subId the unique SubInfoRecord index in database
673     * @return the number of records updated
674     */
675    @Override
676    public int setDisplayName(String displayName, int subId) {
677        return setDisplayNameUsingSrc(displayName, subId, -1);
678    }
679
680    /**
681     * Set display name by simInfo index with name source
682     * @param displayName the display name of SIM card
683     * @param subId the unique SubInfoRecord index in database
684     * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
685     *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
686     * @return the number of records updated
687     */
688    @Override
689    public int setDisplayNameUsingSrc(String displayName, int subId, long nameSource) {
690        logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
691                + " nameSource:" + nameSource);
692        enforceSubscriptionPermission();
693
694        validateSubId(subId);
695        String nameToSet;
696        if (displayName == null) {
697            nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
698        } else {
699            nameToSet = displayName;
700        }
701        ContentValues value = new ContentValues(1);
702        value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
703        if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
704            logd("Set nameSource=" + nameSource);
705            value.put(SubscriptionManager.NAME_SOURCE, nameSource);
706        }
707        logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
708
709        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
710                BaseColumns._ID + "=" + Long.toString(subId), null);
711        broadcastSimInfoContentChanged(subId, SubscriptionManager.DISPLAY_NAME,
712                SubscriptionManager.DEFAULT_INT_VALUE, nameToSet);
713
714        return result;
715    }
716
717    /**
718     * Set phone number by subId
719     * @param number the phone number of the SIM
720     * @param subId the unique SubInfoRecord index in database
721     * @return the number of records updated
722     */
723    @Override
724    public int setDisplayNumber(String number, int subId) {
725        logd("[setDisplayNumber]+ number:" + number + " subId:" + subId);
726        enforceSubscriptionPermission();
727
728        validateSubId(subId);
729        int result = 0;
730        int phoneId = getPhoneId(subId);
731
732        if (number == null || phoneId < 0 ||
733                phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
734            logd("[setDispalyNumber]- fail");
735            return -1;
736        }
737        ContentValues value = new ContentValues(1);
738        value.put(SubscriptionManager.NUMBER, number);
739        logd("[setDisplayNumber]- number:" + number + " set");
740
741        Phone phone = sProxyPhones[phoneId];
742        String alphaTag = TelephonyManager.getDefault().getLine1AlphaTagForSubscriber(subId);
743
744        synchronized(mLock) {
745            mSuccess = false;
746            Message response = mHandler.obtainMessage(EVENT_WRITE_MSISDN_DONE);
747
748            phone.setLine1Number(alphaTag, number, response);
749
750            try {
751                mLock.wait();
752            } catch (InterruptedException e) {
753                loge("interrupted while trying to write MSISDN");
754            }
755        }
756
757        if (mSuccess) {
758            result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
759                    BaseColumns._ID + "=" + Long.toString(subId), null);
760            logd("[setDisplayNumber]- update result :" + result);
761            broadcastSimInfoContentChanged(subId, SubscriptionManager.NUMBER,
762                    SubscriptionManager.DEFAULT_INT_VALUE, number);
763        }
764
765        return result;
766    }
767
768    /**
769     * Set number display format. 0: none, 1: the first four digits, 2: the last four digits
770     * @param format the display format of phone number
771     * @param subId the unique SubInfoRecord index in database
772     * @return the number of records updated
773     */
774    @Override
775    public int setDisplayNumberFormat(int format, int subId) {
776        logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId);
777        enforceSubscriptionPermission();
778
779        validateSubId(subId);
780        if (format < 0) {
781            logd("[setDisplayNumberFormat]- fail, return -1");
782            return -1;
783        }
784        ContentValues value = new ContentValues(1);
785        value.put(SubscriptionManager.DISPLAY_NUMBER_FORMAT, format);
786        logd("[setDisplayNumberFormat]- format:" + format + " set");
787
788        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
789                BaseColumns._ID + "=" + Long.toString(subId), null);
790        broadcastSimInfoContentChanged(subId, SubscriptionManager.DISPLAY_NUMBER_FORMAT,
791                format, SubscriptionManager.DEFAULT_STRING_VALUE);
792
793        return result;
794    }
795
796    /**
797     * Set data roaming by simInfo index
798     * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
799     * @param subId the unique SubInfoRecord index in database
800     * @return the number of records updated
801     */
802    @Override
803    public int setDataRoaming(int roaming, int subId) {
804        logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
805        enforceSubscriptionPermission();
806
807        validateSubId(subId);
808        if (roaming < 0) {
809            logd("[setDataRoaming]- fail");
810            return -1;
811        }
812        ContentValues value = new ContentValues(1);
813        value.put(SubscriptionManager.DATA_ROAMING, roaming);
814        logd("[setDataRoaming]- roaming:" + roaming + " set");
815
816        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
817                BaseColumns._ID + "=" + Long.toString(subId), null);
818        broadcastSimInfoContentChanged(subId, SubscriptionManager.DATA_ROAMING,
819                roaming, SubscriptionManager.DEFAULT_STRING_VALUE);
820
821        return result;
822    }
823
824    /**
825     * Set MCC/MNC by subscription ID
826     * @param mccMnc MCC/MNC associated with the subscription
827     * @param subId the unique SubInfoRecord index in database
828     * @return the number of records updated
829     */
830    public int setMccMnc(String mccMnc, int subId) {
831        int mcc = 0;
832        int mnc = 0;
833        try {
834            mcc = Integer.parseInt(mccMnc.substring(0,3));
835            mnc = Integer.parseInt(mccMnc.substring(3));
836        } catch (NumberFormatException e) {
837            logd("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
838        }
839        logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
840        ContentValues value = new ContentValues(2);
841        value.put(SubscriptionManager.MCC, mcc);
842        value.put(SubscriptionManager.MNC, mnc);
843
844        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
845                BaseColumns._ID + "=" + Long.toString(subId), null);
846        broadcastSimInfoContentChanged(subId, SubscriptionManager.MCC, mcc, null);
847
848        return result;
849    }
850
851
852    @Override
853    public int getSlotId(int subId) {
854        if (VDBG) printStackTrace("[getSlotId] subId=" + subId);
855
856        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
857            subId = getDefaultSubId();
858        }
859        if (!SubscriptionManager.isValidSubId(subId)) {
860            logd("[getSlotId]- subId invalid");
861            return SubscriptionManager.INVALID_SLOT_ID;
862        }
863
864        int size = mSimInfo.size();
865
866        if (size == 0)
867        {
868            logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead");
869            return SubscriptionManager.SIM_NOT_INSERTED;
870        }
871
872        for (Entry<Integer, Integer> entry: mSimInfo.entrySet()) {
873            int sim = entry.getKey();
874            int sub = entry.getValue();
875
876            if (subId == sub)
877            {
878                if (VDBG) logv("[getSlotId]- return = " + sim);
879                return sim;
880            }
881        }
882
883        logd("[getSlotId]- return fail");
884        return SubscriptionManager.INVALID_SLOT_ID;
885    }
886
887    /**
888     * Return the subId for specified slot Id.
889     * @deprecated
890     */
891    @Override
892    @Deprecated
893    public int[] getSubId(int slotId) {
894        if (VDBG) printStackTrace("[getSubId] slotId=" + slotId);
895
896        if (slotId == SubscriptionManager.DEFAULT_SLOT_ID) {
897            logd("[getSubId]- default slotId");
898            slotId = getSlotId(getDefaultSubId());
899        }
900
901        //FIXME remove this
902        final int[] DUMMY_VALUES = {-1 - slotId, -1 - slotId};
903
904        if (!SubscriptionManager.isValidSlotId(slotId)) {
905            logd("[getSubId]- invalid slotId");
906            return null;
907        }
908
909        //FIXME remove this
910        if (slotId < 0) {
911            logd("[getSubId]- slotId < 0, return dummy instead");
912            return DUMMY_VALUES;
913        }
914
915        int size = mSimInfo.size();
916
917        if (size == 0) {
918            logd("[getSubId]- size == 0, return dummy instead");
919            //FIXME return null
920            return DUMMY_VALUES;
921        }
922
923        ArrayList<Integer> subIds = new ArrayList<Integer>();
924        for (Entry<Integer, Integer> entry: mSimInfo.entrySet()) {
925            int slot = entry.getKey();
926            int sub = entry.getValue();
927            if (slotId == slot) {
928                subIds.add(sub);
929            }
930        }
931
932        if (VDBG) logd("[getSubId]-, subIds = " + subIds);
933        int numSubIds = subIds.size();
934
935        if (numSubIds == 0) {
936            logd("[getSubId]- numSubIds == 0, return dummy instead");
937            return DUMMY_VALUES;
938        }
939
940        int[] subIdArr = new int[numSubIds];
941        for (int i = 0; i < numSubIds; i++) {
942            subIdArr[i] = subIds.get(i);
943        }
944
945        return subIdArr;
946    }
947
948    @Override
949    public int getPhoneId(int subId) {
950        if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
951        int phoneId;
952
953        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
954            subId = getDefaultSubId();
955            logdl("[getPhoneId] asked for default subId=" + subId);
956        }
957
958        if (!SubscriptionManager.isValidSubId(subId)) {
959            logdl("[getPhoneId]- invalid subId return=" + SubscriptionManager.INVALID_PHONE_ID);
960            return SubscriptionManager.INVALID_PHONE_ID;
961        }
962
963        //FIXME remove this
964        if (subId < 0) {
965            phoneId = (int) (-1 - subId);
966            if (VDBG) logdl("[getPhoneId]- map subId=" + subId + " phoneId=" + phoneId);
967            return phoneId;
968        }
969
970        int size = mSimInfo.size();
971
972        if (size == 0) {
973            phoneId = mDefaultPhoneId;
974            logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
975            return phoneId;
976        }
977
978        // FIXME: Assumes phoneId == slotId
979        for (Entry<Integer, Integer> entry: mSimInfo.entrySet()) {
980            int sim = entry.getKey();
981            int sub = entry.getValue();
982
983            if (subId == sub) {
984                if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
985                return sim;
986            }
987        }
988
989        phoneId = mDefaultPhoneId;
990        logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId);
991        return phoneId;
992
993    }
994
995    /**
996     * @return the number of records cleared
997     */
998    @Override
999    public int clearSubInfo() {
1000        enforceSubscriptionPermission();
1001        logd("[clearSubInfo]+");
1002
1003        int size = mSimInfo.size();
1004
1005        if (size == 0) {
1006            logdl("[clearSubInfo]- no simInfo size=" + size);
1007            return 0;
1008        }
1009
1010        mSimInfo.clear();
1011        logdl("[clearSubInfo]- clear size=" + size);
1012        return size;
1013    }
1014
1015    private static int[] setSimResource(int type) {
1016        int[] simResource = null;
1017
1018        switch (type) {
1019            case RES_TYPE_BACKGROUND_DARK:
1020                simResource = new int[] {
1021                    com.android.internal.R.drawable.sim_dark_blue,
1022                    com.android.internal.R.drawable.sim_dark_orange,
1023                    com.android.internal.R.drawable.sim_dark_green,
1024                    com.android.internal.R.drawable.sim_dark_purple
1025                };
1026                break;
1027            case RES_TYPE_BACKGROUND_LIGHT:
1028                simResource = new int[] {
1029                    com.android.internal.R.drawable.sim_light_blue,
1030                    com.android.internal.R.drawable.sim_light_orange,
1031                    com.android.internal.R.drawable.sim_light_green,
1032                    com.android.internal.R.drawable.sim_light_purple
1033                };
1034                break;
1035        }
1036
1037        return simResource;
1038    }
1039
1040    private void logvl(String msg) {
1041        logv(msg);
1042        mLocalLog.log(msg);
1043    }
1044
1045    private void logv(String msg) {
1046        Rlog.v(LOG_TAG, msg);
1047    }
1048
1049    private void logdl(String msg) {
1050        logd(msg);
1051        mLocalLog.log(msg);
1052    }
1053
1054    private static void slogd(String msg) {
1055        Rlog.d(LOG_TAG, msg);
1056    }
1057
1058    private void logd(String msg) {
1059        Rlog.d(LOG_TAG, msg);
1060    }
1061
1062    private void logel(String msg) {
1063        loge(msg);
1064        mLocalLog.log(msg);
1065    }
1066
1067    private void loge(String msg) {
1068        Rlog.e(LOG_TAG, msg);
1069    }
1070
1071    @Override
1072    public int getDefaultSubId() {
1073        //FIXME: Make this smarter, need to handle data only and voice devices
1074        int subId = mDefaultVoiceSubId;
1075        if (VDBG) logv("[getDefaultSubId] value = " + subId);
1076        return subId;
1077    }
1078
1079    @Override
1080    public void setDefaultSmsSubId(int subId) {
1081        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
1082            throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
1083        }
1084        logdl("[setDefaultSmsSubId] subId=" + subId);
1085        Settings.Global.putInt(mContext.getContentResolver(),
1086                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
1087        broadcastDefaultSmsSubIdChanged(subId);
1088    }
1089
1090    private void broadcastDefaultSmsSubIdChanged(int subId) {
1091        // Broadcast an Intent for default sms sub change
1092        logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
1093        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
1094        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1095        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1096        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1097    }
1098
1099    @Override
1100    public int getDefaultSmsSubId() {
1101        int subId = Settings.Global.getInt(mContext.getContentResolver(),
1102                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
1103                SubscriptionManager.INVALID_SUB_ID);
1104        if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId);
1105        return subId;
1106    }
1107
1108    @Override
1109    public void setDefaultVoiceSubId(int subId) {
1110        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
1111            throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
1112        }
1113        logdl("[setDefaultVoiceSubId] subId=" + subId);
1114        Settings.Global.putInt(mContext.getContentResolver(),
1115                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
1116        broadcastDefaultVoiceSubIdChanged(subId);
1117    }
1118
1119    private void broadcastDefaultVoiceSubIdChanged(int subId) {
1120        // Broadcast an Intent for default voice sub change
1121        logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
1122        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
1123        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1124        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1125        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1126    }
1127
1128    @Override
1129    public int getDefaultVoiceSubId() {
1130        int subId = Settings.Global.getInt(mContext.getContentResolver(),
1131                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
1132                SubscriptionManager.INVALID_SUB_ID);
1133        if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
1134        return subId;
1135    }
1136
1137    @Override
1138    public int getDefaultDataSubId() {
1139        int subId = Settings.Global.getInt(mContext.getContentResolver(),
1140                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
1141                SubscriptionManager.INVALID_SUB_ID);
1142        if (VDBG) logd("[getDefaultDataSubId] subId= " + subId);
1143        return subId;
1144    }
1145
1146    @Override
1147    public void setDefaultDataSubId(int subId) {
1148        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
1149            throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
1150        }
1151        logdl("[setDefaultDataSubId] subId=" + subId);
1152
1153        Settings.Global.putInt(mContext.getContentResolver(),
1154                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
1155        broadcastDefaultDataSubIdChanged(subId);
1156
1157        // FIXME is this still needed?
1158        updateAllDataConnectionTrackers();
1159    }
1160
1161    private void updateAllDataConnectionTrackers() {
1162        // Tell Phone Proxies to update data connection tracker
1163        int len = sProxyPhones.length;
1164        logdl("[updateAllDataConnectionTrackers] sProxyPhones.length=" + len);
1165        for (int phoneId = 0; phoneId < len; phoneId++) {
1166            logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId);
1167            sProxyPhones[phoneId].updateDataConnectionTracker();
1168        }
1169    }
1170
1171    private void broadcastDefaultDataSubIdChanged(int subId) {
1172        // Broadcast an Intent for default data sub change
1173        logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
1174        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
1175        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1176        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1177        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1178    }
1179
1180    /* Sets the default subscription. If only one sub is active that
1181     * sub is set as default subId. If two or more  sub's are active
1182     * the first sub is set as default subscription
1183     */
1184    // FIXME
1185    public void setDefaultSubId(int subId) {
1186        if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
1187            throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
1188        }
1189        logdl("[setDefaultSubId] subId=" + subId);
1190        if (SubscriptionManager.isValidSubId(subId)) {
1191            int phoneId = getPhoneId(subId);
1192            if (phoneId >= 0 && (phoneId < TelephonyManager.getDefault().getPhoneCount()
1193                    || TelephonyManager.getDefault().getSimCount() == 1)) {
1194                logdl("[setDefaultSubId] set mDefaultVoiceSubId=" + subId);
1195                mDefaultVoiceSubId = subId;
1196                // Update MCC MNC device configuration information
1197                String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId);
1198                MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);
1199
1200                // Broadcast an Intent for default sub change
1201                Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
1202                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1203                SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
1204                if (VDBG) {
1205                    logdl("[setDefaultSubId] broadcast default subId changed phoneId=" + phoneId
1206                            + " subId=" + subId);
1207                }
1208                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1209            } else {
1210                if (VDBG) {
1211                    logdl("[setDefaultSubId] not set invalid phoneId=" + phoneId + " subId=" + subId);
1212                }
1213            }
1214        }
1215    }
1216
1217    @Override
1218    public void clearDefaultsForInactiveSubIds() {
1219        final List<SubInfoRecord> records = getActiveSubInfoList();
1220        logdl("[clearDefaultsForInactiveSubIds] records: " + records);
1221        if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
1222            logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
1223            setDefaultDataSubId(SubscriptionManager.INVALID_SUB_ID);
1224        }
1225        if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
1226            logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
1227            setDefaultSmsSubId(SubscriptionManager.INVALID_SUB_ID);
1228        }
1229        if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
1230            logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
1231            setDefaultVoiceSubId(SubscriptionManager.INVALID_SUB_ID);
1232        }
1233    }
1234
1235    private boolean shouldDefaultBeCleared(List<SubInfoRecord> records, int subId) {
1236        logdl("[shouldDefaultBeCleared: subId] " + subId);
1237        if (records == null) {
1238            logdl("[shouldDefaultBeCleared] return true no records subId=" + subId);
1239            return true;
1240        }
1241        if (subId == SubscriptionManager.ASK_USER_SUB_ID && records.size() > 1) {
1242            // Only allow ASK_USER_SUB_ID if there is more than 1 subscription.
1243            logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId);
1244            return false;
1245        }
1246        for (SubInfoRecord record : records) {
1247            int id = record.getSubscriptionId();
1248            logdl("[shouldDefaultBeCleared] Record.id: " + id);
1249            if (id == subId) {
1250                logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId);
1251                return false;
1252            }
1253        }
1254        logdl("[shouldDefaultBeCleared] return true not active subId=" + subId);
1255        return true;
1256    }
1257
1258    // FIXME: We need we should not be assuming phoneId == slotId as it will not be true
1259    // when there are multiple subscriptions per sim and probably for other reasons.
1260    public int getSubIdUsingPhoneId(int phoneId) {
1261        int[] subIds = getSubId(phoneId);
1262        if (subIds == null || subIds.length == 0) {
1263            return SubscriptionManager.INVALID_SUB_ID;
1264        }
1265        return subIds[0];
1266    }
1267
1268    public int[] getSubIdUsingSlotId(int slotId) {
1269        return getSubId(slotId);
1270    }
1271
1272    public List<SubInfoRecord> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) {
1273        logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
1274        enforceSubscriptionPermission();
1275
1276        if (slotId == SubscriptionManager.DEFAULT_SLOT_ID) {
1277            slotId = getSlotId(getDefaultSubId());
1278        }
1279        if (!SubscriptionManager.isValidSlotId(slotId)) {
1280            logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
1281            return null;
1282        }
1283
1284        if (needCheck && !isSubInfoReady()) {
1285            logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
1286            return null;
1287        }
1288
1289        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
1290                null, SubscriptionManager.SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null);
1291        ArrayList<SubInfoRecord> subList = null;
1292        try {
1293            if (cursor != null) {
1294                while (cursor.moveToNext()) {
1295                    SubInfoRecord subInfo = getSubInfoRecord(cursor);
1296                    if (subInfo != null)
1297                    {
1298                        if (subList == null)
1299                        {
1300                            subList = new ArrayList<SubInfoRecord>();
1301                        }
1302                        subList.add(subInfo);
1303                    }
1304                }
1305            }
1306        } finally {
1307            if (cursor != null) {
1308                cursor.close();
1309            }
1310        }
1311        logd("[getSubInfoUsingSlotId]- null info return");
1312
1313        return subList;
1314    }
1315
1316    private void validateSubId(int subId) {
1317        logd("validateSubId subId: " + subId);
1318        if (!SubscriptionManager.isValidSubId(subId)) {
1319            throw new RuntimeException("Invalid sub id passed as parameter");
1320        } else if (subId == SubscriptionManager.DEFAULT_SUB_ID) {
1321            throw new RuntimeException("Default sub id passed as parameter");
1322        }
1323    }
1324
1325    public void updatePhonesAvailability(PhoneProxy[] phones) {
1326        sProxyPhones = phones;
1327    }
1328
1329    /**
1330     * @return the list of subId's that are active, is never null but the length maybe 0.
1331     */
1332    @Override
1333    public int[] getActiveSubIdList() {
1334        Set<Entry<Integer, Integer>> simInfoSet = mSimInfo.entrySet();
1335        logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet);
1336
1337        int[] subIdArr = new int[simInfoSet.size()];
1338        int i = 0;
1339        for (Entry<Integer, Integer> entry: simInfoSet) {
1340            int sub = entry.getValue();
1341            subIdArr[i] = sub;
1342            i++;
1343        }
1344
1345        logdl("[getActiveSubIdList] X subIdArr.length=" + subIdArr.length);
1346        return subIdArr;
1347    }
1348
1349    private static void printStackTrace(String msg) {
1350        RuntimeException re = new RuntimeException();
1351        slogd("StackTrace - " + msg);
1352        StackTraceElement[] st = re.getStackTrace();
1353        boolean first = true;
1354        for (StackTraceElement ste : st) {
1355            if (first) {
1356                first = false;
1357            } else {
1358                slogd(ste.toString());
1359            }
1360        }
1361    }
1362
1363    @Override
1364    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1365        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
1366                "Requires DUMP");
1367        pw.println("SubscriptionController:");
1368        pw.println(" defaultSubId=" + getDefaultSubId());
1369        pw.println(" defaultDataSubId=" + getDefaultDataSubId());
1370        pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
1371        pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());
1372
1373        pw.println(" defaultDataPhoneId=" + SubscriptionManager.getDefaultDataPhoneId());
1374        pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
1375        pw.println(" defaultSmsPhoneId=" + SubscriptionManager.getDefaultSmsPhoneId());
1376        pw.flush();
1377
1378        for (Entry<Integer, Integer> entry : mSimInfo.entrySet()) {
1379            pw.println(" mSimInfo[" + entry.getKey() + "]: subId=" + entry.getValue());
1380        }
1381        pw.flush();
1382        pw.println("++++++++++++++++++++++++++++++++");
1383
1384        List<SubInfoRecord> sirl = getActiveSubInfoList();
1385        if (sirl != null) {
1386            pw.println(" ActiveSubInfoList:");
1387            for (SubInfoRecord entry : sirl) {
1388                pw.println("  " + entry.toString());
1389            }
1390        } else {
1391            pw.println(" ActiveSubInfoList: is null");
1392        }
1393        pw.flush();
1394        pw.println("++++++++++++++++++++++++++++++++");
1395
1396        sirl = getAllSubInfoList();
1397        if (sirl != null) {
1398            pw.println(" AllSubInfoList:");
1399            for (SubInfoRecord entry : sirl) {
1400                pw.println("  " + entry.toString());
1401            }
1402        } else {
1403            pw.println(" AllSubInfoList: is null");
1404        }
1405        pw.flush();
1406        pw.println("++++++++++++++++++++++++++++++++");
1407
1408        mLocalLog.dump(fd, pw, args);
1409        pw.flush();
1410        pw.println("++++++++++++++++++++++++++++++++");
1411        pw.flush();
1412    }
1413}
1414