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