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