1/*
2 * Copyright (C) 2011 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.cdma;
18
19import android.app.ActivityManagerNative;
20import android.content.ContentValues;
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.database.SQLException;
25import android.net.Uri;
26import android.os.AsyncResult;
27import android.os.Handler;
28import android.os.Message;
29import android.os.UserHandle;
30import android.preference.PreferenceManager;
31import android.os.PowerManager;
32import android.os.SystemProperties;
33import android.provider.Telephony;
34import android.text.TextUtils;
35import android.telephony.SubscriptionManager;
36import android.telephony.Rlog;
37
38import com.android.internal.telephony.CommandsInterface;
39
40import android.telephony.TelephonyManager;
41
42import com.android.internal.telephony.dataconnection.DcTracker;
43import com.android.internal.telephony.MccTable;
44import com.android.internal.telephony.OperatorInfo;
45import com.android.internal.telephony.PhoneConstants;
46import com.android.internal.telephony.PhoneNotifier;
47import com.android.internal.telephony.PhoneProxy;
48import com.android.internal.telephony.PhoneFactory;
49import com.android.internal.telephony.PhoneSubInfo;
50import com.android.internal.telephony.SMSDispatcher;
51import com.android.internal.telephony.SmsBroadcastUndelivered;
52import com.android.internal.telephony.SubscriptionController;
53import com.android.internal.telephony.gsm.GsmSMSDispatcher;
54import com.android.internal.telephony.gsm.SmsMessage;
55import com.android.internal.telephony.uicc.IccRecords;
56import com.android.internal.telephony.uicc.IsimRecords;
57import com.android.internal.telephony.uicc.IsimUiccRecords;
58import com.android.internal.telephony.uicc.RuimRecords;
59import com.android.internal.telephony.uicc.SIMRecords;
60import com.android.internal.telephony.uicc.UiccCardApplication;
61import com.android.internal.telephony.uicc.UiccController;
62import com.android.internal.telephony.ServiceStateTracker;
63import com.android.internal.telephony.TelephonyIntents;
64import com.android.internal.telephony.TelephonyProperties;
65
66import java.io.FileDescriptor;
67import java.io.PrintWriter;
68
69
70public class CDMALTEPhone extends CDMAPhone {
71    static final String LOG_LTE_TAG = "CDMALTEPhone";
72    private static final boolean DBG = true;
73
74    /** CdmaLtePhone in addition to RuimRecords available from
75     * PhoneBase needs access to SIMRecords and IsimUiccRecords
76     */
77    private SIMRecords mSimRecords;
78    private IsimUiccRecords mIsimUiccRecords;
79
80    // Constructors
81    public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
82            int phoneId) {
83        this(context, ci, notifier, false, phoneId);
84    }
85
86    public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
87            boolean unitTestMode, int phoneId) {
88        super(context, ci, notifier, phoneId);
89
90        Rlog.d(LOG_TAG, "CDMALTEPhone: constructor: sub = " + mPhoneId);
91
92        mDcTracker = new DcTracker(this);
93
94    }
95
96    @Override
97    protected void initSstIcc() {
98        mSST = new CdmaLteServiceStateTracker(this);
99    }
100
101    @Override
102    public void dispose() {
103        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
104            if (mSimRecords != null) {
105                mSimRecords.unregisterForRecordsLoaded(this);
106            }
107            super.dispose();
108        }
109    }
110
111    @Override
112    public void removeReferences() {
113        super.removeReferences();
114    }
115
116    @Override
117    public void handleMessage(Message msg) {
118        AsyncResult ar;
119        Message onComplete;
120
121        // messages to be handled whether or not the phone is being destroyed
122        // should only include messages which are being re-directed and do not use
123        // resources of the phone being destroyed
124        switch (msg.what) {
125            // handle the select network completion callbacks.
126            case EVENT_SET_NETWORK_MANUAL_COMPLETE:
127            case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
128                super.handleMessage(msg);
129                return;
130        }
131
132        if (!mIsTheCurrentActivePhone) {
133            Rlog.e(LOG_TAG, "Received message " + msg +
134                    "[" + msg.what + "] while being destroyed. Ignoring.");
135            return;
136        }
137        switch(msg.what) {
138            case EVENT_SIM_RECORDS_LOADED:
139                mSimRecordsLoadedRegistrants.notifyRegistrants();
140                break;
141
142            default:
143                super.handleMessage(msg);
144        }
145    }
146    @Override
147    public PhoneConstants.DataState getDataConnectionState(String apnType) {
148        PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED;
149
150        if (mSST == null) {
151            // Radio Technology Change is ongoing, dispose() and
152            // removeReferences() have already been called
153
154            ret = PhoneConstants.DataState.DISCONNECTED;
155        } else if (mDcTracker.isApnTypeEnabled(apnType) == false) {
156            ret = PhoneConstants.DataState.DISCONNECTED;
157        } else {
158            switch (mDcTracker.getState(apnType)) {
159                case RETRYING:
160                case FAILED:
161                case IDLE:
162                    ret = PhoneConstants.DataState.DISCONNECTED;
163                    break;
164
165                case CONNECTED:
166                case DISCONNECTING:
167                    if (mCT.mState != PhoneConstants.State.IDLE &&
168                            !mSST.isConcurrentVoiceAndDataAllowed()) {
169                        ret = PhoneConstants.DataState.SUSPENDED;
170                    } else {
171                        ret = PhoneConstants.DataState.CONNECTED;
172                    }
173                    break;
174
175                case CONNECTING:
176                case SCANNING:
177                    ret = PhoneConstants.DataState.CONNECTING;
178                    break;
179            }
180        }
181
182        log("getDataConnectionState apnType=" + apnType + " ret=" + ret);
183        return ret;
184    }
185
186    /**
187     * Sets the "current" field in the telephony provider according to the
188     * build-time operator numeric property
189     *
190     * @return true for success; false otherwise.
191     */
192    @Override
193    boolean updateCurrentCarrierInProvider(String operatorNumeric) {
194        boolean retVal;
195        if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) == null) {
196            if (DBG) log("updateCurrentCarrierInProvider APP_FAM_3GPP == null");
197            retVal = super.updateCurrentCarrierInProvider(operatorNumeric);
198        } else {
199            if (DBG) log("updateCurrentCarrierInProvider not updated");
200            retVal = true;
201        }
202        if (DBG) log("updateCurrentCarrierInProvider X retVal=" + retVal);
203        return retVal;
204    }
205
206    @Override
207    public boolean updateCurrentCarrierInProvider() {
208        long currentDds = SubscriptionManager.getDefaultDataSubId();
209        String operatorNumeric = getOperatorNumeric();
210
211        Rlog.d(LOG_TAG, "updateCurrentCarrierInProvider: mSubscription = " + getSubId()
212                + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric);
213
214        if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) {
215            try {
216                Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
217                ContentValues map = new ContentValues();
218                map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
219                mContext.getContentResolver().insert(uri, map);
220                return true;
221            } catch (SQLException e) {
222                Rlog.e(LOG_TAG, "Can't store current operator", e);
223            }
224        }
225        return false;
226    }
227
228    // return IMSI from USIM as subscriber ID.
229    @Override
230    public String getSubscriberId() {
231        return (mSimRecords != null) ? mSimRecords.getIMSI() : "";
232    }
233
234    // return GID1 from USIM
235    @Override
236    public String getGroupIdLevel1() {
237        return (mSimRecords != null) ? mSimRecords.getGid1() : "";
238    }
239
240    @Override
241    public String getImei() {
242        return mImei;
243    }
244
245    @Override
246    public String getDeviceSvn() {
247        return mImeiSv;
248    }
249
250    @Override
251    public IsimRecords getIsimRecords() {
252        return mIsimUiccRecords;
253    }
254
255    @Override
256    public String getMsisdn() {
257        return (mSimRecords != null) ? mSimRecords.getMsisdnNumber() : null;
258    }
259
260    @Override
261    public void getAvailableNetworks(Message response) {
262        mCi.getAvailableNetworks(response);
263    }
264
265    @Override
266    protected void onUpdateIccAvailability() {
267        if (mSimRecords != null) {
268            mSimRecords.unregisterForRecordsLoaded(this);
269        }
270
271        if (mUiccController == null ) {
272            return;
273        }
274
275        // Update IsimRecords
276        UiccCardApplication newUiccApplication =
277                mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_IMS);
278        IsimUiccRecords newIsimUiccRecords = null;
279
280        if (newUiccApplication != null) {
281            newIsimUiccRecords = (IsimUiccRecords) newUiccApplication.getIccRecords();
282        }
283        mIsimUiccRecords = newIsimUiccRecords;
284
285        // Update UsimRecords
286        newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId,
287                UiccController.APP_FAM_3GPP);
288        SIMRecords newSimRecords = null;
289        if (newUiccApplication != null) {
290            newSimRecords = (SIMRecords) newUiccApplication.getIccRecords();
291        }
292        mSimRecords = newSimRecords;
293        if (mSimRecords != null) {
294            mSimRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
295        }
296
297        super.onUpdateIccAvailability();
298    }
299
300    @Override
301    protected void init(Context context, PhoneNotifier notifier) {
302        mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
303        mCT = new CdmaCallTracker(this);
304        mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context, mCi, this,
305                EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
306        mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
307        mSubInfo = new PhoneSubInfo(this);
308        mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
309
310        mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
311        mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
312        mCi.registerForOn(this, EVENT_RADIO_ON, null);
313        mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
314        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
315        mCi.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
316        mCi.registerForExitEmergencyCallbackMode(this, EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE,
317                null);
318
319        PowerManager pm
320            = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
321        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,LOG_TAG);
322
323        // This is needed to handle phone process crashes
324        String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
325        mIsPhoneInEcmState = inEcm.equals("true");
326        if (mIsPhoneInEcmState) {
327            // Send a message which will invoke handleExitEmergencyCallbackMode
328            mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
329        }
330
331        // get the string that specifies the carrier OTA Sp number
332        mCarrierOtaSpNumSchema = TelephonyManager.from(mContext).getOtaSpNumberSchemaForPhone(
333                getPhoneId(), "");
334
335        setProperties();
336    }
337
338    // Set the properties per subscription
339    private void setProperties() {
340        TelephonyManager tm = TelephonyManager.from(mContext);
341        //Change the system property
342        tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_CDMA);
343        // Sets operator alpha property by retrieving from build-time system property
344        String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha");
345        if (!TextUtils.isEmpty(operatorAlpha)) {
346            tm.setSimOperatorNameForPhone(getPhoneId(), operatorAlpha);
347        }
348
349        // Sets operator numeric property by retrieving from build-time system property
350        String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC);
351        log("update icc_operator_numeric=" + operatorNumeric);
352        if (!TextUtils.isEmpty(operatorNumeric)) {
353            tm.setSimOperatorNumericForPhone(getPhoneId(), operatorNumeric);
354
355            SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
356            // Sets iso country property by retrieving from build-time system property
357            setIsoCountryProperty(operatorNumeric);
358            // Updates MCC MNC device configuration information
359            log("update mccmnc=" + operatorNumeric);
360            MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
361        }
362        // Sets current entry in the telephony carrier table
363        updateCurrentCarrierInProvider();
364    }
365
366    @Override
367    public void setSystemProperty(String property, String value) {
368        if(getUnitTestMode()) {
369            return;
370        }
371        TelephonyManager.setTelephonyProperty(mPhoneId, property, value);
372    }
373
374    public String getSystemProperty(String property, String defValue) {
375        if(getUnitTestMode()) {
376            return null;
377        }
378        return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue);
379    }
380
381    public void updateDataConnectionTracker() {
382        ((DcTracker)mDcTracker).update();
383    }
384
385    public void setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
386        ((DcTracker)mDcTracker)
387                .setInternalDataEnabled(enable, onCompleteMsg);
388    }
389
390    public boolean setInternalDataEnabledFlag(boolean enable) {
391       return ((DcTracker)mDcTracker)
392                .setInternalDataEnabledFlag(enable);
393    }
394
395    /**
396     * @return operator numeric.
397     */
398    public String getOperatorNumeric() {
399        String operatorNumeric = null;
400        IccRecords curIccRecords = null;
401        if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_NV) {
402            operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric");
403        } else if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_RUIM_SIM) {
404            curIccRecords = mSimRecords;
405            if (curIccRecords != null) {
406                operatorNumeric = curIccRecords.getOperatorNumeric();
407            } else {
408                curIccRecords = mIccRecords.get();
409                if (curIccRecords != null && (curIccRecords instanceof RuimRecords)) {
410                    RuimRecords csim = (RuimRecords) curIccRecords;
411                    operatorNumeric = csim.getRUIMOperatorNumeric();
412                }
413            }
414        }
415        if (operatorNumeric == null) {
416            Rlog.e(LOG_TAG, "getOperatorNumeric: Cannot retrieve operatorNumeric:"
417                    + " mCdmaSubscriptionSource = " + mCdmaSubscriptionSource + " mIccRecords = "
418                    + ((curIccRecords != null) ? curIccRecords.getRecordsLoaded() : null));
419        }
420
421        Rlog.d(LOG_TAG, "getOperatorNumeric: mCdmaSubscriptionSource = " + mCdmaSubscriptionSource
422                + " operatorNumeric = " + operatorNumeric);
423
424        return operatorNumeric;
425    }
426    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
427        ((DcTracker)mDcTracker)
428               .registerForAllDataDisconnected(h, what, obj);
429    }
430
431    public void unregisterForAllDataDisconnected(Handler h) {
432        ((DcTracker)mDcTracker)
433                .unregisterForAllDataDisconnected(h);
434    }
435
436    @Override
437    public void registerForSimRecordsLoaded(Handler h, int what, Object obj) {
438        mSimRecordsLoadedRegistrants.addUnique(h, what, obj);
439    }
440
441    @Override
442    public void unregisterForSimRecordsLoaded(Handler h) {
443        mSimRecordsLoadedRegistrants.remove(h);
444    }
445
446
447    @Override
448    protected void log(String s) {
449            Rlog.d(LOG_LTE_TAG, s);
450    }
451
452    protected void loge(String s) {
453            Rlog.e(LOG_LTE_TAG, s);
454    }
455
456    protected void loge(String s, Throwable e) {
457        Rlog.e(LOG_LTE_TAG, s, e);
458    }
459
460    @Override
461    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
462        pw.println("CDMALTEPhone extends:");
463        super.dump(fd, pw, args);
464    }
465}
466