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