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