PhoneFactory.java revision 888c60a7fe1c8f3612ab8e8ec5f2f7ca8f24e766
1/*
2 * Copyright (C) 2006 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 android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.net.LocalServerSocket;
23import android.os.Looper;
24import android.os.SystemProperties;
25import android.os.UserHandle;
26import android.provider.Settings;
27import android.provider.Settings.SettingNotFoundException;
28import android.telephony.Rlog;
29import android.telephony.SubscriptionManager;
30import android.telephony.TelephonyManager;
31
32import com.android.internal.telephony.cdma.CDMALTEPhone;
33import com.android.internal.telephony.cdma.CDMAPhone;
34import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
35import com.android.internal.telephony.gsm.GSMPhone;
36import com.android.internal.telephony.imsphone.ImsPhone;
37import com.android.internal.telephony.sip.SipPhone;
38import com.android.internal.telephony.sip.SipPhoneFactory;
39import com.android.internal.telephony.uicc.UiccController;
40import com.android.internal.telephony.imsphone.ImsPhoneFactory;
41
42import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DEFAULT_SUBSCRIPTION;
43
44/**
45 * {@hide}
46 */
47public class PhoneFactory {
48    static final String LOG_TAG = "PhoneFactory";
49    static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
50    static final int SOCKET_OPEN_MAX_RETRY = 3;
51
52    //***** Class Variables
53
54    static private Phone[] sProxyPhones = null;
55    static private CommandsInterface[] sCommandsInterfaces = null;
56
57    static private ProxyController mProxyController;
58    static private UiccController mUiccController;
59
60    static private Phone sProxyPhone = null;
61    static private CommandsInterface sCommandsInterface = null;
62    static private SubInfoRecordUpdater sSubInfoRecordUpdater = null;
63
64    static private boolean sMadeDefaults = false;
65    static private PhoneNotifier sPhoneNotifier;
66    static private Looper sLooper;
67    static private Context sContext;
68
69    //***** Class Methods
70
71    public static void makeDefaultPhones(Context context) {
72        makeDefaultPhone(context);
73    }
74
75    /**
76     * FIXME replace this with some other way of making these
77     * instances
78     */
79    public static void makeDefaultPhone(Context context) {
80        synchronized(Phone.class) {
81            if (!sMadeDefaults) {
82                sLooper = Looper.myLooper();
83                sContext = context;
84
85                if (sLooper == null) {
86                    throw new RuntimeException(
87                        "PhoneFactory.makeDefaultPhone must be called from Looper thread");
88                }
89
90                // create the telephony device controller.
91                TelephonyDevController.create();
92
93                int retryCount = 0;
94                for(;;) {
95                    boolean hasException = false;
96                    retryCount ++;
97
98                    try {
99                        // use UNIX domain socket to
100                        // prevent subsequent initialization
101                        new LocalServerSocket("com.android.internal.telephony");
102                    } catch (java.io.IOException ex) {
103                        hasException = true;
104                    }
105
106                    if ( !hasException ) {
107                        break;
108                    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
109                        throw new RuntimeException("PhoneFactory probably already running");
110                    } else {
111                        try {
112                            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
113                        } catch (InterruptedException er) {
114                        }
115                    }
116                }
117
118                sPhoneNotifier = new DefaultPhoneNotifier();
119
120                // Get preferred network mode
121                int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
122                if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
123                    preferredNetworkMode = Phone.NT_MODE_GLOBAL;
124                }
125
126                int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
127                Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
128
129                /* In case of multi SIM mode two instances of PhoneProxy, RIL are created,
130                   where as in single SIM mode only instance. isMultiSimEnabled() function checks
131                   whether it is single SIM or multi SIM mode */
132                int numPhones = TelephonyManager.getDefault().getPhoneCount();
133                int[] networkModes = new int[numPhones];
134                sProxyPhones = new PhoneProxy[numPhones];
135                sCommandsInterfaces = new RIL[numPhones];
136
137                for (int i = 0; i < numPhones; i++) {
138                    //reads the system properties and makes commandsinterface
139                    try {
140//                        // Get preferred network type.
141//                        TODO: Sishir added this code to but we need a new technique for MSim
142//                        int networkType = calculatePreferredNetworkType(context);
143//                        Rlog.i(LOG_TAG, "Network Type set to " + Integer.toString(networkType));
144
145                        networkModes[i]  = TelephonyManager.getIntAtIndex(
146                                context.getContentResolver(),
147                                Settings.Global.PREFERRED_NETWORK_MODE, i);
148                    } catch (SettingNotFoundException snfe) {
149                        Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for"+
150                                " Settings.Global.PREFERRED_NETWORK_MODE");
151                        networkModes[i] = preferredNetworkMode;
152                    }
153
154                    Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
155                    sCommandsInterfaces[i] = new RIL(context, networkModes[i],
156                            cdmaSubscription, i);
157                }
158                Rlog.i(LOG_TAG, "Creating SubscriptionController");
159                SubscriptionController.init(context, sCommandsInterfaces);
160
161                // Instantiate UiccController so that all other classes can just
162                // call getInstance()
163                mUiccController = UiccController.make(context, sCommandsInterfaces);
164
165                for (int i = 0; i < numPhones; i++) {
166                    PhoneBase phone = null;
167                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
168                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
169                        phone = new GSMPhone(context,
170                                sCommandsInterfaces[i], sPhoneNotifier, i);
171                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
172                        phone = new CDMALTEPhone(context,
173                                sCommandsInterfaces[i], sPhoneNotifier, i);
174                    }
175                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
176
177                    sProxyPhones[i] = new PhoneProxy(phone);
178                }
179                mProxyController = ProxyController.getInstance(context, sProxyPhones,
180                        mUiccController, sCommandsInterfaces);
181
182                // Set the default phone in base class
183                sProxyPhone = sProxyPhones[PhoneConstants.DEFAULT_SUBSCRIPTION];
184                sCommandsInterface = sCommandsInterfaces[PhoneConstants.DEFAULT_SUBSCRIPTION];
185
186                // Ensure that we have a default SMS app. Requesting the app with
187                // updateIfNeeded set to true is enough to configure a default SMS app.
188                ComponentName componentName =
189                        SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */);
190                String packageName = "NONE";
191                if (componentName != null) {
192                    packageName = componentName.getPackageName();
193                }
194                Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);
195
196                // Set up monitor to watch for changes to SMS packages
197                SmsApplication.initSmsPackageMonitor(context);
198
199                sMadeDefaults = true;
200
201                Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
202                sSubInfoRecordUpdater = new SubInfoRecordUpdater(context,
203                        sProxyPhones, sCommandsInterfaces);
204            }
205        }
206    }
207
208    public static Phone getCdmaPhone(int phoneId) {
209        Phone phone;
210        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
211            phone = new CDMALTEPhone(sContext, sCommandsInterfaces[phoneId],
212                    sPhoneNotifier, phoneId);
213        }
214        return phone;
215    }
216
217    public static Phone getGsmPhone(int phoneId) {
218        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
219            Phone phone = new GSMPhone(sContext, sCommandsInterfaces[phoneId],
220                    sPhoneNotifier, phoneId);
221            return phone;
222        }
223    }
224
225    public static Phone getDefaultPhone() {
226        if (sLooper != Looper.myLooper()) {
227            throw new RuntimeException(
228                "PhoneFactory.getDefaultPhone must be called from Looper thread");
229        }
230
231        if (!sMadeDefaults) {
232            throw new IllegalStateException("Default phones haven't been made yet!");
233        }
234       return sProxyPhone;
235    }
236
237    public static Phone getPhone(int phoneId) {
238        if (sLooper != Looper.myLooper()) {
239            throw new RuntimeException(
240                "PhoneFactory.getPhone must be called from Looper thread");
241        }
242        if (!sMadeDefaults) {
243            throw new IllegalStateException("Default phones haven't been made yet!");
244        // CAF_MSIM FIXME need to introduce default phone id ?
245        } else if (phoneId == PhoneConstants.DEFAULT_SUBSCRIPTION) {
246            return sProxyPhone;
247        }
248        return sProxyPhones[phoneId];
249    }
250
251    public static Phone [] getPhones() {
252        if (sLooper != Looper.myLooper()) {
253            throw new RuntimeException(
254                "PhoneFactory.getPhone must be called from Looper thread");
255        }
256        if (!sMadeDefaults) {
257            throw new IllegalStateException("Default phones haven't been made yet!");
258        }
259        return sProxyPhones;
260    }
261
262    public static Phone getCdmaPhone() {
263        Phone phone;
264        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
265            switch (TelephonyManager.getLteOnCdmaModeStatic()) {
266                case PhoneConstants.LTE_ON_CDMA_TRUE: {
267                    phone = new CDMALTEPhone(sContext, sCommandsInterface, sPhoneNotifier);
268                    break;
269                }
270                case PhoneConstants.LTE_ON_CDMA_FALSE:
271                case PhoneConstants.LTE_ON_CDMA_UNKNOWN:
272                default: {
273                    phone = new CDMAPhone(sContext, sCommandsInterface, sPhoneNotifier);
274                    break;
275                }
276            }
277        }
278        return phone;
279    }
280
281    public static Phone getGsmPhone() {
282        int phoneId = SubscriptionController.getInstance().getPhoneId(getDefaultSubscription());
283        if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
284            phoneId = 0;
285        }
286        return getGsmPhone(phoneId);
287    }
288
289    /**
290     * Makes a {@link SipPhone} object.
291     * @param sipUri the local SIP URI the phone runs on
292     * @return the {@code SipPhone} object or null if the SIP URI is not valid
293     */
294    public static SipPhone makeSipPhone(String sipUri) {
295        return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
296    }
297
298    /* Sets the default subscription. If only one phone instance is active that
299     * subscription is set as default subscription. If both phone instances
300     * are active the first instance "0" is set as default subscription
301     */
302    public static void setDefaultSubscription(int subId) {
303        SystemProperties.set(PROPERTY_DEFAULT_SUBSCRIPTION, Integer.toString(subId));
304        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
305
306        // Set the default phone in base class
307        if (phoneId >= 0 && phoneId < sProxyPhones.length) {
308            sProxyPhone = sProxyPhones[phoneId];
309            sCommandsInterface = sCommandsInterfaces[phoneId];
310            sMadeDefaults = true;
311        }
312
313        // Update MCC MNC device configuration information
314        String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId);
315        Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
316        MccTable.updateMccMncConfiguration(sContext, defaultMccMnc, false);
317
318        // Broadcast an Intent for default sub change
319        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
320        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
321        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
322        Rlog.d(LOG_TAG, "setDefaultSubscription : " + subId
323                + " Broadcasting Default Subscription Changed...");
324        sContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
325    }
326
327    /**
328     * Returns the preferred network type that should be set in the modem.
329     *
330     * @param context The current {@link Context}.
331     * @return the preferred network mode that should be set.
332     */
333    // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController ..
334    public static int calculatePreferredNetworkType(Context context) {
335        int preferredNetworkType = RILConstants.PREFERRED_NETWORK_MODE;
336        if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
337            preferredNetworkType = Phone.NT_MODE_GLOBAL;
338        }
339        int networkType = Settings.Global.getInt(context.getContentResolver(),
340                Settings.Global.PREFERRED_NETWORK_MODE, preferredNetworkType);
341        return networkType;
342    }
343
344    /* Gets the default subscription */
345    public static long getDefaultSubscription() {
346        return SubscriptionController.getInstance().getDefaultSubId();
347    }
348
349    /* Gets User preferred Voice subscription setting*/
350    public static int getVoiceSubscription() {
351        int subId = 0;
352
353        try {
354            subId = Settings.Global.getInt(sContext.getContentResolver(),
355                    Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
356        } catch (SettingNotFoundException snfe) {
357            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim Voice Call Values");
358        }
359
360        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
361        // Set subscription to 0 if current subscription is invalid.
362        // Ex: multisim.config property is TSTS and subscription is 2.
363        // If user is trying to set multisim.config to DSDS and reboots
364        // in this case index 2 is invalid so need to set to 0.
365        if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
366            Rlog.i(LOG_TAG, "Subscription is invalid..." + subId + " Set to 0");
367            subId = 0;
368            setVoiceSubscription(subId);
369        }
370
371        return subId;
372    }
373
374    /* Returns User Prompt property,  enabed or not */
375    public static boolean isPromptEnabled() {
376        boolean prompt = false;
377        int value = 0;
378        try {
379            value = Settings.Global.getInt(sContext.getContentResolver(),
380                    Settings.Global.MULTI_SIM_VOICE_PROMPT);
381        } catch (SettingNotFoundException snfe) {
382            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim Voice Prompt Values");
383        }
384        prompt = (value == 0) ? false : true ;
385        Rlog.d(LOG_TAG, "Prompt option:" + prompt);
386
387       return prompt;
388    }
389
390    /*Sets User Prompt property,  enabed or not */
391    public static void setPromptEnabled(boolean enabled) {
392        int value = (enabled == false) ? 0 : 1;
393        Settings.Global.putInt(sContext.getContentResolver(),
394                Settings.Global.MULTI_SIM_VOICE_PROMPT, value);
395        Rlog.d(LOG_TAG, "setVoicePromptOption to " + enabled);
396    }
397
398    /* Returns User SMS Prompt property,  enabled or not */
399    public static boolean isSMSPromptEnabled() {
400        boolean prompt = false;
401        int value = 0;
402        try {
403            value = Settings.Global.getInt(sContext.getContentResolver(),
404                    Settings.Global.MULTI_SIM_SMS_PROMPT);
405        } catch (SettingNotFoundException snfe) {
406            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Prompt Values");
407        }
408        prompt = (value == 0) ? false : true ;
409        Rlog.d(LOG_TAG, "SMS Prompt option:" + prompt);
410
411       return prompt;
412    }
413
414    /*Sets User SMS Prompt property,  enable or not */
415    public static void setSMSPromptEnabled(boolean enabled) {
416        int value = (enabled == false) ? 0 : 1;
417        Settings.Global.putInt(sContext.getContentResolver(),
418                Settings.Global.MULTI_SIM_SMS_PROMPT, value);
419        Rlog.d(LOG_TAG, "setSMSPromptOption to " + enabled);
420    }
421
422    /* Gets User preferred Data subscription setting*/
423    public static long getDataSubscription() {
424        long subId = 1;
425
426        try {
427            subId = Settings.Global.getLong(sContext.getContentResolver(),
428                    Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION);
429        } catch (SettingNotFoundException snfe) {
430            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim Data Call Values");
431        }
432
433        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
434        if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
435            subId = 1;
436            Rlog.i(LOG_TAG, "Subscription is invalid..." + subId + " Set to 0");
437            setDataSubscription(subId);
438        }
439
440        return subId;
441    }
442
443    /* Gets User preferred SMS subscription setting*/
444    public static int getSMSSubscription() {
445        int subId = 0;
446        try {
447            subId = Settings.Global.getInt(sContext.getContentResolver(),
448                    Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION);
449        } catch (SettingNotFoundException snfe) {
450            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Values");
451        }
452
453        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
454        if (phoneId < 0 || phoneId >= TelephonyManager.getDefault().getPhoneCount()) {
455            Rlog.i(LOG_TAG, "Subscription is invalid..." + subId + " Set to 0");
456            subId = 0;
457            setSMSSubscription(subId);
458        }
459
460        return subId;
461    }
462
463    static public void setVoiceSubscription(int subId) {
464        Settings.Global.putInt(sContext.getContentResolver(),
465                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
466        Rlog.d(LOG_TAG, "setVoiceSubscription : " + subId);
467    }
468
469    static public void setDataSubscription(long subId) {
470        boolean enabled;
471
472        Settings.Global.putLong(sContext.getContentResolver(),
473                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
474        Rlog.d(LOG_TAG, "setDataSubscription: " + subId);
475
476        // Update the current mobile data flag
477        enabled = Settings.Global.getInt(sContext.getContentResolver(),
478                Settings.Global.MOBILE_DATA + subId, 0) != 0;
479        Settings.Global.putInt(sContext.getContentResolver(),
480                Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
481        Rlog.d(LOG_TAG, "set mobile_data: " + enabled);
482
483        // Update the current data roaming flag
484        enabled = Settings.Global.getInt(sContext.getContentResolver(),
485                Settings.Global.DATA_ROAMING + subId, 0) != 0;
486        Settings.Global.putInt(sContext.getContentResolver(),
487                Settings.Global.DATA_ROAMING, enabled ? 1 : 0);
488        Rlog.d(LOG_TAG, "set data_roaming: " + enabled);
489    }
490
491    static public void setSMSSubscription(int subId) {
492        Settings.Global.putInt(sContext.getContentResolver(),
493                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
494
495        Intent intent = new Intent("com.android.mms.transaction.SEND_MESSAGE");
496        sContext.sendBroadcast(intent);
497
498        // Change occured in SMS preferred sub, update the default
499        // SMS interface Manager object with the new SMS preferred subscription.
500        Rlog.d(LOG_TAG, "setSMSSubscription : " + subId);
501    }
502
503    /**
504     * Makes a {@link ImsPhone} object.
505     * @return the {@code ImsPhone} object or null if the exception occured
506     */
507    public static ImsPhone makeImsPhone(PhoneNotifier phoneNotifier, Phone defaultPhone) {
508        return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
509    }
510}
511