1/*
2* Copyright (C) 2014 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8*      http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17package com.android.internal.telephony;
18
19import static android.Manifest.permission.READ_PHONE_STATE;
20
21import android.app.ActivityManagerNative;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.SharedPreferences;
29import android.os.AsyncResult;
30import android.os.Handler;
31import android.os.Message;
32import android.os.SystemProperties;
33import android.os.UserHandle;
34import android.preference.PreferenceManager;
35import android.provider.Settings;
36import android.telephony.Rlog;
37import android.telephony.SubscriptionManager;
38import android.telephony.SubscriptionInfo;
39import android.telephony.TelephonyManager;
40import com.android.internal.telephony.uicc.IccCardProxy;
41import com.android.internal.telephony.uicc.IccConstants;
42import com.android.internal.telephony.uicc.IccFileHandler;
43import com.android.internal.telephony.uicc.IccRecords;
44import com.android.internal.telephony.uicc.IccUtils;
45import android.text.TextUtils;
46
47import java.util.List;
48
49/**
50 *@hide
51 */
52public class SubscriptionInfoUpdater extends Handler {
53    private static final String LOG_TAG = "SubscriptionInfoUpdater";
54    private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount();
55
56    private static final int EVENT_SIM_LOCKED_QUERY_ICCID_DONE = 1;
57    private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 2;
58    private static final int EVENT_SIM_LOADED = 3;
59    private static final int EVENT_SIM_ABSENT = 4;
60    private static final int EVENT_SIM_LOCKED = 5;
61
62    private static final String ICCID_STRING_FOR_NO_SIM = "";
63    /**
64     *  int[] sInsertSimState maintains all slots' SIM inserted status currently,
65     *  it may contain 4 kinds of values:
66     *    SIM_NOT_INSERT : no SIM inserted in slot i now
67     *    SIM_CHANGED    : a valid SIM insert in slot i and is different SIM from last time
68     *                     it will later become SIM_NEW or SIM_REPOSITION during update procedure
69     *    SIM_NOT_CHANGE : a valid SIM insert in slot i and is the same SIM as last time
70     *    SIM_NEW        : a valid SIM insert in slot i and is a new SIM
71     *    SIM_REPOSITION : a valid SIM insert in slot i and is inserted in different slot last time
72     *    positive integer #: index to distinguish SIM cards with the same IccId
73     */
74    public static final int SIM_NOT_CHANGE = 0;
75    public static final int SIM_CHANGED    = -1;
76    public static final int SIM_NEW        = -2;
77    public static final int SIM_REPOSITION = -3;
78    public static final int SIM_NOT_INSERT = -99;
79
80    public static final int STATUS_NO_SIM_INSERTED = 0x00;
81    public static final int STATUS_SIM1_INSERTED = 0x01;
82    public static final int STATUS_SIM2_INSERTED = 0x02;
83    public static final int STATUS_SIM3_INSERTED = 0x04;
84    public static final int STATUS_SIM4_INSERTED = 0x08;
85
86    // Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED.
87    public static final String CURR_SUBID = "curr_subid";
88
89    private static Phone[] mPhone;
90    private static Context mContext = null;
91    private static String mIccId[] = new String[PROJECT_SIM_NUM];
92    private static int[] mInsertSimState = new int[PROJECT_SIM_NUM];
93    private SubscriptionManager mSubscriptionManager = null;
94
95    public SubscriptionInfoUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) {
96        logd("Constructor invoked");
97
98        mContext = context;
99        mPhone = phoneProxy;
100        mSubscriptionManager = SubscriptionManager.from(mContext);
101
102        IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
103        mContext.registerReceiver(sReceiver, intentFilter);
104        intentFilter = new IntentFilter(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
105        mContext.registerReceiver(sReceiver, intentFilter);
106    }
107
108    private final BroadcastReceiver sReceiver = new  BroadcastReceiver() {
109        @Override
110        public void onReceive(Context context, Intent intent) {
111            logd("[Receiver]+");
112            String action = intent.getAction();
113            logd("Action: " + action);
114
115            if (!action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED) &&
116                !action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
117                return;
118            }
119
120            int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
121                    SubscriptionManager.INVALID_SIM_SLOT_INDEX);
122            logd("slotId: " + slotId);
123            if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
124                return;
125            }
126
127            String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
128            logd("simStatus: " + simStatus);
129
130            if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
131                if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
132                    sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotId, -1));
133                } else {
134                    logd("Ignoring simStatus: " + simStatus);
135                }
136            } else if (action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
137                if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
138                    String reason = intent.getStringExtra(
139                        IccCardConstants.INTENT_KEY_LOCKED_REASON);
140                    sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotId, -1, reason));
141                } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
142                    sendMessage(obtainMessage(EVENT_SIM_LOADED, slotId, -1));
143                } else {
144                    logd("Ignoring simStatus: " + simStatus);
145                }
146            }
147            logd("[Receiver]-");
148        }
149    };
150
151    private boolean isAllIccIdQueryDone() {
152        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
153            if (mIccId[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 void setDisplayNameForNewSub(String newSubName, int subId, int newNameSource) {
164        SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
165        if (subInfo != null) {
166            // overwrite SIM display name if it is not assigned by user
167            int oldNameSource = subInfo.getNameSource();
168            CharSequence oldSubName = subInfo.getDisplayName();
169            logd("[setDisplayNameForNewSub] subId = " + subInfo.getSubscriptionId()
170                    + ", oldSimName = " + oldSubName + ", oldNameSource = " + oldNameSource
171                    + ", newSubName = " + newSubName + ", newNameSource = " + newNameSource);
172            if (oldSubName == null ||
173                (oldNameSource ==
174                    SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE && newSubName != null) ||
175                (oldNameSource == SubscriptionManager.NAME_SOURCE_SIM_SOURCE && newSubName != null
176                        && !newSubName.equals(oldSubName))) {
177                mSubscriptionManager.setDisplayName(newSubName, subInfo.getSubscriptionId(),
178                        newNameSource);
179            }
180        } else {
181            logd("SUB" + (subId + 1) + " SubInfo not created yet");
182        }
183    }
184
185    @Override
186    public void handleMessage(Message msg) {
187        switch (msg.what) {
188            case EVENT_SIM_LOCKED_QUERY_ICCID_DONE: {
189                AsyncResult ar = (AsyncResult)msg.obj;
190                QueryIccIdUserObj uObj = (QueryIccIdUserObj) ar.userObj;
191                int slotId = uObj.slotId;
192                logd("handleMessage : <EVENT_SIM_LOCKED_QUERY_ICCID_DONE> SIM" + (slotId + 1));
193                if (ar.exception == null) {
194                    if (ar.result != null) {
195                        byte[] data = (byte[])ar.result;
196                        mIccId[slotId] = IccUtils.bcdToString(data, 0, data.length);
197                    } else {
198                        logd("Null ar");
199                        mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
200                    }
201                } else {
202                    mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
203                    logd("Query IccId fail: " + ar.exception);
204                }
205                logd("sIccId[" + slotId + "] = " + mIccId[slotId]);
206                if (isAllIccIdQueryDone()) {
207                    updateSubscriptionInfoByIccId();
208                }
209                broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED,
210                                         uObj.reason);
211                break;
212            }
213
214            case EVENT_GET_NETWORK_SELECTION_MODE_DONE: {
215                AsyncResult ar = (AsyncResult)msg.obj;
216                Integer slotId = (Integer)ar.userObj;
217                if (ar.exception == null && ar.result != null) {
218                    int[] modes = (int[])ar.result;
219                    if (modes[0] == 1) {  // Manual mode.
220                        mPhone[slotId].setNetworkSelectionModeAutomatic(null);
221                    }
222                } else {
223                    logd("EVENT_GET_NETWORK_SELECTION_MODE_DONE: error getting network mode.");
224                }
225                break;
226            }
227
228           case EVENT_SIM_LOADED:
229                handleSimLoaded(msg.arg1);
230                break;
231
232            case EVENT_SIM_ABSENT:
233                handleSimAbsent(msg.arg1);
234                break;
235
236            case EVENT_SIM_LOCKED:
237                handleSimLocked(msg.arg1, (String) msg.obj);
238                break;
239
240            default:
241                logd("Unknown msg:" + msg.what);
242        }
243    }
244
245    private static class QueryIccIdUserObj {
246        public String reason;
247        public int slotId;
248
249        QueryIccIdUserObj(String reason, int slotId) {
250            this.reason = reason;
251            this.slotId = slotId;
252        }
253    };
254
255    private void handleSimLocked(int slotId, String reason) {
256        if (mIccId[slotId] != null && mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
257            logd("SIM" + (slotId + 1) + " hot plug in");
258            mIccId[slotId] = null;
259        }
260
261
262        IccFileHandler fileHandler = mPhone[slotId].getIccCard() == null ? null :
263                mPhone[slotId].getIccCard().getIccFileHandler();
264
265        if (fileHandler != null) {
266            String iccId = mIccId[slotId];
267            if (iccId == null) {
268                logd("Querying IccId");
269                fileHandler.loadEFTransparent(IccConstants.EF_ICCID,
270                        obtainMessage(EVENT_SIM_LOCKED_QUERY_ICCID_DONE,
271                                new QueryIccIdUserObj(reason, slotId)));
272            } else {
273                logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
274            }
275        } else {
276            logd("sFh[" + slotId + "] is null, ignore");
277        }
278    }
279
280    private void handleSimLoaded(int slotId) {
281        logd("handleSimStateLoadedInternal: slotId: " + slotId);
282
283        // The SIM should be loaded at this state, but it is possible in cases such as SIM being
284        // removed or a refresh RESET that the IccRecords could be null. The right behavior is to
285        // not broadcast the SIM loaded.
286        IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
287        if (records == null) {  // Possibly a race condition.
288            logd("onRecieve: IccRecords null");
289            return;
290        }
291        if (records.getIccId() == null) {
292            logd("onRecieve: IccID null");
293            return;
294        }
295        mIccId[slotId] = records.getIccId();
296
297        if (isAllIccIdQueryDone()) {
298            updateSubscriptionInfoByIccId();
299        }
300
301        int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
302        int[] subIds = SubscriptionController.getInstance().getSubId(slotId);
303        if (subIds != null) {   // Why an array?
304            subId = subIds[0];
305        }
306
307        if (SubscriptionManager.isValidSubscriptionId(subId)) {
308            String operator = records.getOperatorNumeric();
309            if (operator != null) {
310                if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
311                    MccTable.updateMccMncConfiguration(mContext, operator, false);
312                }
313                SubscriptionController.getInstance().setMccMnc(operator,subId);
314            } else {
315                logd("EVENT_RECORDS_LOADED Operator name is null");
316            }
317            TelephonyManager tm = TelephonyManager.getDefault();
318            String msisdn = tm.getLine1NumberForSubscriber(subId);
319            ContentResolver contentResolver = mContext.getContentResolver();
320
321            if (msisdn != null) {
322                ContentValues number = new ContentValues(1);
323                number.put(SubscriptionManager.NUMBER, msisdn);
324                contentResolver.update(SubscriptionManager.CONTENT_URI, number,
325                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
326                        + Long.toString(subId), null);
327            }
328
329            SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
330            String nameToSet;
331            String CarrierName = tm.getSimOperatorNumericForSubscription(subId);
332            logd("CarrierName = " + CarrierName);
333            String simCarrierName = tm.getSimOperatorNameForSubscription(subId);
334            ContentValues name = new ContentValues(1);
335
336            if (subInfo != null && subInfo.getNameSource() !=
337                    SubscriptionManager.NAME_SOURCE_USER_INPUT) {
338                if (!TextUtils.isEmpty(simCarrierName)) {
339                    nameToSet = simCarrierName;
340                } else {
341                    nameToSet = "CARD " + Integer.toString(slotId + 1);
342                }
343                name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
344                logd("sim name = " + nameToSet);
345            }
346            name.put(SubscriptionManager.CARRIER_NAME,
347                    !TextUtils.isEmpty(simCarrierName) ? simCarrierName :
348                    mContext.getString(com.android.internal.R.string.unknownName));
349            contentResolver.update(SubscriptionManager.CONTENT_URI, name,
350                    SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
351                    + "=" + Long.toString(subId), null);
352            logd("carrier name = " + simCarrierName);
353
354            /* Update preferred network type and network selection mode on SIM change.
355             * Storing last subId in SharedPreference for now to detect SIM change. */
356            SharedPreferences sp =
357                    PreferenceManager.getDefaultSharedPreferences(mContext);
358            int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
359
360            if (storedSubId != subId) {
361                int networkType = RILConstants.PREFERRED_NETWORK_MODE;
362
363                // Set the modem network mode
364                mPhone[slotId].setPreferredNetworkType(networkType, null);
365                Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
366                        Settings.Global.PREFERRED_NETWORK_MODE + subId,
367                        networkType);
368
369                // Only support automatic selection mode on SIM change.
370                mPhone[slotId].getNetworkSelectionMode(
371                        obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, new Integer(slotId)));
372
373                // Update stored subId
374                SharedPreferences.Editor editor = sp.edit();
375                editor.putInt(CURR_SUBID + slotId, subId);
376                editor.apply();
377            }
378        } else {
379            logd("Invalid subId, could not update ContentResolver");
380        }
381
382        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
383    }
384
385    private void handleSimAbsent(int slotId) {
386        if (mIccId[slotId] != null && !mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
387            logd("SIM" + (slotId + 1) + " hot plug out");
388        }
389        mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
390        if (isAllIccIdQueryDone()) {
391            updateSubscriptionInfoByIccId();
392        }
393    }
394
395    /**
396     * TODO: Simplify more, as no one is interested in what happened
397     * only what the current list contains.
398     */
399    synchronized private void updateSubscriptionInfoByIccId() {
400        logd("updateSubscriptionInfoByIccId:+ Start");
401
402        mSubscriptionManager.clearSubscriptionInfo();
403
404        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
405            mInsertSimState[i] = SIM_NOT_CHANGE;
406        }
407
408        int insertedSimCount = PROJECT_SIM_NUM;
409        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
410            if (ICCID_STRING_FOR_NO_SIM.equals(mIccId[i])) {
411                insertedSimCount--;
412                mInsertSimState[i] = SIM_NOT_INSERT;
413            }
414        }
415        logd("insertedSimCount = " + insertedSimCount);
416
417        int index = 0;
418        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
419            if (mInsertSimState[i] == SIM_NOT_INSERT) {
420                continue;
421            }
422            index = 2;
423            for (int j = i + 1; j < PROJECT_SIM_NUM; j++) {
424                if (mInsertSimState[j] == SIM_NOT_CHANGE && mIccId[i].equals(mIccId[j])) {
425                    mInsertSimState[i] = 1;
426                    mInsertSimState[j] = index;
427                    index++;
428                }
429            }
430        }
431
432        ContentResolver contentResolver = mContext.getContentResolver();
433        String[] oldIccId = new String[PROJECT_SIM_NUM];
434        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
435            oldIccId[i] = null;
436            List<SubscriptionInfo> oldSubInfo =
437                    SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false);
438            if (oldSubInfo != null) {
439                oldIccId[i] = oldSubInfo.get(0).getIccId();
440                logd("updateSubscriptionInfoByIccId: oldSubId = "
441                        + oldSubInfo.get(0).getSubscriptionId());
442                if (mInsertSimState[i] == SIM_NOT_CHANGE && !mIccId[i].equals(oldIccId[i])) {
443                    mInsertSimState[i] = SIM_CHANGED;
444                }
445                if (mInsertSimState[i] != SIM_NOT_CHANGE) {
446                    ContentValues value = new ContentValues(1);
447                    value.put(SubscriptionManager.SIM_SLOT_INDEX,
448                            SubscriptionManager.INVALID_SIM_SLOT_INDEX);
449                    contentResolver.update(SubscriptionManager.CONTENT_URI, value,
450                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
451                            + Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null);
452                }
453            } else {
454                if (mInsertSimState[i] == SIM_NOT_CHANGE) {
455                    // no SIM inserted last time, but there is one SIM inserted now
456                    mInsertSimState[i] = SIM_CHANGED;
457                }
458                oldIccId[i] = ICCID_STRING_FOR_NO_SIM;
459                logd("updateSubscriptionInfoByIccId: No SIM in slot " + i + " last time");
460            }
461        }
462
463        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
464            logd("updateSubscriptionInfoByIccId: oldIccId[" + i + "] = " + oldIccId[i] +
465                    ", sIccId[" + i + "] = " + mIccId[i]);
466        }
467
468        //check if the inserted SIM is new SIM
469        int nNewCardCount = 0;
470        int nNewSimStatus = 0;
471        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
472            if (mInsertSimState[i] == SIM_NOT_INSERT) {
473                logd("updateSubscriptionInfoByIccId: No SIM inserted in slot " + i + " this time");
474            } else {
475                if (mInsertSimState[i] > 0) {
476                    //some special SIMs may have the same IccIds, add suffix to distinguish them
477                    //FIXME: addSubInfoRecord can return an error.
478                    mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i]
479                            + Integer.toString(mInsertSimState[i]), i);
480                    logd("SUB" + (i + 1) + " has invalid IccId");
481                } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
482                    mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);
483                }
484                if (isNewSim(mIccId[i], oldIccId)) {
485                    nNewCardCount++;
486                    switch (i) {
487                        case PhoneConstants.SUB1:
488                            nNewSimStatus |= STATUS_SIM1_INSERTED;
489                            break;
490                        case PhoneConstants.SUB2:
491                            nNewSimStatus |= STATUS_SIM2_INSERTED;
492                            break;
493                        case PhoneConstants.SUB3:
494                            nNewSimStatus |= STATUS_SIM3_INSERTED;
495                            break;
496                        //case PhoneConstants.SUB3:
497                        //    nNewSimStatus |= STATUS_SIM4_INSERTED;
498                        //    break;
499                    }
500
501                    mInsertSimState[i] = SIM_NEW;
502                }
503            }
504        }
505
506        for (int i = 0; i < PROJECT_SIM_NUM; i++) {
507            if (mInsertSimState[i] == SIM_CHANGED) {
508                mInsertSimState[i] = SIM_REPOSITION;
509            }
510            logd("updateSubscriptionInfoByIccId: sInsertSimState[" + i + "] = "
511                    + mInsertSimState[i]);
512        }
513
514        List<SubscriptionInfo> subInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
515        int nSubCount = (subInfos == null) ? 0 : subInfos.size();
516        logd("updateSubscriptionInfoByIccId: nSubCount = " + nSubCount);
517        for (int i=0; i < nSubCount; i++) {
518            SubscriptionInfo temp = subInfos.get(i);
519
520            String msisdn = TelephonyManager.getDefault().getLine1NumberForSubscriber(
521                    temp.getSubscriptionId());
522
523            if (msisdn != null) {
524                ContentValues value = new ContentValues(1);
525                value.put(SubscriptionManager.NUMBER, msisdn);
526                contentResolver.update(SubscriptionManager.CONTENT_URI, value,
527                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
528                        + Integer.toString(temp.getSubscriptionId()), null);
529            }
530        }
531
532        SubscriptionController.getInstance().notifySubscriptionInfoChanged();
533        logd("updateSubscriptionInfoByIccId:- SsubscriptionInfo update complete");
534    }
535
536    private boolean isNewSim(String iccId, String[] oldIccId) {
537        boolean newSim = true;
538        for(int i = 0; i < PROJECT_SIM_NUM; i++) {
539            if(iccId.equals(oldIccId[i])) {
540                newSim = false;
541                break;
542            }
543        }
544        logd("newSim = " + newSim);
545
546        return newSim;
547    }
548
549    private void broadcastSimStateChanged(int slotId, String state, String reason) {
550        Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
551        // TODO - we'd like this intent to have a single snapshot of all sim state,
552        // but until then this should not use REPLACE_PENDING or we may lose
553        // information
554        // i.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
555        //         | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
556        i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
557        i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
558        i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
559        i.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
560        SubscriptionManager.putPhoneIdAndSubIdExtra(i, slotId);
561        logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " +
562             IccCardConstants.INTENT_VALUE_ICC_LOADED + " reason " + null +
563             " for mCardIndex : " + slotId);
564        ActivityManagerNative.broadcastStickyIntent(i, READ_PHONE_STATE,
565                UserHandle.USER_ALL);
566    }
567
568    public void dispose() {
569        logd("[dispose]");
570        mContext.unregisterReceiver(sReceiver);
571    }
572
573    private void logd(String message) {
574        Rlog.d(LOG_TAG, message);
575    }
576}
577