blob: f8365bbef50931b148257ba5490a7d35ca0417dc [file] [log] [blame]
/*
* Copyright 2021 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.data;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.sysprop.TelephonyProperties;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.MobileDataPolicy;
import android.telephony.TelephonyRegistryManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.ApnType;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import com.android.internal.telephony.GlobalSettingsHelper;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SettingsObserver;
import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
* DataSettingsManager maintains the data related settings, for example, data enabled settings,
* data roaming settings, etc...
*/
public class DataSettingsManager extends Handler {
/** Invalid mobile data policy **/
private static final int INVALID_MOBILE_DATA_POLICY = -1;
/** Event for call state changed. */
private static final int EVENT_CALL_STATE_CHANGED = 2;
/** Event for subscriptions updated. */
private static final int EVENT_SUBSCRIPTIONS_CHANGED = 4;
/** Event for set data enabled for reason. */
private static final int EVENT_SET_DATA_ENABLED_FOR_REASON = 5;
/** Event for set data roaming enabled. */
private static final int EVENT_SET_DATA_ROAMING_ENABLED = 6;
/** Event for set mobile data policy. */
private static final int EVENT_SET_MOBILE_DATA_POLICY = 7;
/** Event for device provisioned changed. */
private static final int EVENT_PROVISIONED_CHANGED = 9;
/** Event for provisioning data enabled setting changed. */
private static final int EVENT_PROVISIONING_DATA_ENABLED_CHANGED = 10;
/** Event for initializing DataSettingsManager. */
private static final int EVENT_INITIALIZE = 11;
private final Phone mPhone;
private final ContentResolver mResolver;
private final SettingsObserver mSettingsObserver;
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
private Set<Integer> mEnabledMobileDataPolicy = new HashSet<>();
private int mSubId;
/** Data config manager */
private final @NonNull DataConfigManager mDataConfigManager;
/** Data settings manager callbacks. */
private final @NonNull Set<DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
new ArraySet<>();
/** Mapping of {@link TelephonyManager.DataEnabledReason} to data enabled values. */
private final Map<Integer, Boolean> mDataEnabledSettings = new ArrayMap<>();
/**
* Flag indicating whether data is allowed or not for the device.
* It can be disabled by user, carrier, policy or thermal.
*/
private boolean mIsDataEnabled;
/**
* Used to indicate that the initial value for mIsDataEnabled was set.
* Prevent race condition where the initial value might be incorrect.
*/
private boolean mInitialized = false;
/**
* Data settings manager callback. This should be only used by {@link DataNetworkController}.
*/
public static class DataSettingsManagerCallback extends DataCallback {
/**
* Constructor
*
* @param executor The executor of the callback.
*/
public DataSettingsManagerCallback(@NonNull @CallbackExecutor Executor executor) {
super(executor);
}
/**
* Called when user data enabled state changed.
*
* @param enabled {@code true} indicates user mobile data is enabled.
* @param callingPackage The package that changed the data enabled state.
*/
public void onUserDataEnabledChanged(boolean enabled, @NonNull String callingPackage) {}
/**
* Called when overall data enabled state changed.
*
* @param enabled {@code true} indicates mobile data is enabled.
* @param reason {@link TelephonyManager.DataEnabledChangedReason} indicating the reason why
* mobile data enabled changed.
* @param callingPackage The package that changed the data enabled state.
*/
public void onDataEnabledChanged(boolean enabled,
@TelephonyManager.DataEnabledChangedReason int reason,
@NonNull String callingPackage) {}
/**
* Called when data enabled override changed.
*
* @param enabled {@code true} indicates data enabled override is enabled.
* @param policy {@link TelephonyManager.MobileDataPolicy} indicating the policy that was
* enabled or disabled.
*/
public void onDataEnabledOverrideChanged(boolean enabled,
@TelephonyManager.MobileDataPolicy int policy) {}
/**
* Called when data roaming enabled state changed.
*
* @param enabled {@code true} indicates data roaming is enabled.
*/
public void onDataRoamingEnabledChanged(boolean enabled) {}
}
/**
* Constructor
*
* @param phone The phone instance.
* @param dataNetworkController Data network controller.
* @param looper The looper to be used by the handler. Currently the handler thread is the
* phone process's main thread.
* @param callback Data settings manager callback.
*/
public DataSettingsManager(@NonNull Phone phone,
@NonNull DataNetworkController dataNetworkController, @NonNull Looper looper,
@NonNull DataSettingsManagerCallback callback) {
super(looper);
mPhone = phone;
mLogTag = "DSMGR-" + mPhone.getPhoneId();
log("DataSettingsManager created.");
mSubId = mPhone.getSubId();
mResolver = mPhone.getContext().getContentResolver();
registerCallback(callback);
mDataConfigManager = dataNetworkController.getDataConfigManager();
refreshEnabledMobileDataPolicy();
mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_POLICY, true);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_CARRIER, true);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_THERMAL, true);
// Instead of calling onInitialize directly from the constructor, send the event.
// The reason is that getImsPhone is null when we are still in the constructor here.
sendEmptyMessage(EVENT_INITIALIZE);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_CALL_STATE_CHANGED: {
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
break;
}
case EVENT_SUBSCRIPTIONS_CHANGED: {
mSubId = (int) msg.obj;
refreshEnabledMobileDataPolicy();
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER);
mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
break;
}
case EVENT_SET_DATA_ENABLED_FOR_REASON: {
String callingPackage = (String) msg.obj;
boolean enabled = msg.arg2 == 1;
switch (msg.arg1) {
case TelephonyManager.DATA_ENABLED_REASON_USER:
setUserDataEnabled(enabled, callingPackage);
break;
case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
setCarrierDataEnabled(enabled, callingPackage);
break;
case TelephonyManager.DATA_ENABLED_REASON_POLICY:
setPolicyDataEnabled(enabled, callingPackage);
break;
case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
setThermalDataEnabled(enabled, callingPackage);
break;
default:
log("Cannot set data enabled for reason: "
+ dataEnabledChangedReasonToString(msg.arg1));
break;
}
break;
}
case EVENT_SET_DATA_ROAMING_ENABLED: {
boolean enabled = (boolean) msg.obj;
setDataRoamingEnabledInternal(enabled);
setDataRoamingFromUserAction();
break;
}
case EVENT_SET_MOBILE_DATA_POLICY: {
int mobileDataPolicy = msg.arg1;
boolean enable = msg.arg2 == 1;
onSetMobileDataPolicy(mobileDataPolicy, enable);
break;
}
case EVENT_PROVISIONED_CHANGED:
case EVENT_PROVISIONING_DATA_ENABLED_CHANGED: {
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_UNKNOWN);
break;
}
case EVENT_INITIALIZE: {
onInitialize();
break;
}
default:
loge("Unknown msg.what: " + msg.what);
}
}
/**
* Called when needed to register for all events that data network controller is interested.
*/
private void onInitialize() {
mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
@Override
public void onCarrierConfigChanged() {
if (mDataConfigManager.isConfigCarrierSpecific()) {
setDefaultDataRoamingEnabled();
}
}
});
mSettingsObserver.observe(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
EVENT_PROVISIONED_CHANGED);
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
EVENT_PROVISIONING_DATA_ENABLED_CHANGED);
mPhone.getCallTracker().registerForVoiceCallStarted(this, EVENT_CALL_STATE_CHANGED, null);
mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_CALL_STATE_CHANGED, null);
if (mPhone.getImsPhone() != null) {
mPhone.getImsPhone().getCallTracker().registerForVoiceCallStarted(
this, EVENT_CALL_STATE_CHANGED, null);
mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
this, EVENT_CALL_STATE_CHANGED, null);
}
mPhone.getContext().getSystemService(TelephonyRegistryManager.class)
.addOnSubscriptionsChangedListener(new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
if (mSubId != mPhone.getSubId()) {
log("onSubscriptionsChanged: " + mSubId + " to " + mPhone.getSubId());
obtainMessage(EVENT_SUBSCRIPTIONS_CHANGED, mPhone.getSubId())
.sendToTarget();
}
}
}, this::post);
// some overall mobile data override policy depend on whether DDS is user data enabled.
for (Phone phone : PhoneFactory.getPhones()) {
if (phone.getPhoneId() != mPhone.getPhoneId()) {
phone.getDataSettingsManager().registerCallback(new DataSettingsManagerCallback(
this::post) {
@Override
public void onUserDataEnabledChanged(boolean enabled,
@NonNull String callingPackage) {
log("phone" + phone.getPhoneId() + " onUserDataEnabledChanged "
+ enabled + " by " + callingPackage
+ ", reevaluating mobile data policies");
DataSettingsManager.this.updateDataEnabledAndNotify(
TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
}
});
}
}
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_UNKNOWN);
}
/**
* Enable or disable data for a specific {@link TelephonyManager.DataEnabledReason}.
* @param reason The reason the data enabled change is taking place.
* @param enabled {@code true} to enable data for the given reason and {@code false} to disable.
* @param callingPackage The package that changed the data enabled state.
*/
public void setDataEnabled(@TelephonyManager.DataEnabledReason int reason, boolean enabled,
String callingPackage) {
obtainMessage(EVENT_SET_DATA_ENABLED_FOR_REASON, reason, enabled ? 1 : 0, callingPackage)
.sendToTarget();
}
/**
* Check whether the data is enabled for a specific {@link TelephonyManager.DataEnabledReason}.
* @return {@code true} if data is enabled for the given reason and {@code false} otherwise.
*/
public boolean isDataEnabledForReason(@TelephonyManager.DataEnabledReason int reason) {
if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) {
return isUserDataEnabled();
} else {
return mDataEnabledSettings.get(reason);
}
}
private void updateDataEnabledAndNotify(@TelephonyManager.DataEnabledChangedReason int reason) {
updateDataEnabledAndNotify(reason, mPhone.getContext().getOpPackageName());
}
private void updateDataEnabledAndNotify(@TelephonyManager.DataEnabledChangedReason int reason,
@NonNull String callingPackage) {
boolean prevDataEnabled = mIsDataEnabled;
mIsDataEnabled = isDataEnabled(ApnSetting.TYPE_ALL);
log("mIsDataEnabled=" + mIsDataEnabled + ", prevDataEnabled=" + prevDataEnabled);
if (!mInitialized || prevDataEnabled != mIsDataEnabled) {
if (!mInitialized) mInitialized = true;
notifyDataEnabledChanged(mIsDataEnabled, reason, callingPackage);
}
}
/**
* Check whether the user data is enabled when the device is in the provisioning stage.
* In provisioning, we might want to enable mobile data depending on the value of
* Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, which is set by setupwizard.
* @return {@code true} if user data is enabled when provisioning and {@code false} otherwise.
*/
private boolean isProvisioningDataEnabled() {
final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata",
"false");
boolean retVal = "true".equalsIgnoreCase(prov_property);
final int prov_mobile_data = Settings.Global.getInt(mResolver,
Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
retVal ? 1 : 0);
retVal = prov_mobile_data != 0;
log("getDataEnabled during provisioning retVal=" + retVal + " - (" + prov_property
+ ", " + prov_mobile_data + ")");
return retVal;
}
/**
* Check whether the overall data is enabled for the device. Note that this value will only
* be accurate if {@link #isDataInitialized} is {@code true}.
* @return {@code true} if the overall data is enabled and {@code false} otherwise.
*/
public boolean isDataEnabled() {
return mIsDataEnabled;
}
/**
* Check whether data enabled value has been initialized. If this is {@code false}, then
* {@link #isDataEnabled} is not guaranteed to be accurate. Once data is initialized,
* {@link DataSettingsManagerCallback#onDataEnabledChanged} will be invoked with reason
* {@link TelephonyManager#DATA_ENABLED_REASON_UNKNOWN}.
* @return {@code true} if the data enabled value is initialized and {@code false} otherwise.
*/
public boolean isDataInitialized() {
// TODO: Create a new DATA_ENABLED_REASON_INITIALIZED for initial value broadcast
return mInitialized;
}
/**
* Check whether the overall data is enabled for the device for the given APN type.
* @param apnType A single APN type to check data enabled for.
* @return {@code true} if the overall data is enabled for the APN and {@code false} otherwise.
*/
public boolean isDataEnabled(@ApnType int apnType) {
if (Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
return isProvisioningDataEnabled();
} else {
boolean userDataEnabled = isUserDataEnabled();
// Check if we should temporarily enable data based on mobile data policy.
boolean isDataEnabledOverridden = isDataEnabledOverriddenForApn(apnType);
return ((userDataEnabled || isDataEnabledOverridden)
&& mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_POLICY)
&& mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_CARRIER)
&& mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_THERMAL));
}
}
private boolean isStandAloneOpportunistic(int subId) {
SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
.getSubscriptionInfoInternal(subId);
return subInfo != null && subInfo.isOpportunistic()
&& TextUtils.isEmpty(subInfo.getGroupUuid());
}
/**
* Enable or disable user data.
* @param enabled {@code true} to enable user data and {@code false} to disable.
* @param callingPackage The package that changed the data enabled state.
*/
private void setUserDataEnabled(boolean enabled, String callingPackage) {
// Can't disable data for stand alone opportunistic subscription.
if (isStandAloneOpportunistic(mSubId) && !enabled) return;
boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(),
Settings.Global.MOBILE_DATA, mSubId, (enabled ? 1 : 0));
log("Set user data enabled to " + enabled + ", changed=" + changed + ", callingPackage="
+ callingPackage);
if (changed) {
logl("UserDataEnabled changed to " + enabled);
mPhone.notifyUserMobileDataStateChanged(enabled);
mDataSettingsManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onUserDataEnabledChanged(enabled, callingPackage)));
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER, callingPackage);
}
}
/**
* Check whether user data is enabled for the device.
* @return {@code true} if user data is enabled and {@code false} otherwise.
*/
private boolean isUserDataEnabled() {
if (Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
return isProvisioningDataEnabled();
}
// User data should always be true for opportunistic subscription.
if (isStandAloneOpportunistic(mSubId)) return true;
boolean defaultVal = TelephonyProperties.mobile_data().orElse(true);
return GlobalSettingsHelper.getBoolean(mPhone.getContext(),
Settings.Global.MOBILE_DATA, mSubId, defaultVal);
}
/**
* Enable or disable policy data.
* @param enabled {@code true} to enable policy data and {@code false} to disable.
* @param callingPackage The package that changed the data enabled state.
*/
private void setPolicyDataEnabled(boolean enabled, String callingPackage) {
if (mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_POLICY) != enabled) {
logl("PolicyDataEnabled changed to " + enabled + ", callingPackage=" + callingPackage);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_POLICY, enabled);
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_POLICY, callingPackage);
}
}
/**
* Enable or disable carrier data.
* @param enabled {@code true} to enable carrier data and {@code false} to disable.
* @param callingPackage The package that changed the data enabled state.
*/
private void setCarrierDataEnabled(boolean enabled, String callingPackage) {
if (mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_CARRIER) != enabled) {
logl("CarrierDataEnabled changed to " + enabled + ", callingPackage=" + callingPackage);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled);
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
callingPackage);
}
}
/**
* Enable or disable thermal data.
* @param enabled {@code true} to enable thermal data and {@code false} to disable.
* @param callingPackage The package that changed the data enabled state.
*/
private void setThermalDataEnabled(boolean enabled, String callingPackage) {
if (mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_THERMAL) != enabled) {
logl("ThermalDataEnabled changed to " + enabled + ", callingPackage=" + callingPackage);
mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_THERMAL, enabled);
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
callingPackage);
}
}
/**
* Enable or disable data roaming from user settings.
* @param enabled {@code true} to enable data roaming and {@code false} to disable.
*/
public void setDataRoamingEnabled(boolean enabled) {
obtainMessage(EVENT_SET_DATA_ROAMING_ENABLED, enabled).sendToTarget();
}
/**
* Enable or disable data roaming.
* @param enabled {@code true} to enable data roaming and {@code false} to disable.
*/
private void setDataRoamingEnabledInternal(boolean enabled) {
// Will trigger handleDataOnRoamingChange() through observer
boolean changed = GlobalSettingsHelper.setBoolean(mPhone.getContext(),
Settings.Global.DATA_ROAMING, mSubId, enabled);
if (changed) {
logl("DataRoamingEnabled changed to " + enabled);
mDataSettingsManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onDataRoamingEnabledChanged(enabled)));
}
}
/**
* Check whether data roaming is enabled for the device based on the current
* {@link Settings.Global#DATA_ROAMING} value.
* @return {@code true} if data roaming is enabled and {@code false} otherwise.
*/
public boolean isDataRoamingEnabled() {
return GlobalSettingsHelper.getBoolean(mPhone.getContext(),
Settings.Global.DATA_ROAMING, mSubId, isDefaultDataRoamingEnabled());
}
/**
* Check whether data roaming is enabled by default.
* This is true if {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL}
* or the system property "ro.com.android.dataroaming" are true.
* @return {@code true} if data roaming is enabled by default and {@code false} otherwise.
*/
public boolean isDefaultDataRoamingEnabled() {
return "true".equalsIgnoreCase(SystemProperties.get("ro.com.android.dataroaming", "false"))
|| mPhone.getDataNetworkController().getDataConfigManager()
.isDataRoamingEnabledByDefault();
}
/**
* Set default value for {@link android.provider.Settings.Global#DATA_ROAMING} if the user
* has not manually set the value. The default value is {@link #isDefaultDataRoamingEnabled()}.
*/
public void setDefaultDataRoamingEnabled() {
// If the user has not manually set the value, use the default value.
if (!isDataRoamingFromUserAction()) {
setDataRoamingEnabledInternal(isDefaultDataRoamingEnabled());
}
}
/**
* Get whether the user has manually enabled or disabled data roaming from settings for the
* current subscription.
* @return {@code true} if the user has manually enabled data roaming for the current
* subscription and {@code false} if they have not.
*/
private boolean isDataRoamingFromUserAction() {
String key = Phone.DATA_ROAMING_IS_USER_SETTING_KEY + mPhone.getSubId();
final SharedPreferences sp =
PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
// Set the default roaming from user action value if the preference doesn't exist
if (!sp.contains(key)) {
if (sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
log("Reusing previous roaming from user action value for backwards compatibility.");
sp.edit().putBoolean(key, true).commit();
} else {
log("Clearing roaming from user action value for new or upgrading devices.");
sp.edit().putBoolean(key, false).commit();
}
}
boolean isUserSetting = sp.getBoolean(key, true);
log("isDataRoamingFromUserAction: key=" + key + ", isUserSetting=" + isUserSetting);
return isUserSetting;
}
/**
* Indicate that the user has manually enabled or disabled the data roaming value from settings.
* If the user has not manually set the data roaming value, the default value from
* {@link #isDefaultDataRoamingEnabled()} will continue to be used.
*/
private void setDataRoamingFromUserAction() {
String key = Phone.DATA_ROAMING_IS_USER_SETTING_KEY + mPhone.getSubId();
log("setDataRoamingFromUserAction: key=" + key);
final SharedPreferences.Editor sp =
PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()).edit();
sp.putBoolean(key, true).commit();
}
/** Refresh the enabled mobile data policies from Telephony database */
private void refreshEnabledMobileDataPolicy() {
SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
.getSubscriptionInfoInternal(mSubId);
if (subInfo != null) {
mEnabledMobileDataPolicy = getMobileDataPolicyEnabled(
subInfo.getEnabledMobileDataPolicies());
}
}
/**
* @return {@code true} If the mobile data policy is enabled
*/
public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int mobileDataPolicy) {
return mEnabledMobileDataPolicy.contains(mobileDataPolicy);
}
/**
* Set mobile data policy enabled status
* @param mobileDataPolicy The mobile data policy to set
* @param enable {@code true} to enable the policy; {@code false} to disable.
*/
public void setMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) {
obtainMessage(EVENT_SET_MOBILE_DATA_POLICY, mobileDataPolicy, enable ? 1 : 0)
.sendToTarget();
}
/**
* Store data mobile policy to Telephony database.
*
* @param mobileDataPolicy The mobile data policy that overrides user data enabled setting.
* @param enable {@code true} to enable the policy; {@code false} to remove the policy.
*/
private void onSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) {
if (enable == isMobileDataPolicyEnabled(mobileDataPolicy)) {
return;
}
metricsRecordSetMobileDataPolicy(mobileDataPolicy);
if (enable) {
mEnabledMobileDataPolicy.add(mobileDataPolicy);
} else {
mEnabledMobileDataPolicy.remove(mobileDataPolicy);
}
String enabledMobileDataPolicies = mEnabledMobileDataPolicy.stream().map(String::valueOf)
.collect(Collectors.joining(","));
SubscriptionManagerService.getInstance().setEnabledMobileDataPolicies(mSubId,
enabledMobileDataPolicies);
logl(TelephonyUtils.mobileDataPolicyToString(mobileDataPolicy) + " changed to "
+ enable);
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
notifyDataEnabledOverrideChanged(enable, mobileDataPolicy);
}
/**
* Record the number of times a mobile data policy is toggled to metrics.
* @param mobileDataPolicy The mobile data policy that's toggled
*/
private void metricsRecordSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy) {
if (mobileDataPolicy == TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) {
DeviceTelephonyPropertiesStats.recordAutoDataSwitchFeatureToggle();
}
}
/**
* Check whether data stall recovery on bad network is enabled.
* @return {@code true} if data stall recovery is enabled and {@code false} otherwise.
*/
public boolean isRecoveryOnBadNetworkEnabled() {
return Settings.Global.getInt(mResolver,
Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1) == 1;
}
private void notifyDataEnabledChanged(boolean enabled,
@TelephonyManager.DataEnabledChangedReason int reason, @NonNull String callingPackage) {
logl("notifyDataEnabledChanged: enabled=" + enabled + ", reason="
+ dataEnabledChangedReasonToString(reason) + ", callingPackage=" + callingPackage);
mDataSettingsManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onDataEnabledChanged(enabled, reason, callingPackage)));
mPhone.notifyDataEnabled(enabled, reason);
}
private void notifyDataEnabledOverrideChanged(boolean enabled,
@TelephonyManager.MobileDataPolicy int policy) {
logl("notifyDataEnabledOverrideChanged: enabled=" + enabled);
mDataSettingsManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
() -> callback.onDataEnabledOverrideChanged(enabled, policy)));
}
/**
* Return the parsed mobile data policies.
*
* @param policies New mobile data policies in String format.
* @return A Set of parsed mobile data policies.
*/
public @NonNull @MobileDataPolicy Set<Integer> getMobileDataPolicyEnabled(
@NonNull String policies) {
Set<Integer> mobileDataPolicies = new HashSet<>();
String[] rulesString = policies.trim().split("\\s*,\\s*");
for (String rule : rulesString) {
if (!TextUtils.isEmpty(rule)) {
int parsedDataPolicy = parsePolicyFrom(rule);
if (parsedDataPolicy != INVALID_MOBILE_DATA_POLICY) {
mobileDataPolicies.add(parsedDataPolicy);
}
}
}
return mobileDataPolicies;
}
/**
* Parse a mobile data policy retrieved from Telephony db.
* If the policy is in legacy format, convert it into the corresponding mobile data policy.
*
* @param policy Mobile data policy to be parsed from.
* @return Parsed mobile data policy. {@link #INVALID_MOBILE_DATA_POLICY} if string can't be
* parsed into a mobile data policy.
*/
private @MobileDataPolicy int parsePolicyFrom(@NonNull String policy) {
int dataPolicy;
try {
// parse as new override policy
dataPolicy = Integer.parseInt(policy);
} catch (NumberFormatException e) {
dataPolicy = INVALID_MOBILE_DATA_POLICY;
loge("parsePolicyFrom: invalid mobile data policy format: " + policy);
}
return dataPolicy;
}
/**
* Check if data enabled is temporarily overridden in certain conditions.
*
* @param apnType The APN type to check.
* @return {@code true} if data enabled should be overridden.
*/
private boolean isDataEnabledOverriddenForApn(@ApnType int apnType) {
boolean overridden = false;
// mobile data policy : MMS always allowed
if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) {
overridden = apnType == ApnSetting.TYPE_MMS;
}
boolean isNonDds = mPhone.getSubId() != SubscriptionManagerService.getInstance()
.getDefaultDataSubId();
// mobile data policy : data during call
if (isMobileDataPolicyEnabled(TelephonyManager
.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL)) {
overridden = overridden || isNonDds && mPhone.getState() != PhoneConstants.State.IDLE;
}
// mobile data policy : auto data switch
if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) {
// check user enabled data on the default data phone
Phone defaultDataPhone = PhoneFactory.getPhone(SubscriptionManagerService.getInstance()
.getPhoneId(SubscriptionManagerService.getInstance()
.getDefaultDataSubId()));
if (defaultDataPhone == null) {
loge("isDataEnabledOverriddenForApn: unexpected defaultDataPhone is null");
} else {
overridden = overridden || isNonDds && defaultDataPhone.isUserDataEnabled();
}
}
return overridden;
}
/**
* Register the callback for receiving information from {@link DataSettingsManager}.
*
* @param callback The callback.
*/
public void registerCallback(@NonNull DataSettingsManagerCallback callback) {
mDataSettingsManagerCallbacks.add(callback);
}
/**
* Unregister the callback for receiving information from {@link DataSettingsManager}.
*
* @param callback The callback.
*/
public void unregisterCallback(@NonNull DataSettingsManagerCallback callback) {
mDataSettingsManagerCallbacks.remove(callback);
}
private static String dataEnabledChangedReasonToString(
@TelephonyManager.DataEnabledChangedReason int reason) {
switch (reason) {
case TelephonyManager.DATA_ENABLED_REASON_USER:
return "USER";
case TelephonyManager.DATA_ENABLED_REASON_POLICY:
return "POLICY";
case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
return "CARRIER";
case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
return "THERMAL";
case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE:
return "OVERRIDE";
default:
return "UNKNOWN";
}
}
@Override
public String toString() {
return "[isUserDataEnabled=" + isUserDataEnabled()
+ ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
+ ", mIsDataEnabled=" + mIsDataEnabled
+ ", mDataEnabledSettings=" + mDataEnabledSettings
+ ", mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream()
.map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(","))
+ "]";
}
/**
* Log debug messages.
* @param s debug messages
*/
private void log(@NonNull String s) {
Rlog.d(mLogTag, s);
}
/**
* Log error messages.
* @param s error messages
*/
private void loge(@NonNull String s) {
Rlog.e(mLogTag, s);
}
/**
* Log debug messages and also log into the local log.
* @param s debug messages
*/
private void logl(@NonNull String s) {
log(s);
mLocalLog.log(s);
}
/**
* Dump the state of DataSettingsManager
*
* @param fd File descriptor
* @param printWriter Print writer
* @param args Arguments
*/
public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println(DataSettingsManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":");
pw.increaseIndent();
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("isDataEnabled(internet)=" + isDataEnabled(ApnSetting.TYPE_DEFAULT));
pw.println("isDataEnabled(mms)=" + isDataEnabled(ApnSetting.TYPE_MMS));
pw.println("isUserDataEnabled=" + isUserDataEnabled());
pw.println("isDataRoamingEnabled=" + isDataRoamingEnabled());
pw.println("isDefaultDataRoamingEnabled=" + isDefaultDataRoamingEnabled());
pw.println("isDataRoamingFromUserAction=" + isDataRoamingFromUserAction());
pw.println("device_provisioned=" + Settings.Global.getInt(
mResolver, Settings.Global.DEVICE_PROVISIONED, 0));
pw.println("isProvisioningDataEnabled=" + isProvisioningDataEnabled());
pw.println("data_stall_recovery_on_bad_network=" + Settings.Global.getInt(
mResolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1));
pw.println("mDataEnabledSettings=" + mDataEnabledSettings.entrySet().stream()
.map(entry ->
dataEnabledChangedReasonToString(entry.getKey()) + "=" + entry.getValue())
.collect(Collectors.joining(", ")));
pw.println("mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream()
.map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(",")));
pw.println("Local logs:");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
pw.decreaseIndent();
pw.decreaseIndent();
}
}