482 lines
23 KiB
Java
482 lines
23 KiB
Java
/*
|
|
* Copyright (C) 2016 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 android.telephony.CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY;
|
|
import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY;
|
|
|
|
import android.annotation.Nullable;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.ComponentName;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.Network;
|
|
import android.os.AsyncResult;
|
|
import android.os.Build;
|
|
import android.os.Handler;
|
|
import android.os.Message;
|
|
import android.os.PersistableBundle;
|
|
import android.os.UserHandle;
|
|
import android.telephony.CarrierConfigManager;
|
|
import android.telephony.SubscriptionManager;
|
|
import android.telephony.TelephonyManager;
|
|
import android.telephony.data.ApnSetting;
|
|
import android.text.TextUtils;
|
|
import android.util.LocalLog;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.telephony.util.ArrayUtils;
|
|
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.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* This class act as an CarrierSignalling Agent.
|
|
* it load registered carrier signalling receivers from carrier config, cache the result to avoid
|
|
* repeated polling and send the intent to the interested receivers.
|
|
* Each CarrierSignalAgent is associated with a phone object.
|
|
*/
|
|
public class CarrierSignalAgent extends Handler {
|
|
|
|
private static final String LOG_TAG = CarrierSignalAgent.class.getSimpleName();
|
|
private static final boolean DBG = true;
|
|
private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
|
|
private static final boolean WAKE = true;
|
|
private static final boolean NO_WAKE = false;
|
|
|
|
/** delimiters for parsing config of the form: pakName./receiverName : signal1, signal2,..*/
|
|
private static final String COMPONENT_NAME_DELIMITER = "\\s*:\\s*";
|
|
private static final String CARRIER_SIGNAL_DELIMITER = "\\s*,\\s*";
|
|
|
|
/** Member variables */
|
|
private final Phone mPhone;
|
|
private boolean mDefaultNetworkAvail;
|
|
|
|
/**
|
|
* This is a map of intent action -> set of component name of statically registered
|
|
* carrier signal receivers(wakeup receivers).
|
|
* Those intents are declared in the Manifest files, aiming to wakeup broadcast receivers.
|
|
* Carrier apps should be careful when configuring the wake signal list to avoid unnecessary
|
|
* wakeup. Note we use Set as the entry value to compare config directly regardless of element
|
|
* order.
|
|
* @see CarrierConfigManager#KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY
|
|
*/
|
|
private Map<String, Set<ComponentName>> mCachedWakeSignalConfigs = new HashMap<>();
|
|
|
|
/**
|
|
* This is a map of intent action -> set of component name of dynamically registered
|
|
* carrier signal receivers(non-wakeup receivers). Those intents will not wake up the apps.
|
|
* Note Carrier apps should avoid configuring no wake signals in there Manifest files.
|
|
* Note we use Set as the entry value to compare config directly regardless of element order.
|
|
* @see CarrierConfigManager#KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY
|
|
*/
|
|
private Map<String, Set<ComponentName>> mCachedNoWakeSignalConfigs = new HashMap<>();
|
|
|
|
private static final int EVENT_REGISTER_DEFAULT_NETWORK_AVAIL = 0;
|
|
|
|
/**
|
|
* This is a list of supported signals from CarrierSignalAgent
|
|
*/
|
|
private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = Set.of(
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
|
|
|
|
private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP = Map.of(
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
|
|
TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
|
|
TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
|
|
TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
|
|
TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET,
|
|
TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
|
|
TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
|
|
|
|
private static final Map<String, String> COMPAT_ACTION_TO_NEW_MAP = NEW_ACTION_TO_COMPAT_MAP
|
|
.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
|
|
|
|
private final LocalLog mErrorLocalLog = new LocalLog(16);
|
|
|
|
private ConnectivityManager.NetworkCallback mNetworkCallback;
|
|
|
|
/** Constructor */
|
|
public CarrierSignalAgent(Phone phone) {
|
|
mPhone = phone;
|
|
CarrierConfigManager carrierConfigManager = mPhone.getContext().getSystemService(
|
|
CarrierConfigManager.class);
|
|
loadCarrierConfig();
|
|
carrierConfigManager.registerCarrierConfigChangeListener(
|
|
mPhone.getContext().getMainExecutor(),
|
|
(slotIndex, subId, carrierId, specificCarrierId) -> {
|
|
if (slotIndex == mPhone.getPhoneId()) {
|
|
loadCarrierConfig();
|
|
}
|
|
});
|
|
mPhone.getCarrierActionAgent().registerForCarrierAction(
|
|
CarrierActionAgent.CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS, this,
|
|
EVENT_REGISTER_DEFAULT_NETWORK_AVAIL, null, false);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_REGISTER_DEFAULT_NETWORK_AVAIL:
|
|
AsyncResult ar = (AsyncResult) msg.obj;
|
|
if (ar.exception != null) {
|
|
Rlog.e(LOG_TAG, "Register default network exception: " + ar.exception);
|
|
return;
|
|
}
|
|
final ConnectivityManager connectivityMgr = mPhone.getContext()
|
|
.getSystemService(ConnectivityManager.class);
|
|
if ((boolean) ar.result) {
|
|
mNetworkCallback = new ConnectivityManager.NetworkCallback() {
|
|
@Override
|
|
public void onAvailable(Network network) {
|
|
// an optimization to avoid signaling on every default network switch.
|
|
if (!mDefaultNetworkAvail) {
|
|
if (DBG) log("Default network available: " + network);
|
|
Intent intent = new Intent(TelephonyManager
|
|
.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
|
|
intent.putExtra(
|
|
TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, true);
|
|
notifyCarrierSignalReceivers(intent);
|
|
mDefaultNetworkAvail = true;
|
|
}
|
|
}
|
|
@Override
|
|
public void onLost(Network network) {
|
|
if (DBG) log("Default network lost: " + network);
|
|
Intent intent = new Intent(TelephonyManager
|
|
.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
|
|
intent.putExtra(
|
|
TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, false);
|
|
notifyCarrierSignalReceivers(intent);
|
|
mDefaultNetworkAvail = false;
|
|
}
|
|
};
|
|
connectivityMgr.registerDefaultNetworkCallback(mNetworkCallback, mPhone);
|
|
log("Register default network");
|
|
|
|
} else if (mNetworkCallback != null) {
|
|
connectivityMgr.unregisterNetworkCallback(mNetworkCallback);
|
|
mNetworkCallback = null;
|
|
mDefaultNetworkAvail = false;
|
|
log("unregister default network");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* load carrier config and cached the results into a hashMap action -> array list of components.
|
|
*/
|
|
private void loadCarrierConfig() {
|
|
PersistableBundle b =
|
|
CarrierConfigManager.getCarrierConfigSubset(
|
|
mPhone.getContext(),
|
|
mPhone.getSubId(),
|
|
KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
|
|
KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
|
|
if (b.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
synchronized (mCachedWakeSignalConfigs) {
|
|
log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
|
|
Map<String, Set<ComponentName>> config = parseAndCache(
|
|
b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
|
|
// In some rare cases, up-to-date config could be fetched with delay and all signals
|
|
// have already been delivered the receivers from the default carrier config.
|
|
// To handle this raciness, we should notify those receivers (from old configs)
|
|
// and reset carrier actions. This should be done before cached Config got purged
|
|
// and written with the up-to-date value, Otherwise those receivers from the
|
|
// old config might lingers without properly clean-up.
|
|
if (!mCachedWakeSignalConfigs.isEmpty()
|
|
&& !config.equals(mCachedWakeSignalConfigs)) {
|
|
if (VDBG) log("carrier config changed, reset receivers from old config");
|
|
mPhone.getCarrierActionAgent().sendEmptyMessage(
|
|
CarrierActionAgent.CARRIER_ACTION_RESET);
|
|
}
|
|
mCachedWakeSignalConfigs = config;
|
|
}
|
|
|
|
synchronized (mCachedNoWakeSignalConfigs) {
|
|
log("Loading carrier config: "
|
|
+ KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
|
|
Map<String, Set<ComponentName>> config = parseAndCache(
|
|
b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
|
|
if (!mCachedNoWakeSignalConfigs.isEmpty()
|
|
&& !config.equals(mCachedNoWakeSignalConfigs)) {
|
|
if (VDBG) log("carrier config changed, reset receivers from old config");
|
|
mPhone.getCarrierActionAgent().sendEmptyMessage(
|
|
CarrierActionAgent.CARRIER_ACTION_RESET);
|
|
}
|
|
mCachedNoWakeSignalConfigs = config;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse each config with the form {pakName./receiverName : signal1, signal2,.} and cached the
|
|
* result internally to avoid repeated polling
|
|
* @see #CARRIER_SIGNAL_DELIMITER
|
|
* @see #COMPONENT_NAME_DELIMITER
|
|
* @param configs raw information from carrier config
|
|
*/
|
|
private Map<String, Set<ComponentName>> parseAndCache(String[] configs) {
|
|
Map<String, Set<ComponentName>> newCachedWakeSignalConfigs = new HashMap<>();
|
|
if (!ArrayUtils.isEmpty(configs)) {
|
|
for (String config : configs) {
|
|
if (!TextUtils.isEmpty(config)) {
|
|
String[] splitStr = config.trim().split(COMPONENT_NAME_DELIMITER, 2);
|
|
if (splitStr.length == 2) {
|
|
ComponentName componentName = ComponentName
|
|
.unflattenFromString(splitStr[0]);
|
|
if (componentName == null) {
|
|
loge("Invalid component name: " + splitStr[0]);
|
|
continue;
|
|
}
|
|
String[] signals = splitStr[1].split(CARRIER_SIGNAL_DELIMITER);
|
|
for (String s : signals) {
|
|
if (!VALID_CARRIER_SIGNAL_ACTIONS.contains(s)) {
|
|
// It could be a legacy action in the com.android.internal.telephony
|
|
// namespace. If that's the case, translate it to the new actions.
|
|
if (COMPAT_ACTION_TO_NEW_MAP.containsKey(s)) {
|
|
s = COMPAT_ACTION_TO_NEW_MAP.get(s);
|
|
} else {
|
|
loge("Invalid signal name: " + s);
|
|
continue;
|
|
}
|
|
}
|
|
Set<ComponentName> componentList = newCachedWakeSignalConfigs.get(s);
|
|
if (componentList == null) {
|
|
componentList = new HashSet<>();
|
|
newCachedWakeSignalConfigs.put(s, componentList);
|
|
}
|
|
componentList.add(componentName);
|
|
if (VDBG) {
|
|
logv("Add config " + "{signal: " + s
|
|
+ " componentName: " + componentName + "}");
|
|
}
|
|
}
|
|
} else {
|
|
loge("invalid config format: " + config);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return newCachedWakeSignalConfigs;
|
|
}
|
|
|
|
/**
|
|
* Check if there are registered carrier broadcast receivers to handle the passing intent
|
|
*/
|
|
public boolean hasRegisteredReceivers(String action) {
|
|
return mCachedWakeSignalConfigs.containsKey(action)
|
|
|| mCachedNoWakeSignalConfigs.containsKey(action);
|
|
}
|
|
|
|
/**
|
|
* Broadcast the intents explicitly.
|
|
* Some correctness checks will be applied before broadcasting.
|
|
* - for non-wakeup(runtime) receivers, make sure the intent is not declared in their manifests
|
|
* and apply FLAG_EXCLUDE_STOPPED_PACKAGES to avoid wake-up
|
|
* - for wakeup(manifest) receivers, make sure there are matched receivers with registered
|
|
* intents.
|
|
*
|
|
* @param intent intent which signals carrier apps
|
|
* @param receivers a list of component name for broadcast receivers.
|
|
* Those receivers could either be statically declared in Manifest or
|
|
* registered during run-time.
|
|
* @param wakeup true indicate wakeup receivers otherwise non-wakeup receivers
|
|
*/
|
|
private void broadcast(Intent intent, Set<ComponentName> receivers, boolean wakeup) {
|
|
final PackageManager packageManager = mPhone.getContext().getPackageManager();
|
|
for (ComponentName name : receivers) {
|
|
Intent signal = new Intent(intent);
|
|
if (wakeup) {
|
|
signal.setComponent(name);
|
|
} else {
|
|
// Explicit intents won't reach dynamically registered receivers -- set the package
|
|
// instead.
|
|
signal.setPackage(name.getPackageName());
|
|
}
|
|
|
|
if (wakeup && packageManager.queryBroadcastReceivers(signal,
|
|
PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
|
|
loge("Carrier signal receivers are configured but unavailable: "
|
|
+ signal.getComponent());
|
|
continue;
|
|
}
|
|
if (!wakeup && !packageManager.queryBroadcastReceivers(signal,
|
|
PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
|
|
loge("Runtime signals shouldn't be configured in Manifest: "
|
|
+ signal.getComponent());
|
|
continue;
|
|
}
|
|
|
|
SubscriptionManager.putSubscriptionIdExtra(signal, mPhone.getSubId());
|
|
signal.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
|
if (!wakeup) signal.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
|
|
|
|
Intent compatIntent = null;
|
|
try {
|
|
if (mPhone.getContext().getPackageManager()
|
|
.getApplicationInfo(name.getPackageName(), 0).targetSdkVersion
|
|
<= Build.VERSION_CODES.R) {
|
|
compatIntent = createCompatIntent(signal);
|
|
}
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
// ignore, don't do anything special for compatibility
|
|
}
|
|
try {
|
|
Intent intentToSend = compatIntent == null ? signal : compatIntent;
|
|
mPhone.getContext().sendBroadcastAsUser(intentToSend, UserHandle.ALL);
|
|
if (DBG) {
|
|
log("Sending signal " + intentToSend.getAction()
|
|
+ " to the carrier signal receiver: " + intentToSend.getComponent());
|
|
}
|
|
} catch (ActivityNotFoundException e) {
|
|
loge("Send broadcast failed: " + e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Match the intent against cached tables to find a list of registered carrier signal
|
|
* receivers and broadcast the intent.
|
|
* @param intent broadcasting intent, it could belong to wakeup, non-wakeup signal list or both
|
|
*
|
|
*/
|
|
public void notifyCarrierSignalReceivers(Intent intent) {
|
|
Set<ComponentName> receiverSet;
|
|
|
|
synchronized (mCachedWakeSignalConfigs) {
|
|
receiverSet = mCachedWakeSignalConfigs.get(intent.getAction());
|
|
if (!ArrayUtils.isEmpty(receiverSet)) {
|
|
broadcast(intent, receiverSet, WAKE);
|
|
}
|
|
}
|
|
|
|
synchronized (mCachedNoWakeSignalConfigs) {
|
|
receiverSet = mCachedNoWakeSignalConfigs.get(intent.getAction());
|
|
if (!ArrayUtils.isEmpty(receiverSet)) {
|
|
broadcast(intent, receiverSet, NO_WAKE);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static @Nullable Intent createCompatIntent(Intent original) {
|
|
String compatAction = NEW_ACTION_TO_COMPAT_MAP.get(original.getAction());
|
|
if (compatAction == null) {
|
|
Rlog.i(LOG_TAG, "intent action " + original.getAction() + " does not have a"
|
|
+ " compat alternative for component " + original.getComponent());
|
|
return null;
|
|
}
|
|
Intent compatIntent = new Intent(original);
|
|
compatIntent.setAction(compatAction);
|
|
for (String extraKey : original.getExtras().keySet()) {
|
|
switch (extraKey) {
|
|
case TelephonyManager.EXTRA_REDIRECTION_URL:
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL,
|
|
original.getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL));
|
|
break;
|
|
case TelephonyManager.EXTRA_DATA_FAIL_CAUSE:
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_ERROR_CODE,
|
|
original.getIntExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, -1));
|
|
break;
|
|
case TelephonyManager.EXTRA_PCO_ID:
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_PCO_ID,
|
|
original.getIntExtra(TelephonyManager.EXTRA_PCO_ID, -1));
|
|
break;
|
|
case TelephonyManager.EXTRA_PCO_VALUE:
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_PCO_VALUE,
|
|
original.getByteArrayExtra(TelephonyManager.EXTRA_PCO_VALUE));
|
|
break;
|
|
case TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE:
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_DEFAULT_NETWORK_AVAILABLE,
|
|
original.getBooleanExtra(
|
|
TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, false));
|
|
break;
|
|
case TelephonyManager.EXTRA_APN_TYPE:
|
|
int apnType = original.getIntExtra(TelephonyManager.EXTRA_APN_TYPE,
|
|
ApnSetting.TYPE_DEFAULT);
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_INT, apnType);
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_APN_TYPE,
|
|
ApnSetting.getApnTypesStringFromBitmask(apnType));
|
|
break;
|
|
case TelephonyManager.EXTRA_APN_PROTOCOL:
|
|
int apnProtocol = original.getIntExtra(TelephonyManager.EXTRA_APN_PROTOCOL, -1);
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_APN_PROTOCOL_INT, apnProtocol);
|
|
compatIntent.putExtra(TelephonyIntents.EXTRA_APN_PROTOCOL,
|
|
ApnSetting.getProtocolStringFromInt(apnProtocol));
|
|
break;
|
|
}
|
|
}
|
|
return compatIntent;
|
|
}
|
|
|
|
private void log(String s) {
|
|
Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
|
|
}
|
|
|
|
private void loge(String s) {
|
|
mErrorLocalLog.log(s);
|
|
Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
|
|
}
|
|
|
|
private void logv(String s) {
|
|
Rlog.v(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
|
|
}
|
|
|
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
|
pw.println("mCachedWakeSignalConfigs:");
|
|
ipw.increaseIndent();
|
|
for (Map.Entry<String, Set<ComponentName>> entry : mCachedWakeSignalConfigs.entrySet()) {
|
|
pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue());
|
|
}
|
|
ipw.decreaseIndent();
|
|
|
|
pw.println("mCachedNoWakeSignalConfigs:");
|
|
ipw.increaseIndent();
|
|
for (Map.Entry<String, Set<ComponentName>> entry : mCachedNoWakeSignalConfigs.entrySet()) {
|
|
pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue());
|
|
}
|
|
ipw.decreaseIndent();
|
|
|
|
pw.println("mDefaultNetworkAvail: " + mDefaultNetworkAvail);
|
|
|
|
pw.println("error log:");
|
|
ipw.increaseIndent();
|
|
mErrorLocalLog.dump(fd, pw, args);
|
|
ipw.decreaseIndent();
|
|
}
|
|
}
|