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