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