DO NOT MERGE Add logging to detect the start of a sync problem.
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / PhoneFactory.java
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
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      */
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
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
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
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
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
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      */
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      */
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 ..
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 */
319     public static int getDefaultSubscription() {
320         return SubscriptionController.getInstance().getDefaultSubId();
321     }
322
323     /* Gets User preferred Voice subscription setting*/
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 */
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 */
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 */
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 */
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*/
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*/
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      */
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      */
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      */
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
457     public static LocalLog getLocalLog(String key) {
458         synchronized (sLocalLogs) {
459             return sLocalLogs.get(key);
460         }
461     }
462
463     public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
464         pw.println("PhoneFactory:");
465         PhoneProxy [] phones = (PhoneProxy[])PhoneFactory.getPhones();
466         int i = -1;
467         for(PhoneProxy phoneProxy : phones) {
468             PhoneBase phoneBase;
469             i += 1;
470
471             try {
472                 phoneBase = (PhoneBase)phoneProxy.getActivePhone();
473                 phoneBase.dump(fd, pw, args);
474             } catch (Exception e) {
475                 pw.println("Telephony DebugService: Could not get Phone[" + i + "] e=" + e);
476                 continue;
477             }
478
479             pw.flush();
480             pw.println("++++++++++++++++++++++++++++++++");
481
482             try {
483                 ((IccCardProxy)phoneProxy.getIccCard()).dump(fd, pw, args);
484             } catch (Exception e) {
485                 e.printStackTrace();
486             }
487             pw.flush();
488             pw.println("++++++++++++++++++++++++++++++++");
489         }
490
491         try {
492             DctController.getInstance().dump(fd, pw, args);
493         } catch (Exception e) {
494             e.printStackTrace();
495         }
496
497         try {
498             mUiccController.dump(fd, pw, args);
499         } catch (Exception e) {
500             e.printStackTrace();
501         }
502         pw.flush();
503         pw.println("++++++++++++++++++++++++++++++++");
504
505         try {
506             SubscriptionController.getInstance().dump(fd, pw, args);
507         } catch (Exception e) {
508             e.printStackTrace();
509         }
510         pw.flush();
511         pw.println("++++++++++++++++++++++++++++++++");
512
513         try {
514             sSubInfoRecordUpdater.dump(fd, pw, args);
515         } catch (Exception e) {
516             e.printStackTrace();
517         }
518         pw.flush();
519
520         pw.println("++++++++++++++++++++++++++++++++");
521         synchronized (sLocalLogs) {
522             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
523             for (String key : sLocalLogs.keySet()) {
524                 ipw.println(key);
525                 ipw.increaseIndent();
526                 sLocalLogs.get(key).dump(fd, ipw, args);
527                 ipw.decreaseIndent();
528             }
529             ipw.flush();
530         }
531     }
532 }