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