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.settings.R;
|
||||
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.List;
|
||||
@@ -204,7 +204,7 @@ public class ResetNetworkOperationBuilder {
|
||||
Runnable runnable = () -> {
|
||||
long startTime = SystemClock.elapsedRealtime();
|
||||
|
||||
Uri uri = Uri.parse(ApnSettings.RESTORE_CARRIERS_URI);
|
||||
Uri uri = PreferredApnRepository.getRestorePreferredApnUri();
|
||||
|
||||
if (SubscriptionManager.isUsableSubscriptionId(subscriptionId)) {
|
||||
uri = Uri.withAppendedPath(uri, "subId/" + String.valueOf(subscriptionId));
|
||||
|
@@ -32,6 +32,7 @@ import android.widget.RadioButton;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
@@ -45,8 +46,9 @@ import com.android.settings.spa.SpaActivity;
|
||||
public class ApnPreference extends Preference
|
||||
implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
|
||||
private static final String TAG = "ApnPreference";
|
||||
private static String sSelectedKey = null;
|
||||
private static CompoundButton sCurrentChecked = null;
|
||||
private boolean mIsChecked = false;
|
||||
@Nullable
|
||||
private RadioButton mRadioButton = null;
|
||||
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
private boolean mProtectFromCheckedChange = false;
|
||||
private boolean mDefaultSelectable = true;
|
||||
@@ -84,20 +86,15 @@ public class ApnPreference extends Preference
|
||||
textArea.setOnClickListener(this);
|
||||
|
||||
final RadioButton rb = view.itemView.requireViewById(R.id.apn_radiobutton);
|
||||
mRadioButton = rb;
|
||||
if (mDefaultSelectable) {
|
||||
view.itemView.requireViewById(R.id.apn_radio_button_frame).setOnClickListener((v) -> {
|
||||
rb.performClick();
|
||||
});
|
||||
rb.setOnCheckedChangeListener(this);
|
||||
|
||||
final boolean isChecked = getKey().equals(sSelectedKey);
|
||||
if (isChecked) {
|
||||
sCurrentChecked = rb;
|
||||
sSelectedKey = getKey();
|
||||
}
|
||||
|
||||
mProtectFromCheckedChange = true;
|
||||
rb.setChecked(isChecked);
|
||||
rb.setChecked(mIsChecked);
|
||||
mProtectFromCheckedChange = false;
|
||||
rb.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
@@ -106,17 +103,15 @@ public class ApnPreference extends Preference
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the preference is checked or not.
|
||||
* Set preference isChecked.
|
||||
*/
|
||||
public boolean isChecked() {
|
||||
return getKey().equals(sSelectedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set preference checked.
|
||||
*/
|
||||
public void setChecked() {
|
||||
sSelectedKey = getKey();
|
||||
public void setIsChecked(boolean isChecked) {
|
||||
mIsChecked = isChecked;
|
||||
if (mRadioButton != null) {
|
||||
mProtectFromCheckedChange = true;
|
||||
mRadioButton.setChecked(mIsChecked);
|
||||
mProtectFromCheckedChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,15 +124,7 @@ public class ApnPreference extends Preference
|
||||
}
|
||||
|
||||
if (isChecked) {
|
||||
if (sCurrentChecked != null) {
|
||||
sCurrentChecked.setChecked(false);
|
||||
}
|
||||
sCurrentChecked = buttonView;
|
||||
sSelectedKey = getKey();
|
||||
callChangeListener(sSelectedKey);
|
||||
} else {
|
||||
sCurrentChecked = null;
|
||||
sSelectedKey = null;
|
||||
callChangeListener(getKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -22,29 +22,17 @@ import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
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.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Telephony;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.PreciseDataConnectionState;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.data.ApnSetting;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@@ -57,13 +45,13 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.RestrictedSettingsFragment;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.network.telephony.SubscriptionRepository;
|
||||
import com.android.settings.spa.SpaActivity;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
@@ -77,13 +65,8 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
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_LIST = "apn_list";
|
||||
public static final String SUB_ID = "sub_id";
|
||||
public static final String MVNO_TYPE = "mvno_type";
|
||||
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_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 Uri DEFAULTAPN_URI = Uri.parse(RESTORE_CARRIERS_URI);
|
||||
private static final Uri PREFERAPN_URI = Uri.parse(PREFERRED_APN_URI);
|
||||
|
||||
private boolean mRestoreDefaultApnMode;
|
||||
|
||||
private UserManager mUserManager;
|
||||
private TelephonyManager mTelephonyManager;
|
||||
private RestoreApnUiHandler mRestoreApnUiHandler;
|
||||
private RestoreApnProcessHandler mRestoreApnProcessHandler;
|
||||
private HandlerThread mRestoreDefaultApnThread;
|
||||
private SubscriptionInfo mSubscriptionInfo;
|
||||
private int mSubId;
|
||||
private int mPhoneId;
|
||||
private PreferredApnRepository mPreferredApnRepository;
|
||||
private String mMvnoType;
|
||||
private String mMvnoMatchData;
|
||||
|
||||
private String mSelectedKey;
|
||||
|
||||
private IntentFilter mIntentFilter;
|
||||
|
||||
private boolean mUnavailable;
|
||||
|
||||
private boolean mHideImsApn;
|
||||
@@ -144,61 +112,6 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
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
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.APN;
|
||||
@@ -210,15 +123,10 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
final Activity activity = getActivity();
|
||||
mSubId = activity.getIntent().getIntExtra(SUB_ID,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
mPhoneId = SubscriptionUtil.getPhoneId(activity, mSubId);
|
||||
mIntentFilter = new IntentFilter();
|
||||
mIntentFilter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
|
||||
mPreferredApnRepository = new PreferredApnRepository(activity, mSubId);
|
||||
|
||||
setIfOnlyAvailableForAdmins(true);
|
||||
|
||||
mSubscriptionInfo = getSubscriptionInfo(mSubId);
|
||||
mTelephonyManager = activity.getSystemService(TelephonyManager.class);
|
||||
|
||||
final CarrierConfigManager configManager = (CarrierConfigManager)
|
||||
getSystemService(Context.CARRIER_CONFIG_SERVICE);
|
||||
final PersistableBundle b = configManager.getConfigForSubId(mSubId);
|
||||
@@ -256,14 +164,24 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
|
||||
new SubscriptionRepository(requireContext())
|
||||
.collectSubscriptionEnabled(mSubId, getViewLifecycleOwner(), (isEnabled) -> {
|
||||
.collectSubscriptionEnabled(mSubId, viewLifecycleOwner, (isEnabled) -> {
|
||||
if (!isEnabled) {
|
||||
Log.d(TAG, "Due to subscription not enabled, closes APN settings page");
|
||||
finish();
|
||||
}
|
||||
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
|
||||
@@ -274,39 +192,11 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
return;
|
||||
}
|
||||
|
||||
getActivity().registerReceiver(mReceiver, mIntentFilter,
|
||||
Context.RECEIVER_EXPORTED_UNAUDITED);
|
||||
|
||||
restartPhoneStateListener(mSubId);
|
||||
|
||||
if (!mRestoreDefaultApnMode) {
|
||||
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
|
||||
public EnforcedAdmin getRestrictionEnforcedAdmin() {
|
||||
final UserHandle user = UserHandle.of(mUserManager.getProcessUserId());
|
||||
@@ -318,15 +208,9 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
return null;
|
||||
}
|
||||
|
||||
private SubscriptionInfo getSubscriptionInfo(int subId) {
|
||||
return SubscriptionManager.from(getActivity()).getActiveSubscriptionInfo(subId);
|
||||
}
|
||||
|
||||
private void fillList() {
|
||||
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
||||
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
final Uri simApnUri = Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
|
||||
String.valueOf(subId));
|
||||
String.valueOf(mSubId));
|
||||
final StringBuilder where =
|
||||
new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND "
|
||||
+ "user_visible!=0");
|
||||
@@ -342,13 +226,12 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
Telephony.Carriers.DEFAULT_SORT_ORDER);
|
||||
|
||||
if (cursor != null) {
|
||||
final PreferenceGroup apnPrefList = (PreferenceGroup) findPreference("apn_list");
|
||||
final PreferenceGroup apnPrefList = findPreference(APN_LIST);
|
||||
apnPrefList.removeAll();
|
||||
|
||||
final ArrayList<ApnPreference> apnList = new ArrayList<ApnPreference>();
|
||||
final ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>();
|
||||
|
||||
mSelectedKey = getSelectedApnKey();
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast()) {
|
||||
final String name = cursor.getString(NAME_INDEX);
|
||||
@@ -365,7 +248,7 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
pref.setTitle(name);
|
||||
pref.setPersistent(false);
|
||||
pref.setOnPreferenceChangeListener(this);
|
||||
pref.setSubId(subId);
|
||||
pref.setSubId(mSubId);
|
||||
if (mHidePresetApnDetails && edited == Telephony.Carriers.UNEDITED) {
|
||||
pref.setHideDetails();
|
||||
} else {
|
||||
@@ -376,9 +259,6 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
((type == null) || type.contains(ApnSetting.TYPE_DEFAULT_STRING));
|
||||
pref.setDefaultSelectable(defaultSelectable);
|
||||
if (defaultSelectable) {
|
||||
if ((mSelectedKey != null) && mSelectedKey.equals(key)) {
|
||||
pref.setChecked();
|
||||
}
|
||||
apnList.add(pref);
|
||||
} else {
|
||||
mmsApnList.add(pref);
|
||||
@@ -427,15 +307,13 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
|
||||
private void addNewApn() {
|
||||
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
||||
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
if (Flags.newApnPageEnabled()) {
|
||||
String route = ApnEditPageProvider.INSTANCE.getRoute(
|
||||
INSERT_URL, Telephony.Carriers.CONTENT_URI, subId);
|
||||
INSERT_URL, Telephony.Carriers.CONTENT_URI, mSubId);
|
||||
SpaActivity.startSpaActivity(getContext(), route);
|
||||
} else {
|
||||
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);
|
||||
if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
|
||||
intent.putExtra(MVNO_TYPE, mMvnoType);
|
||||
@@ -451,113 +329,36 @@ public class ApnSettings extends RestrictedSettingsFragment
|
||||
+ ", newValue - " + newValue + ", newValue type - "
|
||||
+ newValue.getClass());
|
||||
if (newValue instanceof String) {
|
||||
setSelectedApnKey((String) newValue);
|
||||
mPreferredApnRepository.setPreferredApn((String) newValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setSelectedApnKey(String key) {
|
||||
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);
|
||||
|
||||
private void restoreDefaultApn() {
|
||||
showDialog(DIALOG_RESTORE_DEFAULTAPN);
|
||||
mRestoreDefaultApnMode = true;
|
||||
|
||||
if (mRestoreApnUiHandler == null) {
|
||||
mRestoreApnUiHandler = new RestoreApnUiHandler();
|
||||
}
|
||||
|
||||
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;
|
||||
mPreferredApnRepository.restorePreferredApn(getViewLifecycleOwner(), () -> {
|
||||
onPreferredApnRestored();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
// Append subId to the Uri
|
||||
private Uri getUriForCurrSubId(Uri uri) {
|
||||
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
||||
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
if (SubscriptionManager.isValidSubscriptionId(subId)) {
|
||||
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;
|
||||
}
|
||||
private void onPreferredApnRestored() {
|
||||
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();
|
||||
}
|
||||
|
||||
@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