| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA; |
| import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA_LTE; |
| |
| import static java.util.Arrays.copyOf; |
| |
| import android.annotation.Nullable; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.pm.PackageManager; |
| import android.net.LocalServerSocket; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.preference.PreferenceManager; |
| import android.provider.Settings; |
| import android.provider.Settings.SettingNotFoundException; |
| import android.telephony.AnomalyReporter; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.util.LocalLog; |
| |
| import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; |
| import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory; |
| import com.android.internal.telephony.euicc.EuiccCardController; |
| import com.android.internal.telephony.euicc.EuiccController; |
| import com.android.internal.telephony.imsphone.ImsPhone; |
| import com.android.internal.telephony.imsphone.ImsPhoneFactory; |
| import com.android.internal.telephony.metrics.MetricsCollector; |
| import com.android.internal.telephony.metrics.TelephonyMetrics; |
| import com.android.internal.telephony.sip.SipPhone; |
| import com.android.internal.telephony.sip.SipPhoneFactory; |
| import com.android.internal.telephony.uicc.UiccController; |
| import com.android.internal.telephony.util.NotificationChannelController; |
| import com.android.internal.util.IndentingPrintWriter; |
| import com.android.telephony.Rlog; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * {@hide} |
| */ |
| public class PhoneFactory { |
| static final String LOG_TAG = "PhoneFactory"; |
| static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000; |
| static final int SOCKET_OPEN_MAX_RETRY = 3; |
| static final boolean DBG = false; |
| |
| //***** Class Variables |
| |
| // lock sLockProxyPhones protects sPhones, sPhone and sTelephonyNetworkFactories |
| final static Object sLockProxyPhones = new Object(); |
| static private Phone[] sPhones = null; |
| static private Phone sPhone = null; |
| |
| static private CommandsInterface[] sCommandsInterfaces = null; |
| |
| static private ProxyController sProxyController; |
| static private UiccController sUiccController; |
| private static IntentBroadcaster sIntentBroadcaster; |
| private static @Nullable EuiccController sEuiccController; |
| private static @Nullable EuiccCardController sEuiccCardController; |
| |
| static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null; |
| |
| @UnsupportedAppUsage |
| static private boolean sMadeDefaults = false; |
| @UnsupportedAppUsage |
| static private PhoneNotifier sPhoneNotifier; |
| @UnsupportedAppUsage |
| static private Context sContext; |
| static private PhoneConfigurationManager sPhoneConfigurationManager; |
| static private PhoneSwitcher sPhoneSwitcher; |
| static private TelephonyNetworkFactory[] sTelephonyNetworkFactories; |
| static private NotificationChannelController sNotificationChannelController; |
| static private CellularNetworkValidator sCellularNetworkValidator; |
| |
| static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>(); |
| private static MetricsCollector sMetricsCollector; |
| |
| //***** Class Methods |
| |
| public static void makeDefaultPhones(Context context) { |
| makeDefaultPhone(context); |
| } |
| |
| /** |
| * FIXME replace this with some other way of making these |
| * instances |
| */ |
| @UnsupportedAppUsage |
| public static void makeDefaultPhone(Context context) { |
| synchronized (sLockProxyPhones) { |
| if (!sMadeDefaults) { |
| sContext = context; |
| // create the telephony device controller. |
| TelephonyDevController.create(); |
| |
| TelephonyMetrics metrics = TelephonyMetrics.getInstance(); |
| metrics.setContext(context); |
| |
| int retryCount = 0; |
| for(;;) { |
| boolean hasException = false; |
| retryCount ++; |
| |
| try { |
| // use UNIX domain socket to |
| // prevent subsequent initialization |
| new LocalServerSocket("com.android.internal.telephony"); |
| } catch (java.io.IOException ex) { |
| hasException = true; |
| } |
| |
| if ( !hasException ) { |
| break; |
| } else if (retryCount > SOCKET_OPEN_MAX_RETRY) { |
| throw new RuntimeException("PhoneFactory probably already running"); |
| } else { |
| try { |
| Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); |
| } catch (InterruptedException er) { |
| } |
| } |
| } |
| |
| // register statsd pullers. |
| sMetricsCollector = new MetricsCollector(context); |
| |
| sPhoneNotifier = new DefaultPhoneNotifier(context); |
| |
| int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context); |
| Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription); |
| |
| /* In case of multi SIM mode two instances of Phone, RIL are created, |
| where as in single SIM mode only instance. isMultiSimEnabled() function checks |
| whether it is single SIM or multi SIM mode */ |
| int numPhones = TelephonyManager.getDefault().getActiveModemCount(); |
| |
| int[] networkModes = new int[numPhones]; |
| sPhones = new Phone[numPhones]; |
| sCommandsInterfaces = new RIL[numPhones]; |
| sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones]; |
| |
| for (int i = 0; i < numPhones; i++) { |
| // reads the system properties and makes commandsinterface |
| // Get preferred network type. |
| networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE; |
| |
| Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i])); |
| sCommandsInterfaces[i] = new RIL(context, networkModes[i], |
| cdmaSubscription, i); |
| } |
| |
| // Instantiate UiccController so that all other classes can just |
| // call getInstance() |
| sUiccController = UiccController.make(context); |
| |
| Rlog.i(LOG_TAG, "Creating SubscriptionController"); |
| TelephonyComponentFactory.getInstance().inject(SubscriptionController.class. |
| getName()).initSubscriptionController(context); |
| MultiSimSettingController.init(context, SubscriptionController.getInstance()); |
| |
| if (context.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_TELEPHONY_EUICC)) { |
| sEuiccController = EuiccController.init(context); |
| sEuiccCardController = EuiccCardController.init(context); |
| } |
| |
| for (int i = 0; i < numPhones; i++) { |
| sPhones[i] = createPhone(context, i); |
| } |
| |
| // Set the default phone in base class. |
| // FIXME: This is a first best guess at what the defaults will be. It |
| // FIXME: needs to be done in a more controlled manner in the future. |
| if (numPhones > 0) sPhone = sPhones[0]; |
| |
| // Ensure that we have a default SMS app. Requesting the app with |
| // updateIfNeeded set to true is enough to configure a default SMS app. |
| ComponentName componentName = |
| SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */); |
| String packageName = "NONE"; |
| if (componentName != null) { |
| packageName = componentName.getPackageName(); |
| } |
| Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName); |
| |
| // Set up monitor to watch for changes to SMS packages |
| SmsApplication.initSmsPackageMonitor(context); |
| |
| sMadeDefaults = true; |
| |
| Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater "); |
| HandlerThread pfhandlerThread = new HandlerThread("PhoneFactoryHandlerThread"); |
| pfhandlerThread.start(); |
| sSubInfoRecordUpdater = new SubscriptionInfoUpdater( |
| pfhandlerThread.getLooper(), context, sCommandsInterfaces); |
| |
| // Only bring up IMS if the device supports having an IMS stack. |
| if (context.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_TELEPHONY_IMS)) { |
| // Start monitoring after defaults have been made. |
| // Default phone must be ready before ImsPhone is created because ImsService |
| // might need it when it is being opened. |
| for (int i = 0; i < numPhones; i++) { |
| sPhones[i].createImsPhone(); |
| } |
| } else { |
| Rlog.i(LOG_TAG, "IMS is not supported on this device, skipping ImsResolver."); |
| } |
| |
| sPhoneConfigurationManager = PhoneConfigurationManager.init(sContext); |
| |
| sCellularNetworkValidator = CellularNetworkValidator.make(sContext); |
| |
| int maxActivePhones = sPhoneConfigurationManager |
| .getNumberOfModemsWithSimultaneousDataConnections(); |
| |
| sPhoneSwitcher = TelephonyComponentFactory.getInstance().inject( |
| PhoneSwitcher.class.getName()). |
| makePhoneSwitcher(maxActivePhones, sContext, Looper.myLooper()); |
| |
| sProxyController = ProxyController.getInstance(context); |
| |
| sIntentBroadcaster = IntentBroadcaster.getInstance(context); |
| |
| sNotificationChannelController = new NotificationChannelController(context); |
| |
| for (int i = 0; i < numPhones; i++) { |
| sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory( |
| Looper.myLooper(), sPhones[i]); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Upon single SIM to dual SIM switch or vice versa, we dynamically allocate or de-allocate |
| * Phone and CommandInterface objects. |
| * @param context |
| * @param activeModemCount |
| */ |
| public static void onMultiSimConfigChanged(Context context, int activeModemCount) { |
| synchronized (sLockProxyPhones) { |
| int prevActiveModemCount = sPhones.length; |
| if (prevActiveModemCount == activeModemCount) return; |
| |
| // TODO: clean up sPhones, sCommandsInterfaces and sTelephonyNetworkFactories objects. |
| // Currently we will not clean up the 2nd Phone object, so that it can be re-used if |
| // user switches back. |
| if (prevActiveModemCount > activeModemCount) return; |
| |
| sPhones = copyOf(sPhones, activeModemCount); |
| sCommandsInterfaces = copyOf(sCommandsInterfaces, activeModemCount); |
| sTelephonyNetworkFactories = copyOf(sTelephonyNetworkFactories, activeModemCount); |
| |
| int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context); |
| for (int i = prevActiveModemCount; i < activeModemCount; i++) { |
| sCommandsInterfaces[i] = new RIL(context, RILConstants.PREFERRED_NETWORK_MODE, |
| cdmaSubscription, i); |
| sPhones[i] = createPhone(context, i); |
| if (context.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_TELEPHONY_IMS)) { |
| sPhones[i].createImsPhone(); |
| } |
| sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory( |
| Looper.myLooper(), sPhones[i]); |
| } |
| } |
| } |
| |
| private static Phone createPhone(Context context, int phoneId) { |
| int phoneType = TelephonyManager.getPhoneType(RILConstants.PREFERRED_NETWORK_MODE); |
| Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " phoneId = " + phoneId); |
| |
| // We always use PHONE_TYPE_CDMA_LTE now. |
| if (phoneType == PHONE_TYPE_CDMA) phoneType = PHONE_TYPE_CDMA_LTE; |
| TelephonyComponentFactory injectedComponentFactory = |
| TelephonyComponentFactory.getInstance().inject(GsmCdmaPhone.class.getName()); |
| |
| return injectedComponentFactory.makePhone(context, |
| sCommandsInterfaces[phoneId], sPhoneNotifier, phoneId, phoneType, |
| TelephonyComponentFactory.getInstance()); |
| } |
| |
| @UnsupportedAppUsage |
| public static Phone getDefaultPhone() { |
| synchronized (sLockProxyPhones) { |
| if (!sMadeDefaults) { |
| throw new IllegalStateException("Default phones haven't been made yet!"); |
| } |
| return sPhone; |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public static Phone getPhone(int phoneId) { |
| Phone phone; |
| String dbgInfo = ""; |
| |
| synchronized (sLockProxyPhones) { |
| if (!sMadeDefaults) { |
| throw new IllegalStateException("Default phones haven't been made yet!"); |
| // CAF_MSIM FIXME need to introduce default phone id ? |
| } else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) { |
| if (DBG) { |
| dbgInfo = "phoneId == DEFAULT_PHONE_ID return sPhone"; |
| } |
| phone = sPhone; |
| } else { |
| if (DBG) { |
| dbgInfo = "phoneId != DEFAULT_PHONE_ID return sPhones[phoneId]"; |
| } |
| phone = (phoneId >= 0 && phoneId < sPhones.length) |
| ? sPhones[phoneId] : null; |
| } |
| if (DBG) { |
| Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId + |
| " phone=" + phone); |
| } |
| return phone; |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public static Phone[] getPhones() { |
| synchronized (sLockProxyPhones) { |
| if (!sMadeDefaults) { |
| throw new IllegalStateException("Default phones haven't been made yet!"); |
| } |
| return sPhones; |
| } |
| } |
| |
| public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() { |
| return sSubInfoRecordUpdater; |
| } |
| |
| /** |
| * Get the network factory associated with a given phone ID. |
| * @param phoneId the phone id |
| * @return a factory for this phone ID, or null if none. |
| */ |
| public static TelephonyNetworkFactory getNetworkFactory(int phoneId) { |
| synchronized (sLockProxyPhones) { |
| if (!sMadeDefaults) { |
| throw new IllegalStateException("Default phones haven't been made yet!"); |
| } |
| final String dbgInfo; |
| if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) { |
| dbgInfo = "getNetworkFactory with DEFAULT_PHONE_ID => factory for sPhone"; |
| phoneId = sPhone.getSubId(); |
| } else { |
| dbgInfo = "getNetworkFactory with non-default, return factory for passed id"; |
| } |
| // sTelephonyNetworkFactories is null in tests because in tests makeDefaultPhones() |
| // is not called. |
| final TelephonyNetworkFactory factory = (sTelephonyNetworkFactories != null |
| && (phoneId >= 0 && phoneId < sTelephonyNetworkFactories.length)) |
| ? sTelephonyNetworkFactories[phoneId] : null; |
| if (DBG) { |
| Rlog.d(LOG_TAG, "getNetworkFactory:-" + dbgInfo + " phoneId=" + phoneId |
| + " factory=" + factory); |
| } |
| return factory; |
| } |
| } |
| |
| /** |
| * Makes a {@link SipPhone} object. |
| * @param sipUri the local SIP URI the phone runs on |
| * @return the {@code SipPhone} object or null if the SIP URI is not valid |
| */ |
| public static SipPhone makeSipPhone(String sipUri) { |
| return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier); |
| } |
| |
| /** |
| * Returns the preferred network type that should be set in the modem. |
| * |
| * @param context The current {@link Context}. |
| * @return the preferred network mode that should be set. |
| */ |
| // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController .. |
| @UnsupportedAppUsage |
| public static int calculatePreferredNetworkType(Context context, int phoneSubId) { |
| int networkType = android.provider.Settings.Global.getInt(context.getContentResolver(), |
| android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId, |
| -1 /* invalid network mode */); |
| Rlog.d(LOG_TAG, "calculatePreferredNetworkType: phoneSubId = " + phoneSubId + |
| " networkType = " + networkType); |
| |
| if (networkType == -1) { |
| networkType = RILConstants.PREFERRED_NETWORK_MODE; |
| try { |
| networkType = TelephonyManager.getIntAtIndex(context.getContentResolver(), |
| android.provider.Settings.Global.PREFERRED_NETWORK_MODE, |
| SubscriptionController.getInstance().getPhoneId(phoneSubId)); |
| } catch (SettingNotFoundException retrySnfe) { |
| Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for " |
| + "Settings.Global.PREFERRED_NETWORK_MODE"); |
| } |
| } |
| |
| return networkType; |
| } |
| |
| /* Gets the default subscription */ |
| @UnsupportedAppUsage |
| public static int getDefaultSubscription() { |
| return SubscriptionController.getInstance().getDefaultSubId(); |
| } |
| |
| /* Returns User SMS Prompt property, enabled or not */ |
| public static boolean isSMSPromptEnabled() { |
| boolean prompt = false; |
| int value = 0; |
| try { |
| value = Settings.Global.getInt(sContext.getContentResolver(), |
| Settings.Global.MULTI_SIM_SMS_PROMPT); |
| } catch (SettingNotFoundException snfe) { |
| Rlog.e(LOG_TAG, "Settings Exception Reading Dual Sim SMS Prompt Values"); |
| } |
| prompt = (value == 0) ? false : true ; |
| Rlog.d(LOG_TAG, "SMS Prompt option:" + prompt); |
| |
| return prompt; |
| } |
| |
| /** |
| * Makes a {@link ImsPhone} object. |
| * @return the {@code ImsPhone} object or null if the exception occured |
| */ |
| public static Phone makeImsPhone(PhoneNotifier phoneNotifier, Phone defaultPhone) { |
| return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone); |
| } |
| |
| /** |
| * Request a refresh of the embedded subscription list. |
| * |
| * @param cardId the card ID of the eUICC. |
| * @param callback Optional callback to execute after the refresh completes. Must terminate |
| * quickly as it will be called from SubscriptionInfoUpdater's handler thread. |
| */ |
| public static void requestEmbeddedSubscriptionInfoListRefresh( |
| int cardId, @Nullable Runnable callback) { |
| sSubInfoRecordUpdater.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback); |
| } |
| |
| /** |
| * Get a the SmsController. |
| */ |
| public static SmsController getSmsController() { |
| synchronized (sLockProxyPhones) { |
| if (!sMadeDefaults) { |
| throw new IllegalStateException("Default phones haven't been made yet!"); |
| } |
| return sProxyController.getSmsController(); |
| } |
| } |
| |
| /** |
| * Get Command Interfaces. |
| */ |
| public static CommandsInterface[] getCommandsInterfaces() { |
| synchronized (sLockProxyPhones) { |
| return sCommandsInterfaces; |
| } |
| } |
| |
| /** |
| * Adds a local log category. |
| * |
| * Only used within the telephony process. Use localLog to add log entries. |
| * |
| * TODO - is there a better way to do this? Think about design when we have a minute. |
| * |
| * @param key the name of the category - will be the header in the service dump. |
| * @param size the number of lines to maintain in this category |
| */ |
| public static void addLocalLog(String key, int size) { |
| synchronized(sLocalLogs) { |
| if (sLocalLogs.containsKey(key)) { |
| throw new IllegalArgumentException("key " + key + " already present"); |
| } |
| sLocalLogs.put(key, new LocalLog(size)); |
| } |
| } |
| |
| /** |
| * Add a line to the named Local Log. |
| * |
| * This will appear in the TelephonyDebugService dump. |
| * |
| * @param key the name of the log category to put this in. Must be created |
| * via addLocalLog. |
| * @param log the string to add to the log. |
| */ |
| public static void localLog(String key, String log) { |
| synchronized(sLocalLogs) { |
| if (sLocalLogs.containsKey(key) == false) { |
| throw new IllegalArgumentException("key " + key + " not found"); |
| } |
| sLocalLogs.get(key).log(log); |
| } |
| } |
| |
| /** Returns the MetricsCollector instance. */ |
| public static MetricsCollector getMetricsCollector() { |
| return sMetricsCollector; |
| } |
| |
| public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) { |
| IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, " "); |
| pw.println("PhoneFactory:"); |
| pw.println(" sMadeDefaults=" + sMadeDefaults); |
| |
| sPhoneSwitcher.dump(fd, pw, args); |
| pw.println(); |
| |
| Phone[] phones = (Phone[])PhoneFactory.getPhones(); |
| for (int i = 0; i < phones.length; i++) { |
| pw.increaseIndent(); |
| Phone phone = phones[i]; |
| |
| try { |
| phone.dump(fd, pw, args); |
| } catch (Exception e) { |
| pw.println("Telephony DebugService: Could not get Phone[" + i + "] e=" + e); |
| continue; |
| } |
| |
| pw.flush(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| |
| sTelephonyNetworkFactories[i].dump(fd, pw, args); |
| |
| pw.flush(); |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| } |
| |
| pw.println("UiccController:"); |
| pw.increaseIndent(); |
| try { |
| sUiccController.dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| |
| if (sEuiccController != null) { |
| pw.println("EuiccController:"); |
| pw.increaseIndent(); |
| try { |
| sEuiccController.dump(fd, pw, args); |
| sEuiccCardController.dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| } |
| |
| pw.println("SubscriptionController:"); |
| pw.increaseIndent(); |
| try { |
| SubscriptionController.getInstance().dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| |
| pw.println("SubInfoRecordUpdater:"); |
| pw.increaseIndent(); |
| try { |
| sSubInfoRecordUpdater.dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| |
| pw.println("LocalLogs:"); |
| pw.increaseIndent(); |
| synchronized (sLocalLogs) { |
| for (String key : sLocalLogs.keySet()) { |
| pw.println(key); |
| pw.increaseIndent(); |
| sLocalLogs.get(key).dump(fd, pw, args); |
| pw.decreaseIndent(); |
| } |
| pw.flush(); |
| } |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| |
| pw.println("SharedPreferences:"); |
| pw.increaseIndent(); |
| try { |
| if (sContext != null) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(sContext); |
| Map spValues = sp.getAll(); |
| for (Object key : spValues.keySet()) { |
| pw.println(key + " : " + spValues.get(key)); |
| } |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.decreaseIndent(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| pw.println("DebugEvents:"); |
| pw.increaseIndent(); |
| try { |
| AnomalyReporter.dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| pw.flush(); |
| pw.decreaseIndent(); |
| } |
| } |