SubInfoRecordUpdater.java revision 6a5ef38e6ae3d3a3ad90ae180388fe85de0495a2
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 static android.Manifest.permission.READ_PHONE_STATE;
20import android.app.ActivityManagerNative;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.ContentResolver;
24import android.content.ContentValues;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.os.AsyncResult;
28import android.os.Handler;
29import android.os.Message;
30import android.os.UserHandle;
31import android.telephony.Rlog;
32import android.telephony.SubscriptionManager;
33import android.telephony.SubInfoRecord;
34import android.telephony.TelephonyManager;
35import com.android.internal.telephony.CommandsInterface;
36import com.android.internal.telephony.IccCardConstants;
37import com.android.internal.telephony.Phone;
38import com.android.internal.telephony.PhoneConstants;
39import com.android.internal.telephony.PhoneProxy;
40import com.android.internal.telephony.TelephonyIntents;
41import com.android.internal.telephony.uicc.IccConstants;
42import com.android.internal.telephony.uicc.IccFileHandler;
43import com.android.internal.telephony.uicc.IccUtils;
44import com.android.internal.telephony.uicc.SpnOverride;
45
46import java.util.List;
47
48/**
49 *@hide
50 */
51public class SubInfoRecordUpdater extends Handler {
52    private static final String LOG_TAG = "SUB";
53    private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount();
54    private static final int EVENT_OFFSET = 8;
55    private static final int EVENT_QUERY_ICCID_DONE = 1;
56    private static final String ICCID_STRING_FOR_NO_SIM = "";
57    /**
58     *  int[] sInsertSimState maintains all slots' SIM inserted status currently,
59     *  it may contain 4 kinds of values:
60     *    SIM_NOT_INSERT : no SIM inserted in slot i now
61     *    SIM_CHANGED    : a valid SIM insert in slot i and is different SIM from last time
62     *                     it will later become SIM_NEW or SIM_REPOSITION during update procedure
63     *    SIM_NOT_CHANGE : a valid SIM insert in slot i and is the same SIM as last time
64     *    SIM_NEW        : a valid SIM insert in slot i and is a new SIM
65     *    SIM_REPOSITION : a valid SIM insert in slot i and is inserted in different slot last time
66     *    positive integer #: index to distinguish SIM cards with the same IccId
67     */
68    public static final int SIM_NOT_CHANGE = 0;
69    public static final int SIM_CHANGED    = -1;
70    public static final int SIM_NEW        = -2;
71    public static final int SIM_REPOSITION = -3;
72    public static final int SIM_NOT_INSERT = -99;
73
74    public static final int STATUS_NO_SIM_INSERTED = 0x00;
75    public static final int STATUS_SIM1_INSERTED = 0x01;
76    public static final int STATUS_SIM2_INSERTED = 0x02;
77    public static final int STATUS_SIM3_INSERTED = 0x04;
78    public static final int STATUS_SIM4_INSERTED = 0x08;
79
80    private static Phone[] sPhone;
81    private static Context sContext = null;
82    private static IccFileHandler[] sFh = new IccFileHandler[PROJECT_SIM_NUM];
83    private static String sIccId[] = new String[PROJECT_SIM_NUM];
84    private static int[] sInsertSimState = new int[PROJECT_SIM_NUM];
85    private static TelephonyManager sTelephonyMgr = null;
86    // To prevent repeatedly update flow every time receiver SIM_STATE_CHANGE
87    private static boolean sNeedUpdate = true;
88
89    public SubInfoRecordUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) {
90        logd("Constructor invoked");
91
92        sContext = context;
93        sPhone = phoneProxy;
94        IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
95        sContext.registerReceiver(sReceiver, intentFilter);
96    }
97
98    private static int encodeEventId(int event, int slotId) {
99        return event << (slotId * EVENT_OFFSET);
100    }
101
102    private final BroadcastReceiver sReceiver = new  BroadcastReceiver() {
103        @Override
104        public void onReceive(Context context, Intent intent) {
105            logd("[Receiver]+");
106            String action = intent.getAction();
107            int slotId;
108            logd("Action: " + action);
109            if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
110                String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
111                slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY,
112                        SubscriptionManager.INVALID_SLOT_ID);
113                logd("slotId: " + slotId + " simStatus: " + simStatus);
114                if (slotId == SubscriptionManager.INVALID_SLOT_ID) {
115                    return;
116                }
117                if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(simStatus)
118                        || IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
119                    if (sIccId[slotId] != null && sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
120                        logd("SIM" + (slotId + 1) + " hot plug in");
121                        sIccId[slotId] = null;
122                        sNeedUpdate = true;
123                    }
124                    queryIccId(slotId);
125                } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
126                    queryIccId(slotId);
127                    if (sTelephonyMgr == null) {
128                        sTelephonyMgr = TelephonyManager.from(sContext);
129                    }
130
131                    long subId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY,
132                            SubscriptionManager.INVALID_SUB_ID);
133
134                    if (SubscriptionManager.isValidSubId(subId)) {
135                        String msisdn = TelephonyManager.getDefault().getLine1Number(subId);
136                        ContentResolver contentResolver = sContext.getContentResolver();
137
138                        if (msisdn != null) {
139                            ContentValues number = new ContentValues(1);
140                            number.put(SubscriptionManager.NUMBER, msisdn);
141                            contentResolver.update(SubscriptionManager.CONTENT_URI, number,
142                                    SubscriptionManager._ID + "=" + Long.toString(subId), null);
143                        }
144
145                        SubInfoRecord subInfo =
146                                SubscriptionManager.getSubInfoForSubscriber(subId);
147
148                        if (subInfo != null
149                                && subInfo.nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
150                            SpnOverride mSpnOverride = new SpnOverride();
151                            String nameToSet;
152                            String CarrierName =
153                                    TelephonyManager.getDefault().getSimOperator(subId);
154                            logd("CarrierName = " + CarrierName);
155
156                            if (mSpnOverride.containsCarrier(CarrierName)) {
157                                nameToSet = mSpnOverride.getSpn(CarrierName) + " 0"
158                                        + Integer.toString(slotId + 1);
159                                logd("Found, name = " + nameToSet);
160                            } else {
161                                nameToSet = "SUB 0" + Integer.toString(slotId + 1);
162                                logd("Not found, name = " + nameToSet);
163                            }
164
165                            ContentValues name = new ContentValues(1);
166                            name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
167                            contentResolver.update(SubscriptionManager.CONTENT_URI, name,
168                                    SubscriptionManager._ID + "=" + Long.toString(subId), null);
169                        }
170                    } else {
171                        logd("[Receiver] Invalid subId, could not update ContentResolver");
172                    }
173                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
174                    if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
175                        logd("SIM" + (slotId + 1) + " hot plug out");
176                        sNeedUpdate = true;
177                    }
178                    sFh[slotId] = null;
179                    sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
180                    if (isAllIccIdQueryDone() && sNeedUpdate) {
181                        updateSimInfoByIccId();
182                    }
183                }
184            }
185            logd("[Receiver]-");
186        }
187    };
188
189    private boolean isAllIccIdQueryDone() {
190        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
191            if (sIccId[i] == null) {
192                logd("Wait for SIM" + (i + 1) + " IccId");
193                return false;
194            }
195        }
196        logd("All IccIds query complete");
197
198        return true;
199    }
200
201    public static void setDisplayNameForNewSub(String newSubName, int subId, int newNameSource) {
202        SubInfoRecord subInfo = SubscriptionManager.getSubInfoForSubscriber(subId);
203        if (subInfo != null) {
204            // overwrite SIM display name if it is not assigned by user
205            int oldNameSource = subInfo.nameSource;
206            String oldSubName = subInfo.displayName;
207            logd("[setDisplayNameForNewSub] mSubInfoIdx = " + subInfo.subId + ", oldSimName = "
208                    + oldSubName + ", oldNameSource = " + oldNameSource + ", newSubName = "
209                    + newSubName + ", newNameSource = " + newNameSource);
210            if (oldSubName == null ||
211                (oldNameSource == SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE && newSubName != null) ||
212                (oldNameSource == SubscriptionManager.NAME_SOURCE_SIM_SOURCE && newSubName != null
213                        && !newSubName.equals(oldSubName))) {
214                SubscriptionManager.setDisplayName(newSubName,
215                        subInfo.subId, newNameSource);
216            }
217        } else {
218            logd("SUB" + (subId + 1) + " SubInfo not created yet");
219        }
220    }
221
222    @Override
223    public void handleMessage(Message msg) {
224        AsyncResult ar = (AsyncResult)msg.obj;
225        int msgNum = msg.what;
226        int slotId;
227        for (slotId = PhoneConstants.SUB1; slotId <= PhoneConstants.SUB3; slotId++) {
228            int pivot = 1 << (slotId * EVENT_OFFSET);
229            if (msgNum >= pivot) {
230                continue;
231            } else {
232                break;
233            }
234        }
235        slotId--;
236        int event = msgNum >> (slotId * EVENT_OFFSET);
237        switch (event) {
238            case EVENT_QUERY_ICCID_DONE:
239                logd("handleMessage : <EVENT_QUERY_ICCID_DONE> SIM" + (slotId + 1));
240                if (ar.exception == null) {
241                    if (ar.result != null) {
242                        byte[] data = (byte[])ar.result;
243                        sIccId[slotId] = IccUtils.bcdToString(data, 0, data.length);
244                    } else {
245                        logd("Null ar");
246                        sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
247                    }
248                } else {
249                    sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
250                    logd("Query IccId fail: " + ar.exception);
251                }
252                logd("sIccId[" + slotId + "] = " + sIccId[slotId]);
253                if (isAllIccIdQueryDone() && sNeedUpdate) {
254                    updateSimInfoByIccId();
255                }
256                break;
257            default:
258                logd("Unknown msg:" + msg.what);
259        }
260    }
261
262    private void queryIccId(int slotId) {
263        logd("queryIccId: slotid=" + slotId);
264        if (sFh[slotId] == null) {
265            logd("Getting IccFileHandler");
266            sFh[slotId] = ((PhoneProxy)sPhone[slotId]).getIccFileHandler();
267        }
268        if (sFh[slotId] != null) {
269            String iccId = sIccId[slotId];
270            if (iccId == null) {
271                logd("Querying IccId");
272                sFh[slotId].loadEFTransparent(IccConstants.EF_ICCID, obtainMessage(encodeEventId(EVENT_QUERY_ICCID_DONE, slotId)));
273            } else {
274                logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
275            }
276        } else {
277            logd("sFh[" + slotId + "] is null, ignore");
278        }
279    }
280
281    synchronized public void updateSimInfoByIccId() {
282        logd("[updateSimInfoByIccId]+ Start");
283        sNeedUpdate = false;
284
285        SubscriptionManager.clearSubInfo();
286
287        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
288            sInsertSimState[i] = SIM_NOT_CHANGE;
289        }
290
291        int insertedSimCount = PROJECT_SIM_NUM;
292        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
293            if (ICCID_STRING_FOR_NO_SIM.equals(sIccId[i])) {
294                insertedSimCount--;
295                sInsertSimState[i] = SIM_NOT_INSERT;
296            }
297        }
298        logd("insertedSimCount = " + insertedSimCount);
299
300        int index = 0;
301        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
302            if (sInsertSimState[i] == SIM_NOT_INSERT) {
303                continue;
304            }
305            index = 2;
306            for (int j = i + 1; j < PROJECT_SIM_NUM; j++) {
307                if (sInsertSimState[j] == SIM_NOT_CHANGE && sIccId[i].equals(sIccId[j])) {
308                    sInsertSimState[i] = 1;
309                    sInsertSimState[j] = index;
310                    index++;
311                }
312            }
313        }
314
315        ContentResolver contentResolver = sContext.getContentResolver();
316        String[] oldIccId = new String[PROJECT_SIM_NUM];
317        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
318            oldIccId[i] = null;
319            List<SubInfoRecord> oldSubInfo =
320                    SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false);
321            if (oldSubInfo != null) {
322                oldIccId[i] = oldSubInfo.get(0).iccId;
323                logd("oldSubId = " + oldSubInfo.get(0).subId);
324                if (sInsertSimState[i] == SIM_NOT_CHANGE && !sIccId[i].equals(oldIccId[i])) {
325                    sInsertSimState[i] = SIM_CHANGED;
326                }
327                if (sInsertSimState[i] != SIM_NOT_CHANGE) {
328                    ContentValues value = new ContentValues(1);
329                    value.put(SubscriptionManager.SIM_ID, SubscriptionManager.INVALID_SLOT_ID);
330                    contentResolver.update(SubscriptionManager.CONTENT_URI, value,
331                            SubscriptionManager._ID + "="
332                            + Long.toString(oldSubInfo.get(0).subId), null);
333                }
334            } else {
335                if (sInsertSimState[i] == SIM_NOT_CHANGE) {
336                    // no SIM inserted last time, but there is one SIM inserted now
337                    sInsertSimState[i] = SIM_CHANGED;
338                }
339                oldIccId[i] = ICCID_STRING_FOR_NO_SIM;
340                logd("No SIM in slot " + i + " last time");
341            }
342        }
343
344        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
345            logd("oldIccId[" + i + "] = " + oldIccId[i] + ", sIccId[" + i + "] = " + sIccId[i]);
346        }
347
348        //check if the inserted SIM is new SIM
349        int nNewCardCount = 0;
350        int nNewSimStatus = 0;
351        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
352            if (sInsertSimState[i] == SIM_NOT_INSERT) {
353                logd("No SIM inserted in slot " + i + " this time");
354            } else {
355                if (sInsertSimState[i] > 0) {
356                    //some special SIMs may have the same IccIds, add suffix to distinguish them
357                    //FIXME: addSubInfoRecord can return an error.
358                    SubscriptionManager.addSubInfoRecord(sIccId[i]
359                            + Integer.toString(sInsertSimState[i]), i);
360                    logd("SUB" + (i + 1) + " has invalid IccId");
361                } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
362                    SubscriptionManager.addSubInfoRecord(sIccId[i], i);
363                }
364                if (isNewSim(sIccId[i], oldIccId)) {
365                    nNewCardCount++;
366                    switch (i) {
367                        case PhoneConstants.SUB1:
368                            nNewSimStatus |= STATUS_SIM1_INSERTED;
369                            break;
370                        case PhoneConstants.SUB2:
371                            nNewSimStatus |= STATUS_SIM2_INSERTED;
372                            break;
373                        case PhoneConstants.SUB3:
374                            nNewSimStatus |= STATUS_SIM3_INSERTED;
375                            break;
376                        //case PhoneConstants.SUB3:
377                        //    nNewSimStatus |= STATUS_SIM4_INSERTED;
378                        //    break;
379                    }
380
381                    sInsertSimState[i] = SIM_NEW;
382                }
383            }
384        }
385
386        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
387            if (sInsertSimState[i] == SIM_CHANGED) {
388                sInsertSimState[i] = SIM_REPOSITION;
389            }
390            logd("sInsertSimState[" + i + "] = " + sInsertSimState[i]);
391        }
392
393        List<SubInfoRecord> subInfos = SubscriptionManager.getActiveSubInfoList();
394        int nSubCount = (subInfos == null) ? 0 : subInfos.size();
395        logd("nSubCount = " + nSubCount);
396        for (int i=0; i<nSubCount; i++) {
397            SubInfoRecord temp = subInfos.get(i);
398
399            String msisdn = TelephonyManager.getDefault().getLine1Number(temp.subId);
400
401            if (msisdn != null) {
402                ContentValues value = new ContentValues(1);
403                value.put(SubscriptionManager.NUMBER, msisdn);
404                contentResolver.update(SubscriptionManager.CONTENT_URI, value,
405                        SubscriptionManager._ID + "=" + Long.toString(temp.subId), null);
406            }
407        }
408
409        // true if any slot has no SIM this time, but has SIM last time
410        boolean hasSimRemoved = false;
411        for (int i=0; i < PROJECT_SIM_NUM; i++) {
412            if (sIccId[i] != null && sIccId[i].equals(ICCID_STRING_FOR_NO_SIM)
413                    && !oldIccId[i].equals("")) {
414                hasSimRemoved = true;
415                break;
416            }
417        }
418
419        if (nNewCardCount == 0) {
420            int i;
421            if (hasSimRemoved) {
422                // no new SIM, at least one SIM is removed, check if any SIM is repositioned first
423                for (i=0; i < PROJECT_SIM_NUM; i++) {
424                    if (sInsertSimState[i] == SIM_REPOSITION) {
425                        logd("No new SIM detected and SIM repositioned");
426                        setUpdatedData(SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM,
427                                nSubCount, nNewSimStatus);
428                        break;
429                    }
430                }
431                if (i == PROJECT_SIM_NUM) {
432                    // no new SIM, no SIM is repositioned => at least one SIM is removed
433                    logd("No new SIM detected and SIM removed");
434                    setUpdatedData(SubscriptionManager.EXTRA_VALUE_REMOVE_SIM,
435                            nSubCount, nNewSimStatus);
436                }
437            } else {
438                // no SIM is removed, no new SIM, just check if any SIM is repositioned
439                for (i=0; i< PROJECT_SIM_NUM; i++) {
440                    if (sInsertSimState[i] == SIM_REPOSITION) {
441                        logd("No new SIM detected and SIM repositioned");
442                        setUpdatedData(SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM,
443                                nSubCount, nNewSimStatus);
444                        break;
445                    }
446                }
447                if (i == PROJECT_SIM_NUM) {
448                    // all status remain unchanged
449                    logd("[updateSimInfoByIccId] All SIM inserted into the same slot");
450                    setUpdatedData(SubscriptionManager.EXTRA_VALUE_NOCHANGE,
451                            nSubCount, nNewSimStatus);
452                }
453            }
454        } else {
455            logd("New SIM detected");
456            setUpdatedData(SubscriptionManager.EXTRA_VALUE_NEW_SIM, nSubCount, nNewSimStatus);
457        }
458
459        logd("[updateSimInfoByIccId]- SimInfo update complete");
460    }
461
462    private static void setUpdatedData(int detectedType, int subCount, int newSimStatus) {
463
464        Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
465
466        logd("[setUpdatedData]+ ");
467
468        if (detectedType == SubscriptionManager.EXTRA_VALUE_NEW_SIM ) {
469            intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS,
470                    SubscriptionManager.EXTRA_VALUE_NEW_SIM);
471            intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount);
472            intent.putExtra(SubscriptionManager.INTENT_KEY_NEW_SIM_SLOT, newSimStatus);
473        } else if (detectedType == SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM) {
474            intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS,
475                    SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM);
476            intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount);
477        } else if (detectedType == SubscriptionManager.EXTRA_VALUE_REMOVE_SIM) {
478            intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS,
479                    SubscriptionManager.EXTRA_VALUE_REMOVE_SIM);
480            intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount);
481        } else if (detectedType == SubscriptionManager.EXTRA_VALUE_NOCHANGE) {
482            intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS,
483                    SubscriptionManager.EXTRA_VALUE_NOCHANGE);
484        }
485
486        logd("broadcast intent ACTION_SUBINFO_RECORD_UPDATED : [" + detectedType + ", "
487                + subCount + ", " + newSimStatus+ "]");
488        ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL);
489        logd("[setUpdatedData]- ");
490    }
491
492    private static boolean isNewSim(String iccId, String[] oldIccId) {
493        boolean newSim = true;
494        for(int i = 0; i < PROJECT_SIM_NUM; i++) {
495            if(iccId.equals(oldIccId[i])) {
496                newSim = false;
497                break;
498            }
499        }
500        logd("newSim = " + newSim);
501
502        return newSim;
503    }
504
505    public void dispose() {
506        logd("[dispose]");
507        sContext.unregisterReceiver(sReceiver);
508    }
509
510    private static void logd(String message) {
511        Rlog.d(LOG_TAG, "[SubInfoRecordUpdater]" + message);
512    }
513}
514
515