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