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