1 /* 2 * Copyright (C) 2016 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.dataconnection; 18 19 20 import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE; 21 import static android.telephony.PhoneStateListener.LISTEN_NONE; 22 23 import android.annotation.IntDef; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.os.Handler; 27 import android.os.RegistrantList; 28 import android.os.SystemProperties; 29 import android.provider.Settings; 30 import android.sysprop.TelephonyProperties; 31 import android.telephony.Annotation.CallState; 32 import android.telephony.CarrierConfigManager; 33 import android.telephony.PhoneStateListener; 34 import android.telephony.SubscriptionInfo; 35 import android.telephony.SubscriptionManager; 36 import android.telephony.TelephonyManager; 37 import android.telephony.data.ApnSetting; 38 import android.util.LocalLog; 39 import android.util.Pair; 40 41 import com.android.internal.telephony.GlobalSettingsHelper; 42 import com.android.internal.telephony.MultiSimSettingController; 43 import com.android.internal.telephony.Phone; 44 import com.android.internal.telephony.SubscriptionController; 45 import com.android.telephony.Rlog; 46 47 import java.io.FileDescriptor; 48 import java.io.PrintWriter; 49 import java.lang.annotation.Retention; 50 import java.lang.annotation.RetentionPolicy; 51 52 /** 53 * The class to hold different data enabled/disabled settings. Also it allows clients to register 54 * for overall data enabled setting changed event. 55 * @hide 56 */ 57 public class DataEnabledSettings { 58 59 private static final String LOG_TAG = "DataEnabledSettings"; 60 61 @Retention(RetentionPolicy.SOURCE) 62 @IntDef(prefix = {"REASON_"}, 63 value = { 64 REASON_REGISTERED, 65 REASON_INTERNAL_DATA_ENABLED, 66 REASON_USER_DATA_ENABLED, 67 REASON_POLICY_DATA_ENABLED, 68 REASON_DATA_ENABLED_BY_CARRIER, 69 REASON_PROVISIONED_CHANGED, 70 REASON_PROVISIONING_DATA_ENABLED_CHANGED, 71 REASON_OVERRIDE_RULE_CHANGED, 72 REASON_OVERRIDE_CONDITION_CHANGED, 73 REASON_THERMAL_DATA_ENABLED 74 }) 75 public @interface DataEnabledChangedReason {} 76 77 public static final int REASON_REGISTERED = 0; 78 79 public static final int REASON_INTERNAL_DATA_ENABLED = 1; 80 81 public static final int REASON_USER_DATA_ENABLED = 2; 82 83 public static final int REASON_POLICY_DATA_ENABLED = 3; 84 85 public static final int REASON_DATA_ENABLED_BY_CARRIER = 4; 86 87 public static final int REASON_PROVISIONED_CHANGED = 5; 88 89 public static final int REASON_PROVISIONING_DATA_ENABLED_CHANGED = 6; 90 91 public static final int REASON_OVERRIDE_RULE_CHANGED = 7; 92 93 public static final int REASON_OVERRIDE_CONDITION_CHANGED = 8; 94 95 public static final int REASON_THERMAL_DATA_ENABLED = 9; 96 97 /** 98 * responds to the setInternalDataEnabled call - used internally to turn off data. 99 * For example during emergency calls 100 */ 101 private boolean mInternalDataEnabled = true; 102 103 /** 104 * Flag indicating data allowed by network policy manager or not. 105 */ 106 private boolean mPolicyDataEnabled = true; 107 108 /** 109 * Indicate if metered APNs are enabled by the carrier. set false to block all the metered APNs 110 * from continuously sending requests, which causes undesired network load. 111 */ 112 private boolean mCarrierDataEnabled = true; 113 114 /** 115 * Flag indicating data allowed by Thermal service or not. 116 */ 117 private boolean mThermalDataEnabled = true; 118 119 /** 120 * Flag indicating whether data is allowed or not for the device. It can be disabled by 121 * user, carrier, policy or thermal 122 */ 123 private boolean mIsDataEnabled = false; 124 125 private final Phone mPhone; 126 127 private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 128 129 private ContentResolver mResolver = null; 130 131 private final RegistrantList mOverallDataEnabledChangedRegistrants = new RegistrantList(); 132 133 // TODO: Merge this with mOverallDataEnabledChangedRegistrants. In the future, notifying data 134 // enabled changed with APN types bitmask 135 private final RegistrantList mOverallDataEnabledOverrideChangedRegistrants = 136 new RegistrantList(); 137 138 private final LocalLog mSettingChangeLocalLog = new LocalLog(50); 139 140 private DataEnabledOverride mDataEnabledOverride; 141 142 private TelephonyManager mTelephonyManager; 143 144 // for msim, user data enabled setting depends on subId. 145 private final SubscriptionManager.OnSubscriptionsChangedListener 146 mOnSubscriptionsChangeListener = 147 new SubscriptionManager.OnSubscriptionsChangedListener() { 148 @Override 149 public void onSubscriptionsChanged() { 150 synchronized (this) { 151 if (mSubId != mPhone.getSubId()) { 152 log("onSubscriptionsChanged subId: " + mSubId + " to: " 153 + mPhone.getSubId()); 154 mSubId = mPhone.getSubId(); 155 mDataEnabledOverride = getDataEnabledOverride(); 156 updatePhoneStateListener(); 157 updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED); 158 mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled()); 159 } 160 } 161 } 162 }; 163 updatePhoneStateListener()164 private void updatePhoneStateListener() { 165 mTelephonyManager.listen(mPhoneStateListener, LISTEN_NONE); 166 if (SubscriptionManager.isUsableSubscriptionId(mSubId)) { 167 mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); 168 } 169 mTelephonyManager.listen(mPhoneStateListener, LISTEN_CALL_STATE); 170 } 171 172 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 173 @Override 174 public void onCallStateChanged(@CallState int state, String phoneNumber) { 175 updateDataEnabledAndNotify(REASON_OVERRIDE_CONDITION_CHANGED); 176 } 177 }; 178 179 @Override toString()180 public String toString() { 181 return "[mInternalDataEnabled=" + mInternalDataEnabled 182 + ", isUserDataEnabled=" + isUserDataEnabled() 183 + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled() 184 + ", mPolicyDataEnabled=" + mPolicyDataEnabled 185 + ", mCarrierDataEnabled=" + mCarrierDataEnabled 186 + ", mIsDataEnabled=" + mIsDataEnabled 187 + ", mThermalDataEnabled=" + mThermalDataEnabled 188 + ", " + mDataEnabledOverride 189 + "]"; 190 } 191 DataEnabledSettings(Phone phone)192 public DataEnabledSettings(Phone phone) { 193 mPhone = phone; 194 mResolver = mPhone.getContext().getContentResolver(); 195 SubscriptionManager subscriptionManager = (SubscriptionManager) mPhone.getContext() 196 .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 197 subscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); 198 mTelephonyManager = (TelephonyManager) mPhone.getContext() 199 .getSystemService(Context.TELEPHONY_SERVICE); 200 mDataEnabledOverride = getDataEnabledOverride(); 201 updateDataEnabled(); 202 } 203 getDataEnabledOverride()204 private DataEnabledOverride getDataEnabledOverride() { 205 return new DataEnabledOverride(SubscriptionController.getInstance() 206 .getDataEnabledOverrideRules(mPhone.getSubId())); 207 } 208 setInternalDataEnabled(boolean enabled)209 public synchronized void setInternalDataEnabled(boolean enabled) { 210 if (mInternalDataEnabled != enabled) { 211 localLog("InternalDataEnabled", enabled); 212 mInternalDataEnabled = enabled; 213 updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED); 214 } 215 } isInternalDataEnabled()216 public synchronized boolean isInternalDataEnabled() { 217 return mInternalDataEnabled; 218 } 219 setUserDataEnabled(boolean enabled)220 private synchronized void setUserDataEnabled(boolean enabled) { 221 // By default the change should propagate to the group. 222 setUserDataEnabled(enabled, true); 223 } 224 225 /** 226 * @param notifyMultiSimSettingController if setUserDataEnabled is already from propagating 227 * from MultiSimSettingController, don't notify MultiSimSettingController again. 228 * For example, if sub1 and sub2 are in the same group and user enables data for sub 229 * 1, sub 2 will also be enabled but with propagateToGroup = false. 230 */ setUserDataEnabled(boolean enabled, boolean notifyMultiSimSettingController)231 public synchronized void setUserDataEnabled(boolean enabled, 232 boolean notifyMultiSimSettingController) { 233 // Can't disable data for stand alone opportunistic subscription. 234 if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext()) && !enabled) return; 235 236 boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(), 237 Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0)); 238 if (changed) { 239 localLog("UserDataEnabled", enabled); 240 mPhone.notifyUserMobileDataStateChanged(enabled); 241 updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED); 242 if (notifyMultiSimSettingController) { 243 MultiSimSettingController.getInstance().notifyUserDataEnabled( 244 mPhone.getSubId(), enabled); 245 } 246 } 247 } 248 249 /** 250 * Policy control of data connection with reason 251 * @param reason the reason the data enable change is taking place 252 * @param enabled True if enabling the data, otherwise disabling. 253 */ setDataEnabled(@elephonyManager.DataEnabledReason int reason, boolean enabled)254 public synchronized void setDataEnabled(@TelephonyManager.DataEnabledReason int reason, 255 boolean enabled) { 256 switch (reason) { 257 case TelephonyManager.DATA_ENABLED_REASON_USER: 258 setUserDataEnabled(enabled); 259 break; 260 case TelephonyManager.DATA_ENABLED_REASON_CARRIER: 261 setCarrierDataEnabled(enabled); 262 break; 263 case TelephonyManager.DATA_ENABLED_REASON_POLICY: 264 setPolicyDataEnabled(enabled); 265 break; 266 case TelephonyManager.DATA_ENABLED_REASON_THERMAL: 267 setThermalDataEnabled(enabled); 268 break; 269 default: 270 log("Invalid data enable reason " + reason); 271 break; 272 } 273 } 274 isUserDataEnabled()275 public synchronized boolean isUserDataEnabled() { 276 // User data should always be true for opportunistic subscription. 277 if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext())) return true; 278 279 boolean defaultVal = TelephonyProperties.mobile_data().orElse(true); 280 281 return GlobalSettingsHelper.getBoolean(mPhone.getContext(), 282 Settings.Global.MOBILE_DATA, mPhone.getSubId(), defaultVal); 283 } 284 285 /** 286 * Set whether always allowing MMS data connection. 287 * 288 * @param alwaysAllow {@code true} if MMS data is always allowed. 289 * 290 * @return {@code false} if the setting is changed. 291 */ setAlwaysAllowMmsData(boolean alwaysAllow)292 public synchronized boolean setAlwaysAllowMmsData(boolean alwaysAllow) { 293 localLog("setAlwaysAllowMmsData", alwaysAllow); 294 mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow); 295 boolean changed = SubscriptionController.getInstance() 296 .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules()); 297 if (changed) { 298 updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED); 299 notifyDataEnabledOverrideChanged(); 300 } 301 302 return changed; 303 } 304 305 /** 306 * Set allowing mobile data during voice call. This is used for allowing data on the non-default 307 * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will 308 * not be able to use mobile data. By calling this API, data will be temporarily enabled on the 309 * non-default data SIM during the life cycle of the voice call. 310 * 311 * @param allow {@code true} if allowing using data during voice call, {@code false} if 312 * disallowed 313 * 314 * @return {@code true} if operation is successful. otherwise {@code false}. 315 */ setAllowDataDuringVoiceCall(boolean allow)316 public synchronized boolean setAllowDataDuringVoiceCall(boolean allow) { 317 localLog("setAllowDataDuringVoiceCall", allow); 318 if (allow == isDataAllowedInVoiceCall()) { 319 return true; 320 } 321 mDataEnabledOverride.setDataAllowedInVoiceCall(allow); 322 323 boolean changed = SubscriptionController.getInstance() 324 .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules()); 325 if (changed) { 326 updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED); 327 notifyDataEnabledOverrideChanged(); 328 } 329 330 return changed; 331 } 332 333 /** 334 * Check if data is allowed during voice call. 335 * 336 * @return {@code true} if data is allowed during voice call. 337 */ isDataAllowedInVoiceCall()338 public synchronized boolean isDataAllowedInVoiceCall() { 339 return mDataEnabledOverride.isDataAllowedInVoiceCall(); 340 } 341 isMmsAlwaysAllowed()342 public synchronized boolean isMmsAlwaysAllowed() { 343 return mDataEnabledOverride.isMmsAlwaysAllowed(); 344 } 345 setPolicyDataEnabled(boolean enabled)346 private synchronized void setPolicyDataEnabled(boolean enabled) { 347 if (mPolicyDataEnabled != enabled) { 348 localLog("PolicyDataEnabled", enabled); 349 mPolicyDataEnabled = enabled; 350 updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED); 351 } 352 } 353 isPolicyDataEnabled()354 public synchronized boolean isPolicyDataEnabled() { 355 return mPolicyDataEnabled; 356 } 357 setCarrierDataEnabled(boolean enabled)358 private synchronized void setCarrierDataEnabled(boolean enabled) { 359 if (mCarrierDataEnabled != enabled) { 360 localLog("CarrierDataEnabled", enabled); 361 mCarrierDataEnabled = enabled; 362 updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER); 363 } 364 } 365 isCarrierDataEnabled()366 public synchronized boolean isCarrierDataEnabled() { 367 return mCarrierDataEnabled; 368 } 369 setThermalDataEnabled(boolean enabled)370 private synchronized void setThermalDataEnabled(boolean enabled) { 371 if (mThermalDataEnabled != enabled) { 372 localLog("ThermalDataEnabled", enabled); 373 mThermalDataEnabled = enabled; 374 updateDataEnabledAndNotify(REASON_THERMAL_DATA_ENABLED); 375 } 376 } 377 isThermalDataEnabled()378 public synchronized boolean isThermalDataEnabled() { 379 return mThermalDataEnabled; 380 } 381 updateProvisionedChanged()382 public synchronized void updateProvisionedChanged() { 383 updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED); 384 } 385 updateProvisioningDataEnabled()386 public synchronized void updateProvisioningDataEnabled() { 387 updateDataEnabledAndNotify(REASON_PROVISIONING_DATA_ENABLED_CHANGED); 388 } 389 isDataEnabled()390 public synchronized boolean isDataEnabled() { 391 return mIsDataEnabled; 392 } 393 394 /** 395 * Check if data is enabled for a specific reason {@@TelephonyManager.DataEnabledReason} 396 * 397 * @return {@code true} if the overall data is enabled; {@code false} if not. 398 */ isDataEnabledForReason( @elephonyManager.DataEnabledReason int reason)399 public synchronized boolean isDataEnabledForReason( 400 @TelephonyManager.DataEnabledReason int reason) { 401 switch (reason) { 402 case TelephonyManager.DATA_ENABLED_REASON_USER: 403 return isUserDataEnabled(); 404 case TelephonyManager.DATA_ENABLED_REASON_CARRIER: 405 return isCarrierDataEnabled(); 406 case TelephonyManager.DATA_ENABLED_REASON_POLICY: 407 return isPolicyDataEnabled(); 408 case TelephonyManager.DATA_ENABLED_REASON_THERMAL: 409 return isThermalDataEnabled(); 410 default: 411 return false; 412 } 413 } 414 updateDataEnabledAndNotify(int reason)415 private synchronized void updateDataEnabledAndNotify(int reason) { 416 boolean prevDataEnabled = mIsDataEnabled; 417 418 updateDataEnabled(); 419 420 if (prevDataEnabled != mIsDataEnabled) { 421 notifyDataEnabledChanged(!prevDataEnabled, reason); 422 } 423 } 424 updateDataEnabled()425 private synchronized void updateDataEnabled() { 426 if (isProvisioning()) { 427 mIsDataEnabled = isProvisioningDataEnabled(); 428 } else { 429 mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride 430 .shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL)) 431 && mPolicyDataEnabled && mCarrierDataEnabled && mThermalDataEnabled; 432 } 433 } 434 isProvisioning()435 public boolean isProvisioning() { 436 return Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0; 437 } 438 /** 439 * In provisioning, we might want to have enable mobile data during provisioning. It depends 440 * on value of Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED which is set by 441 * setupwizard. It only matters if it's in provisioning stage. 442 * @return whether we are enabling userData during provisioning stage. 443 */ isProvisioningDataEnabled()444 public boolean isProvisioningDataEnabled() { 445 final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata", 446 "false"); 447 boolean retVal = "true".equalsIgnoreCase(prov_property); 448 449 final int prov_mobile_data = Settings.Global.getInt(mResolver, 450 Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 451 retVal ? 1 : 0); 452 retVal = prov_mobile_data != 0; 453 log("getDataEnabled during provisioning retVal=" + retVal + " - (" + prov_property 454 + ", " + prov_mobile_data + ")"); 455 456 return retVal; 457 } 458 setDataRoamingEnabled(boolean enabled)459 public synchronized void setDataRoamingEnabled(boolean enabled) { 460 // will trigger handleDataOnRoamingChange() through observer 461 boolean changed = GlobalSettingsHelper.setBoolean(mPhone.getContext(), 462 Settings.Global.DATA_ROAMING, mPhone.getSubId(), enabled); 463 464 if (changed) { 465 localLog("setDataRoamingEnabled", enabled); 466 MultiSimSettingController.getInstance().notifyRoamingDataEnabled(mPhone.getSubId(), 467 enabled); 468 } 469 } 470 471 /** 472 * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value. 473 */ getDataRoamingEnabled()474 public synchronized boolean getDataRoamingEnabled() { 475 return GlobalSettingsHelper.getBoolean(mPhone.getContext(), 476 Settings.Global.DATA_ROAMING, mPhone.getSubId(), getDefaultDataRoamingEnabled()); 477 } 478 479 /** 480 * get default values for {@link Settings.Global#DATA_ROAMING} 481 * return {@code true} if either 482 * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} or 483 * system property ro.com.android.dataroaming is set to true. otherwise return {@code false} 484 */ getDefaultDataRoamingEnabled()485 public synchronized boolean getDefaultDataRoamingEnabled() { 486 final CarrierConfigManager configMgr = (CarrierConfigManager) 487 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 488 boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get( 489 "ro.com.android.dataroaming", "false")); 490 isDataRoamingEnabled |= configMgr.getConfigForSubId(mPhone.getSubId()).getBoolean( 491 CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL); 492 return isDataRoamingEnabled; 493 } 494 notifyDataEnabledChanged(boolean enabled, int reason)495 private void notifyDataEnabledChanged(boolean enabled, int reason) { 496 mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason)); 497 mPhone.notifyDataEnabled(enabled, reason); 498 } 499 registerForDataEnabledChanged(Handler h, int what, Object obj)500 public void registerForDataEnabledChanged(Handler h, int what, Object obj) { 501 mOverallDataEnabledChangedRegistrants.addUnique(h, what, obj); 502 notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED); 503 } 504 unregisterForDataEnabledChanged(Handler h)505 public void unregisterForDataEnabledChanged(Handler h) { 506 mOverallDataEnabledChangedRegistrants.remove(h); 507 } 508 notifyDataEnabledOverrideChanged()509 private void notifyDataEnabledOverrideChanged() { 510 mOverallDataEnabledOverrideChangedRegistrants.notifyRegistrants(); 511 } 512 513 /** 514 * Register for data enabled override changed event. 515 * 516 * @param h The handler 517 * @param what The event 518 */ registerForDataEnabledOverrideChanged(Handler h, int what)519 public void registerForDataEnabledOverrideChanged(Handler h, int what) { 520 mOverallDataEnabledOverrideChangedRegistrants.addUnique(h, what, null); 521 notifyDataEnabledOverrideChanged(); 522 } 523 524 /** 525 * Unregistered for data enabled override changed event. 526 * 527 * @param h The handler 528 */ unregisterForDataEnabledOverrideChanged(Handler h)529 public void unregisterForDataEnabledOverrideChanged(Handler h) { 530 mOverallDataEnabledOverrideChangedRegistrants.remove(h); 531 } 532 isStandAloneOpportunistic(int subId, Context context)533 private static boolean isStandAloneOpportunistic(int subId, Context context) { 534 SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo( 535 subId, context.getOpPackageName(), context.getAttributionTag()); 536 return (info != null) && info.isOpportunistic() && info.getGroupUuid() == null; 537 } 538 isDataEnabled(int apnType)539 public synchronized boolean isDataEnabled(int apnType) { 540 if (isProvisioning()) { 541 return isProvisioningDataEnabled(); 542 } else { 543 boolean userDataEnabled = isUserDataEnabled(); 544 // Check if we should temporarily enable data in certain conditions. 545 boolean isDataEnabledOverridden = mDataEnabledOverride 546 .shouldOverrideDataEnabledSettings(mPhone, apnType); 547 548 return (mInternalDataEnabled && mPolicyDataEnabled && mCarrierDataEnabled 549 && mThermalDataEnabled && (userDataEnabled || isDataEnabledOverridden)); 550 } 551 } 552 log(String s)553 private void log(String s) { 554 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 555 } 556 localLog(String name, boolean value)557 private void localLog(String name, boolean value) { 558 mSettingChangeLocalLog.log(name + " change to " + value); 559 } 560 dump(FileDescriptor fd, PrintWriter pw, String[] args)561 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 562 pw.println(" DataEnabledSettings="); 563 mSettingChangeLocalLog.dump(fd, pw, args); 564 } 565 } 566