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.annotation.Nullable;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.SharedPreferences;
23import android.content.pm.PackageManager;
24import android.net.LocalServerSocket;
25import android.os.Looper;
26import android.os.ServiceManager;
27import android.preference.PreferenceManager;
28import android.provider.Settings;
29import android.provider.Settings.SettingNotFoundException;
30import android.telephony.Rlog;
31import android.telephony.SubscriptionManager;
32import android.telephony.TelephonyManager;
33import android.util.LocalLog;
34
35import com.android.internal.os.BackgroundThread;
36import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
37import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory;
38import com.android.internal.telephony.euicc.EuiccCardController;
39import com.android.internal.telephony.euicc.EuiccController;
40import com.android.internal.telephony.ims.ImsResolver;
41import com.android.internal.telephony.imsphone.ImsPhone;
42import com.android.internal.telephony.imsphone.ImsPhoneFactory;
43import com.android.internal.telephony.sip.SipPhone;
44import com.android.internal.telephony.sip.SipPhoneFactory;
45import com.android.internal.telephony.uicc.UiccController;
46import com.android.internal.telephony.uicc.UiccProfile;
47import com.android.internal.telephony.util.NotificationChannelController;
48import com.android.internal.util.IndentingPrintWriter;
49
50import java.io.FileDescriptor;
51import java.io.PrintWriter;
52import java.util.HashMap;
53import java.util.Map;
54
55/**
56 * {@hide}
57 */
58public class PhoneFactory {
59    static final String LOG_TAG = "PhoneFactory";
60    static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
61    static final int SOCKET_OPEN_MAX_RETRY = 3;
62    static final boolean DBG = false;
63
64    //***** Class Variables
65
66    // lock sLockProxyPhones protects both sPhones and sPhone
67    final static Object sLockProxyPhones = new Object();
68    static private Phone[] sPhones = null;
69    static private Phone sPhone = null;
70
71    static private CommandsInterface[] sCommandsInterfaces = null;
72
73    static private ProxyController sProxyController;
74    static private UiccController sUiccController;
75    private static IntentBroadcaster sIntentBroadcaster;
76    private static @Nullable EuiccController sEuiccController;
77    private static @Nullable EuiccCardController sEuiccCardController;
78
79    static private CommandsInterface sCommandsInterface = null;
80    static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
81
82    static private boolean sMadeDefaults = false;
83    static private PhoneNotifier sPhoneNotifier;
84    static private Context sContext;
85    static private PhoneSwitcher sPhoneSwitcher;
86    static private SubscriptionMonitor sSubscriptionMonitor;
87    static private TelephonyNetworkFactory[] sTelephonyNetworkFactories;
88    static private ImsResolver sImsResolver;
89    static private NotificationChannelController sNotificationChannelController;
90
91    static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
92
93    // TODO - make this a dynamic property read from the modem
94    public static final int MAX_ACTIVE_PHONES = 1;
95
96    //***** Class Methods
97
98    public static void makeDefaultPhones(Context context) {
99        makeDefaultPhone(context);
100    }
101
102    /**
103     * FIXME replace this with some other way of making these
104     * instances
105     */
106    public static void makeDefaultPhone(Context context) {
107        synchronized (sLockProxyPhones) {
108            if (!sMadeDefaults) {
109                sContext = context;
110                // create the telephony device controller.
111                TelephonyDevController.create();
112
113                int retryCount = 0;
114                for(;;) {
115                    boolean hasException = false;
116                    retryCount ++;
117
118                    try {
119                        // use UNIX domain socket to
120                        // prevent subsequent initialization
121                        new LocalServerSocket("com.android.internal.telephony");
122                    } catch (java.io.IOException ex) {
123                        hasException = true;
124                    }
125
126                    if ( !hasException ) {
127                        break;
128                    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
129                        throw new RuntimeException("PhoneFactory probably already running");
130                    } else {
131                        try {
132                            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
133                        } catch (InterruptedException er) {
134                        }
135                    }
136                }
137
138                sPhoneNotifier = new DefaultPhoneNotifier();
139
140                int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
141                Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
142
143                /* In case of multi SIM mode two instances of Phone, RIL are created,
144                   where as in single SIM mode only instance. isMultiSimEnabled() function checks
145                   whether it is single SIM or multi SIM mode */
146                int numPhones = TelephonyManager.getDefault().getPhoneCount();
147                // Return whether or not the device should use dynamic binding or the static
148                // implementation (deprecated)
149                boolean isDynamicBinding = sContext.getResources().getBoolean(
150                        com.android.internal.R.bool.config_dynamic_bind_ims);
151                // Get the package name of the default IMS implementation.
152                String defaultImsPackage = sContext.getResources().getString(
153                        com.android.internal.R.string.config_ims_package);
154                // Start ImsResolver and bind to ImsServices.
155                Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
156                sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones,
157                        isDynamicBinding);
158                sImsResolver.initPopulateCacheAndStartBind();
159
160                int[] networkModes = new int[numPhones];
161                sPhones = new Phone[numPhones];
162                sCommandsInterfaces = new RIL[numPhones];
163                sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
164
165                for (int i = 0; i < numPhones; i++) {
166                    // reads the system properties and makes commandsinterface
167                    // Get preferred network type.
168                    networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;
169
170                    Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
171                    sCommandsInterfaces[i] = new RIL(context, networkModes[i],
172                            cdmaSubscription, i);
173                }
174                Rlog.i(LOG_TAG, "Creating SubscriptionController");
175                SubscriptionController.init(context, sCommandsInterfaces);
176
177                // Instantiate UiccController so that all other classes can just
178                // call getInstance()
179                sUiccController = UiccController.make(context, sCommandsInterfaces);
180
181                if (context.getPackageManager().hasSystemFeature(
182                        PackageManager.FEATURE_TELEPHONY_EUICC)) {
183                    sEuiccController = EuiccController.init(context);
184                    sEuiccCardController = EuiccCardController.init(context);
185                }
186
187                for (int i = 0; i < numPhones; i++) {
188                    Phone phone = null;
189                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
190                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
191                        phone = new GsmCdmaPhone(context,
192                                sCommandsInterfaces[i], sPhoneNotifier, i,
193                                PhoneConstants.PHONE_TYPE_GSM,
194                                TelephonyComponentFactory.getInstance());
195                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
196                        phone = new GsmCdmaPhone(context,
197                                sCommandsInterfaces[i], sPhoneNotifier, i,
198                                PhoneConstants.PHONE_TYPE_CDMA_LTE,
199                                TelephonyComponentFactory.getInstance());
200                    }
201                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
202
203                    sPhones[i] = phone;
204                }
205
206                // Set the default phone in base class.
207                // FIXME: This is a first best guess at what the defaults will be. It
208                // FIXME: needs to be done in a more controlled manner in the future.
209                sPhone = sPhones[0];
210                sCommandsInterface = sCommandsInterfaces[0];
211
212                // Ensure that we have a default SMS app. Requesting the app with
213                // updateIfNeeded set to true is enough to configure a default SMS app.
214                ComponentName componentName =
215                        SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */);
216                String packageName = "NONE";
217                if (componentName != null) {
218                    packageName = componentName.getPackageName();
219                }
220                Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);
221
222                // Set up monitor to watch for changes to SMS packages
223                SmsApplication.initSmsPackageMonitor(context);
224
225                sMadeDefaults = true;
226
227                Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
228                sSubInfoRecordUpdater = new SubscriptionInfoUpdater(
229                        BackgroundThread.get().getLooper(), context, sPhones, sCommandsInterfaces);
230                SubscriptionController.getInstance().updatePhonesAvailability(sPhones);
231
232                // Start monitoring after defaults have been made.
233                // Default phone must be ready before ImsPhone is created because ImsService might
234                // need it when it is being opened. This should initialize multiple ImsPhones for
235                // ImsResolver implementations of ImsService.
236                for (int i = 0; i < numPhones; i++) {
237                    sPhones[i].startMonitoringImsService();
238                }
239
240                ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(
241                        ServiceManager.getService("telephony.registry"));
242                SubscriptionController sc = SubscriptionController.getInstance();
243
244                sSubscriptionMonitor = new SubscriptionMonitor(tr, sContext, sc, numPhones);
245
246                sPhoneSwitcher = new PhoneSwitcher(MAX_ACTIVE_PHONES, numPhones,
247                        sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces,
248                        sPhones);
249
250                sProxyController = ProxyController.getInstance(context, sPhones,
251                        sUiccController, sCommandsInterfaces, sPhoneSwitcher);
252
253                sIntentBroadcaster = IntentBroadcaster.getInstance(context);
254
255                sNotificationChannelController = new NotificationChannelController(context);
256
257                sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
258                for (int i = 0; i < numPhones; i++) {
259                    sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
260                            sPhoneSwitcher, sc, sSubscriptionMonitor, Looper.myLooper(),
261                            sContext, i, sPhones[i].mDcTracker);
262                }
263            }
264        }
265    }
266
267    public static Phone getDefaultPhone() {
268        synchronized (sLockProxyPhones) {
269            if (!sMadeDefaults) {
270                throw new IllegalStateException("Default phones haven't been made yet!");
271            }
272            return sPhone;
273        }
274    }
275
276    public static Phone getPhone(int phoneId) {
277        Phone phone;
278        String dbgInfo = "";
279
280        synchronized (sLockProxyPhones) {
281            if (!sMadeDefaults) {
282                throw new IllegalStateException("Default phones haven't been made yet!");
283                // CAF_MSIM FIXME need to introduce default phone id ?
284            } else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
285                if (DBG) dbgInfo = "phoneId == DEFAULT_PHONE_ID return sPhone";
286                phone = sPhone;
287            } else {
288                if (DBG) dbgInfo = "phoneId != DEFAULT_PHONE_ID return sPhones[phoneId]";
289                phone = (((phoneId >= 0)
290                                && (phoneId < TelephonyManager.getDefault().getPhoneCount()))
291                        ? sPhones[phoneId] : null);
292            }
293            if (DBG) {
294                Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId +
295                        " phone=" + phone);
296            }
297            return phone;
298        }
299    }
300
301    public static Phone[] getPhones() {
302        synchronized (sLockProxyPhones) {
303            if (!sMadeDefaults) {
304                throw new IllegalStateException("Default phones haven't been made yet!");
305            }
306            return sPhones;
307        }
308    }
309
310    public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() {
311        return sSubInfoRecordUpdater;
312    }
313
314    public static ImsResolver getImsResolver() {
315        return sImsResolver;
316    }
317
318    /**
319     * Makes a {@link SipPhone} object.
320     * @param sipUri the local SIP URI the phone runs on
321     * @return the {@code SipPhone} object or null if the SIP URI is not valid
322     */
323    public static SipPhone makeSipPhone(String sipUri) {
324        return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
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, int phoneSubId) {
335        int networkType = android.provider.Settings.Global.getInt(context.getContentResolver(),
336                android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
337                -1 /* invalid network mode */);
338        Rlog.d(LOG_TAG, "calculatePreferredNetworkType: phoneSubId = " + phoneSubId +
339                " networkType = " + networkType);
340
341        if (networkType == -1) {
342            networkType = RILConstants.PREFERRED_NETWORK_MODE;
343            try {
344                networkType = TelephonyManager.getIntAtIndex(context.getContentResolver(),
345                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE,
346                        SubscriptionController.getInstance().getPhoneId(phoneSubId));
347            } catch (SettingNotFoundException retrySnfe) {
348                Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
349                        + "Settings.Global.PREFERRED_NETWORK_MODE");
350            }
351        }
352
353        return networkType;
354    }
355
356    /* Gets the default subscription */
357    public static int getDefaultSubscription() {
358        return SubscriptionController.getInstance().getDefaultSubId();
359    }
360
361    /* Returns User SMS Prompt property,  enabled or not */
362    public static boolean isSMSPromptEnabled() {
363        boolean prompt = false;
364        int value = 0;
365        try {
366            value = Settings.Global.getInt(sContext.getContentResolver(),
367                    Settings.Global.MULTI_SIM_SMS_PROMPT);
368        } catch (SettingNotFoundException snfe) {
369            Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Prompt Values");
370        }
371        prompt = (value == 0) ? false : true ;
372        Rlog.d(LOG_TAG, "SMS Prompt option:" + prompt);
373
374       return prompt;
375    }
376
377    /**
378     * Makes a {@link ImsPhone} object.
379     * @return the {@code ImsPhone} object or null if the exception occured
380     */
381    public static Phone makeImsPhone(PhoneNotifier phoneNotifier, Phone defaultPhone) {
382        return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
383    }
384
385    /**
386     * Request a refresh of the embedded subscription list.
387     *
388     * @param callback Optional callback to execute after the refresh completes. Must terminate
389     *     quickly as it will be called from SubscriptionInfoUpdater's handler thread.
390     */
391    public static void requestEmbeddedSubscriptionInfoListRefresh(@Nullable Runnable callback) {
392        sSubInfoRecordUpdater.requestEmbeddedSubscriptionInfoListRefresh(callback);
393    }
394
395    /**
396     * Adds a local log category.
397     *
398     * Only used within the telephony process.  Use localLog to add log entries.
399     *
400     * TODO - is there a better way to do this?  Think about design when we have a minute.
401     *
402     * @param key the name of the category - will be the header in the service dump.
403     * @param size the number of lines to maintain in this category
404     */
405    public static void addLocalLog(String key, int size) {
406        synchronized(sLocalLogs) {
407            if (sLocalLogs.containsKey(key)) {
408                throw new IllegalArgumentException("key " + key + " already present");
409            }
410            sLocalLogs.put(key, new LocalLog(size));
411        }
412    }
413
414    /**
415     * Add a line to the named Local Log.
416     *
417     * This will appear in the TelephonyDebugService dump.
418     *
419     * @param key the name of the log category to put this in.  Must be created
420     *            via addLocalLog.
421     * @param log the string to add to the log.
422     */
423    public static void localLog(String key, String log) {
424        synchronized(sLocalLogs) {
425            if (sLocalLogs.containsKey(key) == false) {
426                throw new IllegalArgumentException("key " + key + " not found");
427            }
428            sLocalLogs.get(key).log(log);
429        }
430    }
431
432    public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
433        IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, "  ");
434        pw.println("PhoneFactory:");
435        pw.println(" sMadeDefaults=" + sMadeDefaults);
436
437        sPhoneSwitcher.dump(fd, pw, args);
438        pw.println();
439
440        Phone[] phones = (Phone[])PhoneFactory.getPhones();
441        for (int i = 0; i < phones.length; i++) {
442            pw.increaseIndent();
443            Phone phone = phones[i];
444
445            try {
446                phone.dump(fd, pw, args);
447            } catch (Exception e) {
448                pw.println("Telephony DebugService: Could not get Phone[" + i + "] e=" + e);
449                continue;
450            }
451
452            pw.flush();
453            pw.println("++++++++++++++++++++++++++++++++");
454
455            sTelephonyNetworkFactories[i].dump(fd, pw, args);
456
457            pw.flush();
458            pw.println("++++++++++++++++++++++++++++++++");
459
460            try {
461                UiccProfile uiccProfile = (UiccProfile) phone.getIccCard();
462                if (uiccProfile != null) {
463                    uiccProfile.dump(fd, pw, args);
464                }
465            } catch (Exception e) {
466                e.printStackTrace();
467            }
468            pw.flush();
469            pw.decreaseIndent();
470            pw.println("++++++++++++++++++++++++++++++++");
471        }
472
473        pw.println("SubscriptionMonitor:");
474        pw.increaseIndent();
475        try {
476            sSubscriptionMonitor.dump(fd, pw, args);
477        } catch (Exception e) {
478            e.printStackTrace();
479        }
480        pw.decreaseIndent();
481        pw.println("++++++++++++++++++++++++++++++++");
482
483        pw.println("UiccController:");
484        pw.increaseIndent();
485        try {
486            sUiccController.dump(fd, pw, args);
487        } catch (Exception e) {
488            e.printStackTrace();
489        }
490        pw.flush();
491        pw.decreaseIndent();
492        pw.println("++++++++++++++++++++++++++++++++");
493
494        if (sEuiccController != null) {
495            pw.println("EuiccController:");
496            pw.increaseIndent();
497            try {
498                sEuiccController.dump(fd, pw, args);
499                sEuiccCardController.dump(fd, pw, args);
500            } catch (Exception e) {
501                e.printStackTrace();
502            }
503            pw.flush();
504            pw.decreaseIndent();
505            pw.println("++++++++++++++++++++++++++++++++");
506        }
507
508        pw.println("SubscriptionController:");
509        pw.increaseIndent();
510        try {
511            SubscriptionController.getInstance().dump(fd, pw, args);
512        } catch (Exception e) {
513            e.printStackTrace();
514        }
515        pw.flush();
516        pw.decreaseIndent();
517        pw.println("++++++++++++++++++++++++++++++++");
518
519        pw.println("SubInfoRecordUpdater:");
520        pw.increaseIndent();
521        try {
522            sSubInfoRecordUpdater.dump(fd, pw, args);
523        } catch (Exception e) {
524            e.printStackTrace();
525        }
526        pw.flush();
527        pw.decreaseIndent();
528        pw.println("++++++++++++++++++++++++++++++++");
529
530        pw.println("LocalLogs:");
531        pw.increaseIndent();
532        synchronized (sLocalLogs) {
533            for (String key : sLocalLogs.keySet()) {
534                pw.println(key);
535                pw.increaseIndent();
536                sLocalLogs.get(key).dump(fd, pw, args);
537                pw.decreaseIndent();
538            }
539            pw.flush();
540        }
541        pw.decreaseIndent();
542        pw.println("++++++++++++++++++++++++++++++++");
543
544        pw.println("SharedPreferences:");
545        pw.increaseIndent();
546        try {
547            if (sContext != null) {
548                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(sContext);
549                Map spValues = sp.getAll();
550                for (Object key : spValues.keySet()) {
551                    pw.println(key + " : " + spValues.get(key));
552                }
553            }
554        } catch (Exception e) {
555            e.printStackTrace();
556        }
557        pw.flush();
558        pw.decreaseIndent();
559    }
560}
561