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