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.HandlerThread;
26import android.os.Looper;
27import android.os.ServiceManager;
28import android.os.SystemProperties;
29import android.os.UserHandle;
30import android.provider.Settings;
31import android.provider.Settings.SettingNotFoundException;
32import android.telephony.Rlog;
33import android.telephony.SubscriptionManager;
34import android.telephony.TelephonyManager;
35import android.util.LocalLog;
36
37import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
38import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory;
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;
45import com.android.internal.util.IndentingPrintWriter;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49import java.util.HashMap;
50
51/**
52 * {@hide}
53 */
54public class PhoneFactory {
55    static final String LOG_TAG = "PhoneFactory";
56    static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
57    static final int SOCKET_OPEN_MAX_RETRY = 3;
58    static final boolean DBG = false;
59
60    //***** Class Variables
61
62    // lock sLockProxyPhones protects both sPhones and sPhone
63    final static Object sLockProxyPhones = new Object();
64    static private Phone[] sPhones = null;
65    static private Phone sPhone = null;
66
67    static private CommandsInterface[] sCommandsInterfaces = null;
68
69    static private ProxyController sProxyController;
70    static private UiccController sUiccController;
71
72    static private CommandsInterface sCommandsInterface = null;
73    static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
74
75    static private boolean sMadeDefaults = false;
76    static private PhoneNotifier sPhoneNotifier;
77    static private Context sContext;
78    static private PhoneSwitcher sPhoneSwitcher;
79    static private SubscriptionMonitor sSubscriptionMonitor;
80    static private TelephonyNetworkFactory[] sTelephonyNetworkFactories;
81
82    static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
83
84    // TODO - make this a dynamic property read from the modem
85    public static final int MAX_ACTIVE_PHONES = 1;
86
87    //***** Class Methods
88
89    public static void makeDefaultPhones(Context context) {
90        makeDefaultPhone(context);
91    }
92
93    /**
94     * FIXME replace this with some other way of making these
95     * instances
96     */
97    public static void makeDefaultPhone(Context context) {
98        synchronized (sLockProxyPhones) {
99            if (!sMadeDefaults) {
100                sContext = context;
101
102                // create the telephony device controller.
103                TelephonyDevController.create();
104
105                int retryCount = 0;
106                for(;;) {
107                    boolean hasException = false;
108                    retryCount ++;
109
110                    try {
111                        // use UNIX domain socket to
112                        // prevent subsequent initialization
113                        new LocalServerSocket("com.android.internal.telephony");
114                    } catch (java.io.IOException ex) {
115                        hasException = true;
116                    }
117
118                    if ( !hasException ) {
119                        break;
120                    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
121                        throw new RuntimeException("PhoneFactory probably already running");
122                    } else {
123                        try {
124                            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
125                        } catch (InterruptedException er) {
126                        }
127                    }
128                }
129
130                sPhoneNotifier = new DefaultPhoneNotifier();
131
132                int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
133                Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
134
135                /* In case of multi SIM mode two instances of Phone, RIL are created,
136                   where as in single SIM mode only instance. isMultiSimEnabled() function checks
137                   whether it is single SIM or multi SIM mode */
138                int numPhones = TelephonyManager.getDefault().getPhoneCount();
139                int[] networkModes = new int[numPhones];
140                sPhones = new Phone[numPhones];
141                sCommandsInterfaces = new RIL[numPhones];
142                sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
143
144                for (int i = 0; i < numPhones; i++) {
145                    // reads the system properties and makes commandsinterface
146                    // Get preferred network type.
147                    networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;
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                sUiccController = UiccController.make(context, sCommandsInterfaces);
159
160                for (int i = 0; i < numPhones; i++) {
161                    Phone phone = null;
162                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
163                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
164                        phone = new GsmCdmaPhone(context,
165                                sCommandsInterfaces[i], sPhoneNotifier, i,
166                                PhoneConstants.PHONE_TYPE_GSM,
167                                TelephonyComponentFactory.getInstance());
168                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
169                        phone = new GsmCdmaPhone(context,
170                                sCommandsInterfaces[i], sPhoneNotifier, i,
171                                PhoneConstants.PHONE_TYPE_CDMA_LTE,
172                                TelephonyComponentFactory.getInstance());
173                    }
174                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
175
176                    sPhones[i] = phone;
177                }
178
179                // Set the default phone in base class.
180                // FIXME: This is a first best guess at what the defaults will be. It
181                // FIXME: needs to be done in a more controlled manner in the future.
182                sPhone = sPhones[0];
183                sCommandsInterface = sCommandsInterfaces[0];
184
185                // Ensure that we have a default SMS app. Requesting the app with
186                // updateIfNeeded set to true is enough to configure a default SMS app.
187                ComponentName componentName =
188                        SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */);
189                String packageName = "NONE";
190                if (componentName != null) {
191                    packageName = componentName.getPackageName();
192                }
193                Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);
194
195                // Set up monitor to watch for changes to SMS packages
196                SmsApplication.initSmsPackageMonitor(context);
197
198                sMadeDefaults = true;
199
200                Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
201                sSubInfoRecordUpdater = new SubscriptionInfoUpdater(context,
202                        sPhones, sCommandsInterfaces);
203                SubscriptionController.getInstance().updatePhonesAvailability(sPhones);
204
205                // Start monitoring after defaults have been made.
206                // Default phone must be ready before ImsPhone is created
207                // because ImsService might need it when it is being opened.
208                for (int i = 0; i < numPhones; i++) {
209                    sPhones[i].startMonitoringImsService();
210                }
211
212                ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(
213                        ServiceManager.getService("telephony.registry"));
214                SubscriptionController sc = SubscriptionController.getInstance();
215
216                sSubscriptionMonitor = new SubscriptionMonitor(tr, sContext, sc, numPhones);
217
218                sPhoneSwitcher = new PhoneSwitcher(MAX_ACTIVE_PHONES, numPhones,
219                        sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces,
220                        sPhones);
221
222                sProxyController = ProxyController.getInstance(context, sPhones,
223                        sUiccController, sCommandsInterfaces, sPhoneSwitcher);
224
225                sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
226                for (int i = 0; i < numPhones; i++) {
227                    sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
228                            sPhoneSwitcher, sc, sSubscriptionMonitor, Looper.myLooper(),
229                            sContext, i, sPhones[i].mDcTracker);
230                }
231            }
232        }
233    }
234
235    public static Phone getDefaultPhone() {
236        synchronized (sLockProxyPhones) {
237            if (!sMadeDefaults) {
238                throw new IllegalStateException("Default phones haven't been made yet!");
239            }
240            return sPhone;
241        }
242    }
243
244    public static Phone getPhone(int phoneId) {
245        Phone phone;
246        String dbgInfo = "";
247
248        synchronized (sLockProxyPhones) {
249            if (!sMadeDefaults) {
250                throw new IllegalStateException("Default phones haven't been made yet!");
251                // CAF_MSIM FIXME need to introduce default phone id ?
252            } else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
253                if (DBG) dbgInfo = "phoneId == DEFAULT_PHONE_ID return sPhone";
254                phone = sPhone;
255            } else {
256                if (DBG) dbgInfo = "phoneId != DEFAULT_PHONE_ID return sPhones[phoneId]";
257                phone = (((phoneId >= 0)
258                                && (phoneId < TelephonyManager.getDefault().getPhoneCount()))
259                        ? sPhones[phoneId] : null);
260            }
261            if (DBG) {
262                Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId +
263                        " phone=" + phone);
264            }
265            return phone;
266        }
267    }
268
269    public static Phone[] getPhones() {
270        synchronized (sLockProxyPhones) {
271            if (!sMadeDefaults) {
272                throw new IllegalStateException("Default phones haven't been made yet!");
273            }
274            return sPhones;
275        }
276    }
277
278    /**
279     * Makes a {@link SipPhone} object.
280     * @param sipUri the local SIP URI the phone runs on
281     * @return the {@code SipPhone} object or null if the SIP URI is not valid
282     */
283    public static SipPhone makeSipPhone(String sipUri) {
284        return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
285    }
286
287    /**
288     * Returns the preferred network type that should be set in the modem.
289     *
290     * @param context The current {@link Context}.
291     * @return the preferred network mode that should be set.
292     */
293    // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController ..
294    public static int calculatePreferredNetworkType(Context context, int phoneSubId) {
295        int networkType = android.provider.Settings.Global.getInt(context.getContentResolver(),
296                android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
297                RILConstants.PREFERRED_NETWORK_MODE);
298        Rlog.d(LOG_TAG, "calculatePreferredNetworkType: phoneSubId = " + phoneSubId +
299                " networkType = " + networkType);
300        return networkType;
301    }
302
303    /* Gets the default subscription */
304    public static int getDefaultSubscription() {
305        return SubscriptionController.getInstance().getDefaultSubId();
306    }
307
308    /* Returns User SMS Prompt property,  enabled or not */
309    public static boolean isSMSPromptEnabled() {
310        boolean prompt = false;
311        int value = 0;
312        try {
313            value = Settings.Global.getInt(sContext.getContentResolver(),
314                    Settings.Global.MULTI_SIM_SMS_PROMPT);
315        } catch (SettingNotFoundException snfe) {
316            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Prompt Values");
317        }
318        prompt = (value == 0) ? false : true ;
319        Rlog.d(LOG_TAG, "SMS Prompt option:" + prompt);
320
321       return prompt;
322    }
323
324    /**
325     * Makes a {@link ImsPhone} object.
326     * @return the {@code ImsPhone} object or null if the exception occured
327     */
328    public static Phone makeImsPhone(PhoneNotifier phoneNotifier, Phone defaultPhone) {
329        return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
330    }
331
332    /**
333     * Adds a local log category.
334     *
335     * Only used within the telephony process.  Use localLog to add log entries.
336     *
337     * TODO - is there a better way to do this?  Think about design when we have a minute.
338     *
339     * @param key the name of the category - will be the header in the service dump.
340     * @param size the number of lines to maintain in this category
341     */
342    public static void addLocalLog(String key, int size) {
343        synchronized(sLocalLogs) {
344            if (sLocalLogs.containsKey(key)) {
345                throw new IllegalArgumentException("key " + key + " already present");
346            }
347            sLocalLogs.put(key, new LocalLog(size));
348        }
349    }
350
351    /**
352     * Add a line to the named Local Log.
353     *
354     * This will appear in the TelephonyDebugService dump.
355     *
356     * @param key the name of the log category to put this in.  Must be created
357     *            via addLocalLog.
358     * @param log the string to add to the log.
359     */
360    public static void localLog(String key, String log) {
361        synchronized(sLocalLogs) {
362            if (sLocalLogs.containsKey(key) == false) {
363                throw new IllegalArgumentException("key " + key + " not found");
364            }
365            sLocalLogs.get(key).log(log);
366        }
367    }
368
369    public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
370        IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, "  ");
371        pw.println("PhoneFactory:");
372        pw.println(" sMadeDefaults=" + sMadeDefaults);
373
374        sPhoneSwitcher.dump(fd, pw, args);
375        pw.println();
376
377        Phone[] phones = (Phone[])PhoneFactory.getPhones();
378        for (int i = 0; i < phones.length; i++) {
379            pw.increaseIndent();
380            Phone phone = phones[i];
381
382            try {
383                phone.dump(fd, pw, args);
384            } catch (Exception e) {
385                pw.println("Telephony DebugService: Could not get Phone[" + i + "] e=" + e);
386                continue;
387            }
388
389            pw.flush();
390            pw.println("++++++++++++++++++++++++++++++++");
391
392            sTelephonyNetworkFactories[i].dump(fd, pw, args);
393
394            pw.flush();
395            pw.println("++++++++++++++++++++++++++++++++");
396
397            try {
398                ((IccCardProxy)phone.getIccCard()).dump(fd, pw, args);
399            } catch (Exception e) {
400                e.printStackTrace();
401            }
402            pw.flush();
403            pw.decreaseIndent();
404            pw.println("++++++++++++++++++++++++++++++++");
405        }
406
407        pw.println("SubscriptionMonitor:");
408        pw.increaseIndent();
409        try {
410            sSubscriptionMonitor.dump(fd, pw, args);
411        } catch (Exception e) {
412            e.printStackTrace();
413        }
414        pw.decreaseIndent();
415        pw.println("++++++++++++++++++++++++++++++++");
416
417        pw.println("UiccController:");
418        pw.increaseIndent();
419        try {
420            sUiccController.dump(fd, pw, args);
421        } catch (Exception e) {
422            e.printStackTrace();
423        }
424        pw.flush();
425        pw.decreaseIndent();
426        pw.println("++++++++++++++++++++++++++++++++");
427
428        pw.println("SubscriptionController:");
429        pw.increaseIndent();
430        try {
431            SubscriptionController.getInstance().dump(fd, pw, args);
432        } catch (Exception e) {
433            e.printStackTrace();
434        }
435        pw.flush();
436        pw.decreaseIndent();
437        pw.println("++++++++++++++++++++++++++++++++");
438
439        pw.println("SubInfoRecordUpdater:");
440        pw.increaseIndent();
441        try {
442            sSubInfoRecordUpdater.dump(fd, pw, args);
443        } catch (Exception e) {
444            e.printStackTrace();
445        }
446        pw.flush();
447        pw.decreaseIndent();
448        pw.println("++++++++++++++++++++++++++++++++");
449
450        pw.println("LocalLogs:");
451        pw.increaseIndent();
452        synchronized (sLocalLogs) {
453            for (String key : sLocalLogs.keySet()) {
454                pw.println(key);
455                pw.increaseIndent();
456                sLocalLogs.get(key).dump(fd, pw, args);
457                pw.decreaseIndent();
458            }
459            pw.flush();
460        }
461        pw.decreaseIndent();
462    }
463}
464