Refresh the preferred APN after "Reset to default"
Previously, ApnSettings will updated the preferred APN selection when data connection changes, but this is not the source of truth. Observe the preferred APN directly to fix. Fix: 257316932 Test: manual - on ApnSettings Test: unit test Change-Id: Ie323316ab8f7fa63edf5cf90633bcdd4486728c4
This commit is contained in:
@@ -36,7 +36,7 @@ import android.util.Log;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.ResetNetworkRequest;
|
import com.android.settings.ResetNetworkRequest;
|
||||||
import com.android.settings.network.apn.ApnSettings;
|
import com.android.settings.network.apn.PreferredApnRepository;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -204,7 +204,7 @@ public class ResetNetworkOperationBuilder {
|
|||||||
Runnable runnable = () -> {
|
Runnable runnable = () -> {
|
||||||
long startTime = SystemClock.elapsedRealtime();
|
long startTime = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
Uri uri = Uri.parse(ApnSettings.RESTORE_CARRIERS_URI);
|
Uri uri = PreferredApnRepository.getRestorePreferredApnUri();
|
||||||
|
|
||||||
if (SubscriptionManager.isUsableSubscriptionId(subscriptionId)) {
|
if (SubscriptionManager.isUsableSubscriptionId(subscriptionId)) {
|
||||||
uri = Uri.withAppendedPath(uri, "subId/" + String.valueOf(subscriptionId));
|
uri = Uri.withAppendedPath(uri, "subId/" + String.valueOf(subscriptionId));
|
||||||
|
@@ -32,6 +32,7 @@ import android.widget.RadioButton;
|
|||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
@@ -45,8 +46,9 @@ import com.android.settings.spa.SpaActivity;
|
|||||||
public class ApnPreference extends Preference
|
public class ApnPreference extends Preference
|
||||||
implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
|
implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
|
||||||
private static final String TAG = "ApnPreference";
|
private static final String TAG = "ApnPreference";
|
||||||
private static String sSelectedKey = null;
|
private boolean mIsChecked = false;
|
||||||
private static CompoundButton sCurrentChecked = null;
|
@Nullable
|
||||||
|
private RadioButton mRadioButton = null;
|
||||||
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||||
private boolean mProtectFromCheckedChange = false;
|
private boolean mProtectFromCheckedChange = false;
|
||||||
private boolean mDefaultSelectable = true;
|
private boolean mDefaultSelectable = true;
|
||||||
@@ -84,20 +86,15 @@ public class ApnPreference extends Preference
|
|||||||
textArea.setOnClickListener(this);
|
textArea.setOnClickListener(this);
|
||||||
|
|
||||||
final RadioButton rb = view.itemView.requireViewById(R.id.apn_radiobutton);
|
final RadioButton rb = view.itemView.requireViewById(R.id.apn_radiobutton);
|
||||||
|
mRadioButton = rb;
|
||||||
if (mDefaultSelectable) {
|
if (mDefaultSelectable) {
|
||||||
view.itemView.requireViewById(R.id.apn_radio_button_frame).setOnClickListener((v) -> {
|
view.itemView.requireViewById(R.id.apn_radio_button_frame).setOnClickListener((v) -> {
|
||||||
rb.performClick();
|
rb.performClick();
|
||||||
});
|
});
|
||||||
rb.setOnCheckedChangeListener(this);
|
rb.setOnCheckedChangeListener(this);
|
||||||
|
|
||||||
final boolean isChecked = getKey().equals(sSelectedKey);
|
|
||||||
if (isChecked) {
|
|
||||||
sCurrentChecked = rb;
|
|
||||||
sSelectedKey = getKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
mProtectFromCheckedChange = true;
|
mProtectFromCheckedChange = true;
|
||||||
rb.setChecked(isChecked);
|
rb.setChecked(mIsChecked);
|
||||||
mProtectFromCheckedChange = false;
|
mProtectFromCheckedChange = false;
|
||||||
rb.setVisibility(View.VISIBLE);
|
rb.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
@@ -106,17 +103,15 @@ public class ApnPreference extends Preference
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the preference is checked or not.
|
* Set preference isChecked.
|
||||||
*/
|
*/
|
||||||
public boolean isChecked() {
|
public void setIsChecked(boolean isChecked) {
|
||||||
return getKey().equals(sSelectedKey);
|
mIsChecked = isChecked;
|
||||||
}
|
if (mRadioButton != null) {
|
||||||
|
mProtectFromCheckedChange = true;
|
||||||
/**
|
mRadioButton.setChecked(mIsChecked);
|
||||||
* Set preference checked.
|
mProtectFromCheckedChange = false;
|
||||||
*/
|
}
|
||||||
public void setChecked() {
|
|
||||||
sSelectedKey = getKey();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,15 +124,7 @@ public class ApnPreference extends Preference
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
if (sCurrentChecked != null) {
|
callChangeListener(getKey());
|
||||||
sCurrentChecked.setChecked(false);
|
|
||||||
}
|
|
||||||
sCurrentChecked = buttonView;
|
|
||||||
sSelectedKey = getKey();
|
|
||||||
callChangeListener(sSelectedKey);
|
|
||||||
} else {
|
|
||||||
sCurrentChecked = null;
|
|
||||||
sSelectedKey = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,29 +22,17 @@ import android.app.Activity;
|
|||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.provider.Telephony;
|
import android.provider.Telephony;
|
||||||
import android.telephony.CarrierConfigManager;
|
import android.telephony.CarrierConfigManager;
|
||||||
import android.telephony.PhoneStateListener;
|
|
||||||
import android.telephony.PreciseDataConnectionState;
|
|
||||||
import android.telephony.SubscriptionInfo;
|
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.telephony.data.ApnSetting;
|
import android.telephony.data.ApnSetting;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -57,13 +45,13 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceGroup;
|
import androidx.preference.PreferenceGroup;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.RestrictedSettingsFragment;
|
import com.android.settings.RestrictedSettingsFragment;
|
||||||
import com.android.settings.flags.Flags;
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.network.SubscriptionUtil;
|
|
||||||
import com.android.settings.network.telephony.SubscriptionRepository;
|
import com.android.settings.network.telephony.SubscriptionRepository;
|
||||||
import com.android.settings.spa.SpaActivity;
|
import com.android.settings.spa.SpaActivity;
|
||||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
@@ -77,13 +65,8 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
implements Preference.OnPreferenceChangeListener {
|
implements Preference.OnPreferenceChangeListener {
|
||||||
static final String TAG = "ApnSettings";
|
static final String TAG = "ApnSettings";
|
||||||
|
|
||||||
public static final String EXTRA_POSITION = "position";
|
|
||||||
public static final String RESTORE_CARRIERS_URI =
|
|
||||||
"content://telephony/carriers/restore";
|
|
||||||
public static final String PREFERRED_APN_URI =
|
|
||||||
"content://telephony/carriers/preferapn";
|
|
||||||
|
|
||||||
public static final String APN_ID = "apn_id";
|
public static final String APN_ID = "apn_id";
|
||||||
|
public static final String APN_LIST = "apn_list";
|
||||||
public static final String SUB_ID = "sub_id";
|
public static final String SUB_ID = "sub_id";
|
||||||
public static final String MVNO_TYPE = "mvno_type";
|
public static final String MVNO_TYPE = "mvno_type";
|
||||||
public static final String MVNO_MATCH_DATA = "mvno_match_data";
|
public static final String MVNO_MATCH_DATA = "mvno_match_data";
|
||||||
@@ -109,31 +92,16 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
private static final int MENU_NEW = Menu.FIRST;
|
private static final int MENU_NEW = Menu.FIRST;
|
||||||
private static final int MENU_RESTORE = Menu.FIRST + 1;
|
private static final int MENU_RESTORE = Menu.FIRST + 1;
|
||||||
|
|
||||||
private static final int EVENT_RESTORE_DEFAULTAPN_START = 1;
|
|
||||||
private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2;
|
|
||||||
|
|
||||||
private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
|
private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
|
||||||
|
|
||||||
private static final Uri DEFAULTAPN_URI = Uri.parse(RESTORE_CARRIERS_URI);
|
|
||||||
private static final Uri PREFERAPN_URI = Uri.parse(PREFERRED_APN_URI);
|
|
||||||
|
|
||||||
private boolean mRestoreDefaultApnMode;
|
private boolean mRestoreDefaultApnMode;
|
||||||
|
|
||||||
private UserManager mUserManager;
|
private UserManager mUserManager;
|
||||||
private TelephonyManager mTelephonyManager;
|
|
||||||
private RestoreApnUiHandler mRestoreApnUiHandler;
|
|
||||||
private RestoreApnProcessHandler mRestoreApnProcessHandler;
|
|
||||||
private HandlerThread mRestoreDefaultApnThread;
|
|
||||||
private SubscriptionInfo mSubscriptionInfo;
|
|
||||||
private int mSubId;
|
private int mSubId;
|
||||||
private int mPhoneId;
|
private PreferredApnRepository mPreferredApnRepository;
|
||||||
private String mMvnoType;
|
private String mMvnoType;
|
||||||
private String mMvnoMatchData;
|
private String mMvnoMatchData;
|
||||||
|
|
||||||
private String mSelectedKey;
|
|
||||||
|
|
||||||
private IntentFilter mIntentFilter;
|
|
||||||
|
|
||||||
private boolean mUnavailable;
|
private boolean mUnavailable;
|
||||||
|
|
||||||
private boolean mHideImsApn;
|
private boolean mHideImsApn;
|
||||||
@@ -144,61 +112,6 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
|
super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
|
|
||||||
@Override
|
|
||||||
public void onPreciseDataConnectionStateChanged(
|
|
||||||
PreciseDataConnectionState dataConnectionState) {
|
|
||||||
if (dataConnectionState.getState() == TelephonyManager.DATA_CONNECTED) {
|
|
||||||
if (!mRestoreDefaultApnMode) {
|
|
||||||
fillList();
|
|
||||||
} else {
|
|
||||||
showDialog(DIALOG_RESTORE_DEFAULTAPN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (intent.getAction().equals(
|
|
||||||
TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
|
|
||||||
if (mRestoreDefaultApnMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int extraSubId = intent.getIntExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID,
|
|
||||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
|
||||||
if (SubscriptionManager.isValidSubscriptionId(extraSubId)
|
|
||||||
&& mPhoneId == SubscriptionUtil.getPhoneId(context, extraSubId)
|
|
||||||
&& extraSubId != mSubId) {
|
|
||||||
// subscription has changed
|
|
||||||
mSubId = extraSubId;
|
|
||||||
mSubscriptionInfo = getSubscriptionInfo(mSubId);
|
|
||||||
restartPhoneStateListener(mSubId);
|
|
||||||
}
|
|
||||||
fillList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void restartPhoneStateListener(int subId) {
|
|
||||||
if (mRestoreDefaultApnMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final TelephonyManager updatedTelephonyManager =
|
|
||||||
mTelephonyManager.createForSubscriptionId(subId);
|
|
||||||
|
|
||||||
// restart monitoring when subscription has been changed
|
|
||||||
mTelephonyManager.listen(mPhoneStateListener,
|
|
||||||
PhoneStateListener.LISTEN_NONE);
|
|
||||||
|
|
||||||
mTelephonyManager = updatedTelephonyManager;
|
|
||||||
|
|
||||||
mTelephonyManager.listen(mPhoneStateListener,
|
|
||||||
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
return SettingsEnums.APN;
|
return SettingsEnums.APN;
|
||||||
@@ -210,15 +123,10 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
mSubId = activity.getIntent().getIntExtra(SUB_ID,
|
mSubId = activity.getIntent().getIntExtra(SUB_ID,
|
||||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||||
mPhoneId = SubscriptionUtil.getPhoneId(activity, mSubId);
|
mPreferredApnRepository = new PreferredApnRepository(activity, mSubId);
|
||||||
mIntentFilter = new IntentFilter();
|
|
||||||
mIntentFilter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
|
|
||||||
|
|
||||||
setIfOnlyAvailableForAdmins(true);
|
setIfOnlyAvailableForAdmins(true);
|
||||||
|
|
||||||
mSubscriptionInfo = getSubscriptionInfo(mSubId);
|
|
||||||
mTelephonyManager = activity.getSystemService(TelephonyManager.class);
|
|
||||||
|
|
||||||
final CarrierConfigManager configManager = (CarrierConfigManager)
|
final CarrierConfigManager configManager = (CarrierConfigManager)
|
||||||
getSystemService(Context.CARRIER_CONFIG_SERVICE);
|
getSystemService(Context.CARRIER_CONFIG_SERVICE);
|
||||||
final PersistableBundle b = configManager.getConfigForSubId(mSubId);
|
final PersistableBundle b = configManager.getConfigForSubId(mSubId);
|
||||||
@@ -256,14 +164,24 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
|
||||||
new SubscriptionRepository(requireContext())
|
new SubscriptionRepository(requireContext())
|
||||||
.collectSubscriptionEnabled(mSubId, getViewLifecycleOwner(), (isEnabled) -> {
|
.collectSubscriptionEnabled(mSubId, viewLifecycleOwner, (isEnabled) -> {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
Log.d(TAG, "Due to subscription not enabled, closes APN settings page");
|
Log.d(TAG, "Due to subscription not enabled, closes APN settings page");
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mPreferredApnRepository.collectPreferredApn(viewLifecycleOwner, (preferredApn) -> {
|
||||||
|
final PreferenceGroup apnPreferenceList = findPreference(APN_LIST);
|
||||||
|
for (int i = 0; i < apnPreferenceList.getPreferenceCount(); i++) {
|
||||||
|
ApnPreference apnPreference = (ApnPreference) apnPreferenceList.getPreference(i);
|
||||||
|
apnPreference.setIsChecked(apnPreference.getKey().equals(preferredApn));
|
||||||
|
}
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -274,39 +192,11 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActivity().registerReceiver(mReceiver, mIntentFilter,
|
|
||||||
Context.RECEIVER_EXPORTED_UNAUDITED);
|
|
||||||
|
|
||||||
restartPhoneStateListener(mSubId);
|
|
||||||
|
|
||||||
if (!mRestoreDefaultApnMode) {
|
if (!mRestoreDefaultApnMode) {
|
||||||
fillList();
|
fillList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
if (mUnavailable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getActivity().unregisterReceiver(mReceiver);
|
|
||||||
|
|
||||||
mTelephonyManager.listen(mPhoneStateListener,
|
|
||||||
PhoneStateListener.LISTEN_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
|
|
||||||
if (mRestoreDefaultApnThread != null) {
|
|
||||||
mRestoreDefaultApnThread.quit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EnforcedAdmin getRestrictionEnforcedAdmin() {
|
public EnforcedAdmin getRestrictionEnforcedAdmin() {
|
||||||
final UserHandle user = UserHandle.of(mUserManager.getProcessUserId());
|
final UserHandle user = UserHandle.of(mUserManager.getProcessUserId());
|
||||||
@@ -318,15 +208,9 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubscriptionInfo getSubscriptionInfo(int subId) {
|
|
||||||
return SubscriptionManager.from(getActivity()).getActiveSubscriptionInfo(subId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillList() {
|
private void fillList() {
|
||||||
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
|
||||||
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|
||||||
final Uri simApnUri = Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
|
final Uri simApnUri = Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
|
||||||
String.valueOf(subId));
|
String.valueOf(mSubId));
|
||||||
final StringBuilder where =
|
final StringBuilder where =
|
||||||
new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND "
|
new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND "
|
||||||
+ "user_visible!=0");
|
+ "user_visible!=0");
|
||||||
@@ -342,13 +226,12 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
Telephony.Carriers.DEFAULT_SORT_ORDER);
|
Telephony.Carriers.DEFAULT_SORT_ORDER);
|
||||||
|
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
final PreferenceGroup apnPrefList = (PreferenceGroup) findPreference("apn_list");
|
final PreferenceGroup apnPrefList = findPreference(APN_LIST);
|
||||||
apnPrefList.removeAll();
|
apnPrefList.removeAll();
|
||||||
|
|
||||||
final ArrayList<ApnPreference> apnList = new ArrayList<ApnPreference>();
|
final ArrayList<ApnPreference> apnList = new ArrayList<ApnPreference>();
|
||||||
final ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>();
|
final ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>();
|
||||||
|
|
||||||
mSelectedKey = getSelectedApnKey();
|
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
while (!cursor.isAfterLast()) {
|
while (!cursor.isAfterLast()) {
|
||||||
final String name = cursor.getString(NAME_INDEX);
|
final String name = cursor.getString(NAME_INDEX);
|
||||||
@@ -365,7 +248,7 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
pref.setTitle(name);
|
pref.setTitle(name);
|
||||||
pref.setPersistent(false);
|
pref.setPersistent(false);
|
||||||
pref.setOnPreferenceChangeListener(this);
|
pref.setOnPreferenceChangeListener(this);
|
||||||
pref.setSubId(subId);
|
pref.setSubId(mSubId);
|
||||||
if (mHidePresetApnDetails && edited == Telephony.Carriers.UNEDITED) {
|
if (mHidePresetApnDetails && edited == Telephony.Carriers.UNEDITED) {
|
||||||
pref.setHideDetails();
|
pref.setHideDetails();
|
||||||
} else {
|
} else {
|
||||||
@@ -376,9 +259,6 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
((type == null) || type.contains(ApnSetting.TYPE_DEFAULT_STRING));
|
((type == null) || type.contains(ApnSetting.TYPE_DEFAULT_STRING));
|
||||||
pref.setDefaultSelectable(defaultSelectable);
|
pref.setDefaultSelectable(defaultSelectable);
|
||||||
if (defaultSelectable) {
|
if (defaultSelectable) {
|
||||||
if ((mSelectedKey != null) && mSelectedKey.equals(key)) {
|
|
||||||
pref.setChecked();
|
|
||||||
}
|
|
||||||
apnList.add(pref);
|
apnList.add(pref);
|
||||||
} else {
|
} else {
|
||||||
mmsApnList.add(pref);
|
mmsApnList.add(pref);
|
||||||
@@ -427,15 +307,13 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addNewApn() {
|
private void addNewApn() {
|
||||||
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
|
||||||
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|
||||||
if (Flags.newApnPageEnabled()) {
|
if (Flags.newApnPageEnabled()) {
|
||||||
String route = ApnEditPageProvider.INSTANCE.getRoute(
|
String route = ApnEditPageProvider.INSTANCE.getRoute(
|
||||||
INSERT_URL, Telephony.Carriers.CONTENT_URI, subId);
|
INSERT_URL, Telephony.Carriers.CONTENT_URI, mSubId);
|
||||||
SpaActivity.startSpaActivity(getContext(), route);
|
SpaActivity.startSpaActivity(getContext(), route);
|
||||||
} else {
|
} else {
|
||||||
final Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI);
|
final Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI);
|
||||||
intent.putExtra(SUB_ID, subId);
|
intent.putExtra(SUB_ID, mSubId);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
|
if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
|
||||||
intent.putExtra(MVNO_TYPE, mMvnoType);
|
intent.putExtra(MVNO_TYPE, mMvnoType);
|
||||||
@@ -451,113 +329,36 @@ public class ApnSettings extends RestrictedSettingsFragment
|
|||||||
+ ", newValue - " + newValue + ", newValue type - "
|
+ ", newValue - " + newValue + ", newValue type - "
|
||||||
+ newValue.getClass());
|
+ newValue.getClass());
|
||||||
if (newValue instanceof String) {
|
if (newValue instanceof String) {
|
||||||
setSelectedApnKey((String) newValue);
|
mPreferredApnRepository.setPreferredApn((String) newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSelectedApnKey(String key) {
|
private void restoreDefaultApn() {
|
||||||
mSelectedKey = key;
|
|
||||||
final ContentResolver resolver = getContentResolver();
|
|
||||||
|
|
||||||
final ContentValues values = new ContentValues();
|
|
||||||
values.put(APN_ID, mSelectedKey);
|
|
||||||
resolver.update(getUriForCurrSubId(PREFERAPN_URI), values, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSelectedApnKey() {
|
|
||||||
String key = null;
|
|
||||||
|
|
||||||
final Cursor cursor = getContentResolver().query(getUriForCurrSubId(PREFERAPN_URI),
|
|
||||||
new String[] {"_id"}, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
|
|
||||||
if (cursor.getCount() > 0) {
|
|
||||||
cursor.moveToFirst();
|
|
||||||
key = cursor.getString(ID_INDEX);
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean restoreDefaultApn() {
|
|
||||||
// Callback of data connection change could be some noise during the stage of restore.
|
|
||||||
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
|
|
||||||
|
|
||||||
showDialog(DIALOG_RESTORE_DEFAULTAPN);
|
showDialog(DIALOG_RESTORE_DEFAULTAPN);
|
||||||
mRestoreDefaultApnMode = true;
|
mRestoreDefaultApnMode = true;
|
||||||
|
|
||||||
if (mRestoreApnUiHandler == null) {
|
mPreferredApnRepository.restorePreferredApn(getViewLifecycleOwner(), () -> {
|
||||||
mRestoreApnUiHandler = new RestoreApnUiHandler();
|
onPreferredApnRestored();
|
||||||
}
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
if (mRestoreApnProcessHandler == null || mRestoreDefaultApnThread == null) {
|
|
||||||
mRestoreDefaultApnThread = new HandlerThread(
|
|
||||||
"Restore default APN Handler: Process Thread");
|
|
||||||
mRestoreDefaultApnThread.start();
|
|
||||||
mRestoreApnProcessHandler = new RestoreApnProcessHandler(
|
|
||||||
mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
mRestoreApnProcessHandler
|
|
||||||
.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append subId to the Uri
|
private void onPreferredApnRestored() {
|
||||||
private Uri getUriForCurrSubId(Uri uri) {
|
final Activity activity = getActivity();
|
||||||
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
if (activity == null) {
|
||||||
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
mRestoreDefaultApnMode = false;
|
||||||
if (SubscriptionManager.isValidSubscriptionId(subId)) {
|
return;
|
||||||
return Uri.withAppendedPath(uri, "subId/" + String.valueOf(subId));
|
|
||||||
} else {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RestoreApnUiHandler extends Handler {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case EVENT_RESTORE_DEFAULTAPN_COMPLETE:
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
if (activity == null) {
|
|
||||||
mRestoreDefaultApnMode = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fillList();
|
|
||||||
getPreferenceScreen().setEnabled(true);
|
|
||||||
mRestoreDefaultApnMode = false;
|
|
||||||
removeDialog(DIALOG_RESTORE_DEFAULTAPN);
|
|
||||||
Toast.makeText(
|
|
||||||
activity,
|
|
||||||
getResources().getString(
|
|
||||||
R.string.restore_default_apn_completed),
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
restartPhoneStateListener(mSubId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RestoreApnProcessHandler extends Handler {
|
|
||||||
private Handler mRestoreApnUiHandler;
|
|
||||||
|
|
||||||
RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
|
|
||||||
super(looper);
|
|
||||||
this.mRestoreApnUiHandler = restoreApnUiHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case EVENT_RESTORE_DEFAULTAPN_START:
|
|
||||||
final ContentResolver resolver = getContentResolver();
|
|
||||||
resolver.delete(getUriForCurrSubId(DEFAULTAPN_URI), null, null);
|
|
||||||
mRestoreApnUiHandler
|
|
||||||
.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fillList();
|
||||||
|
getPreferenceScreen().setEnabled(true);
|
||||||
|
mRestoreDefaultApnMode = false;
|
||||||
|
removeDialog(DIALOG_RESTORE_DEFAULTAPN);
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
getResources().getString(R.string.restore_default_apn_completed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.apn
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.Telephony
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
|
import com.android.settingslib.spaprivileged.database.contentChangeFlow
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.conflate
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class PreferredApnRepository(private val context: Context, private val subId: Int) {
|
||||||
|
private val contentResolver = context.contentResolver
|
||||||
|
private val preferredApnUri =
|
||||||
|
Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI, "$subId")
|
||||||
|
|
||||||
|
/** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
|
||||||
|
fun restorePreferredApn(lifecycleOwner: LifecycleOwner, onRestored: () -> Unit) {
|
||||||
|
lifecycleOwner.lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
restorePreferredApn()
|
||||||
|
}
|
||||||
|
onRestored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restorePreferredApn() {
|
||||||
|
contentResolver.delete(
|
||||||
|
Uri.withAppendedPath(RestorePreferredApnUri, "subId/$subId"), null, null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPreferredApn(apnId: String) {
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(ApnSettings.APN_ID, apnId)
|
||||||
|
}
|
||||||
|
contentResolver.update(preferredApnUri, values, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
|
||||||
|
fun collectPreferredApn(lifecycleOwner: LifecycleOwner, action: (String?) -> Unit) {
|
||||||
|
preferredApnFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun preferredApnFlow(): Flow<String?> = context.contentChangeFlow(preferredApnUri).map {
|
||||||
|
contentResolver.query(
|
||||||
|
preferredApnUri,
|
||||||
|
arrayOf(Telephony.Carriers._ID),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
Telephony.Carriers.DEFAULT_SORT_ORDER,
|
||||||
|
).use { cursor ->
|
||||||
|
if (cursor?.moveToNext() == true) {
|
||||||
|
cursor.getString(cursor.getColumnIndex(Telephony.Carriers._ID))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}.also { Log.d(TAG, "[$subId] preferred APN: $it") }
|
||||||
|
}
|
||||||
|
}.conflate().flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "PreferredApnRepository"
|
||||||
|
|
||||||
|
private const val RESTORE_PREFERRED_APN = "content://telephony/carriers/restore"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val RestorePreferredApnUri: Uri = Uri.parse(RESTORE_PREFERRED_APN)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.apn
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.Telephony
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.kotlin.argThat
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.eq
|
||||||
|
import org.mockito.kotlin.isNull
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class PreferredApnRepositoryTest {
|
||||||
|
|
||||||
|
private val contentResolver = mock<ContentResolver>()
|
||||||
|
|
||||||
|
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { contentResolver } doReturn contentResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
private val repository = PreferredApnRepository(context, SUB_ID)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun restorePreferredApn() {
|
||||||
|
repository.restorePreferredApn()
|
||||||
|
|
||||||
|
verify(contentResolver).delete(
|
||||||
|
Uri.parse("content://telephony/carriers/restore/subId/2"),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setPreferredApn() {
|
||||||
|
val apnId = "10"
|
||||||
|
|
||||||
|
repository.setPreferredApn(apnId)
|
||||||
|
|
||||||
|
verify(contentResolver).update(
|
||||||
|
eq(Uri.parse("content://telephony/carriers/preferapn/subId/2")),
|
||||||
|
argThat { getAsString(ApnSettings.APN_ID) == apnId },
|
||||||
|
isNull(),
|
||||||
|
isNull(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun preferredApnFlow() = runBlocking {
|
||||||
|
val expectedPreferredApn = "10"
|
||||||
|
val mockCursor = mock<Cursor> {
|
||||||
|
on { moveToNext() } doReturn true
|
||||||
|
on { getColumnIndex(Telephony.Carriers._ID) } doReturn 0
|
||||||
|
on { getString(0) } doReturn expectedPreferredApn
|
||||||
|
}
|
||||||
|
contentResolver.stub {
|
||||||
|
on {
|
||||||
|
query(
|
||||||
|
Uri.parse("content://telephony/carriers/preferapn/subId/2"),
|
||||||
|
arrayOf(Telephony.Carriers._ID),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
Telephony.Carriers.DEFAULT_SORT_ORDER,
|
||||||
|
)
|
||||||
|
} doReturn mockCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
val preferredApn = repository.preferredApnFlow().firstWithTimeoutOrNull()
|
||||||
|
|
||||||
|
assertThat(preferredApn).isEqualTo(expectedPreferredApn)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val SUB_ID = 2
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user