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