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 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