Previously ApnPrefence starts an intent with URI content://telephony/carrier/filtered/id, this URI is only implemented in telephony provider for query(), but not for delete() or update(). This caused the crash when user tries to update or delete an APN through this URI. We should let ApnPrefence starts an intent with URI content://telephony/carrier/id, which is a general telephony URI and all of query() add() delete() update() are implemented for non-DPC mode. And let ApnPrefence starts an intent with URI content://telephony/carrier/filtered/id in DPC mode(as the general URI can't access DPC-owned APN), when only DPC-owned APNs are presented to user. In the DPC mode, user can't update, add or delete an APN, they can only view(query) the APN, so it won't crash with URI_FILTERED. Bug: 72387301 Test: manual. Tried update, delete, and add action in Access Point Name page, no crash occurs. Instrumentation test will be added in b/72154761. Change-Id: I9979cbffcc94a37b2bd96db766ececd0ac7b20e2
543 lines
20 KiB
Java
Executable File
543 lines
20 KiB
Java
Executable File
/*
|
|
* Copyright (C) 2008 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;
|
|
|
|
import static android.provider.Telephony.Carriers.CONTENT_URI;
|
|
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
|
|
import static android.provider.Telephony.Carriers.FILTERED_URI;
|
|
|
|
import android.app.Activity;
|
|
import android.app.Dialog;
|
|
import android.app.ProgressDialog;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.ContentResolver;
|
|
import android.content.ContentUris;
|
|
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.support.v7.preference.Preference;
|
|
import android.support.v7.preference.PreferenceGroup;
|
|
import android.telephony.CarrierConfigManager;
|
|
import android.telephony.SubscriptionInfo;
|
|
import android.telephony.SubscriptionManager;
|
|
import android.telephony.TelephonyManager;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.view.Menu;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.MotionEvent;
|
|
import android.widget.Toast;
|
|
|
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
import com.android.internal.telephony.PhoneConstants;
|
|
import com.android.internal.telephony.TelephonyIntents;
|
|
import com.android.internal.telephony.dataconnection.ApnSetting;
|
|
import com.android.internal.telephony.uicc.IccRecords;
|
|
import com.android.internal.telephony.uicc.UiccController;
|
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
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 SUB_ID = "sub_id";
|
|
public static final String MVNO_TYPE = "mvno_type";
|
|
public static final String MVNO_MATCH_DATA = "mvno_match_data";
|
|
|
|
private static final int ID_INDEX = 0;
|
|
private static final int NAME_INDEX = 1;
|
|
private static final int APN_INDEX = 2;
|
|
private static final int TYPES_INDEX = 3;
|
|
private static final int MVNO_TYPE_INDEX = 4;
|
|
private static final int MVNO_MATCH_DATA_INDEX = 5;
|
|
|
|
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 static boolean mRestoreDefaultApnMode;
|
|
|
|
private UserManager mUserManager;
|
|
private RestoreApnUiHandler mRestoreApnUiHandler;
|
|
private RestoreApnProcessHandler mRestoreApnProcessHandler;
|
|
private HandlerThread mRestoreDefaultApnThread;
|
|
private SubscriptionInfo mSubscriptionInfo;
|
|
private UiccController mUiccController;
|
|
private String mMvnoType;
|
|
private String mMvnoMatchData;
|
|
|
|
private String mSelectedKey;
|
|
|
|
private IntentFilter mMobileStateFilter;
|
|
|
|
private boolean mUnavailable;
|
|
|
|
private boolean mHideImsApn;
|
|
private boolean mAllowAddingApns;
|
|
|
|
public ApnSettings() {
|
|
super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
|
|
}
|
|
|
|
private final BroadcastReceiver mMobileStateReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (intent.getAction().equals(
|
|
TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
|
|
PhoneConstants.DataState state = getMobileDataState(intent);
|
|
switch (state) {
|
|
case CONNECTED:
|
|
if (!mRestoreDefaultApnMode) {
|
|
fillList();
|
|
} else {
|
|
showDialog(DIALOG_RESTORE_DEFAULTAPN);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
private static PhoneConstants.DataState getMobileDataState(Intent intent) {
|
|
String str = intent.getStringExtra(PhoneConstants.STATE_KEY);
|
|
if (str != null) {
|
|
return Enum.valueOf(PhoneConstants.DataState.class, str);
|
|
} else {
|
|
return PhoneConstants.DataState.DISCONNECTED;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.APN;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
final Activity activity = getActivity();
|
|
final int subId = activity.getIntent().getIntExtra(SUB_ID,
|
|
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
|
|
|
mMobileStateFilter = new IntentFilter(
|
|
TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
|
|
|
|
setIfOnlyAvailableForAdmins(true);
|
|
|
|
mSubscriptionInfo = SubscriptionManager.from(activity).getActiveSubscriptionInfo(subId);
|
|
mUiccController = UiccController.getInstance();
|
|
|
|
CarrierConfigManager configManager = (CarrierConfigManager)
|
|
getSystemService(Context.CARRIER_CONFIG_SERVICE);
|
|
PersistableBundle b = configManager.getConfig();
|
|
mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL);
|
|
mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL);
|
|
if (mAllowAddingApns) {
|
|
String[] readOnlyApnTypes = b.getStringArray(
|
|
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
|
|
// if no apn type can be edited, do not allow adding APNs
|
|
if (ApnEditor.hasAllApns(readOnlyApnTypes)) {
|
|
Log.d(TAG, "not allowing adding APN because all APN types are read only");
|
|
mAllowAddingApns = false;
|
|
}
|
|
}
|
|
mUserManager = UserManager.get(activity);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
super.onActivityCreated(savedInstanceState);
|
|
|
|
getEmptyTextView().setText(R.string.apn_settings_not_available);
|
|
mUnavailable = isUiRestricted();
|
|
setHasOptionsMenu(!mUnavailable);
|
|
if (mUnavailable) {
|
|
addPreferencesFromResource(R.xml.placeholder_prefs);
|
|
return;
|
|
}
|
|
|
|
addPreferencesFromResource(R.xml.apn_settings);
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
if (mUnavailable) {
|
|
return;
|
|
}
|
|
|
|
getActivity().registerReceiver(mMobileStateReceiver, mMobileStateFilter);
|
|
|
|
if (!mRestoreDefaultApnMode) {
|
|
fillList();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
|
|
if (mUnavailable) {
|
|
return;
|
|
}
|
|
|
|
getActivity().unregisterReceiver(mMobileStateReceiver);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
|
|
if (mRestoreDefaultApnThread != null) {
|
|
mRestoreDefaultApnThread.quit();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public EnforcedAdmin getRestrictionEnforcedAdmin() {
|
|
final UserHandle user = UserHandle.of(mUserManager.getUserHandle());
|
|
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, user)
|
|
&& !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
|
|
user)) {
|
|
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean isDpcApnEnforced() {
|
|
try (Cursor enforceCursor = getContentResolver().query(ENFORCE_MANAGED_URI,
|
|
null, null, null, null)) {
|
|
if (enforceCursor == null || enforceCursor.getCount() != 1) {
|
|
return false;
|
|
}
|
|
enforceCursor.moveToFirst();
|
|
return enforceCursor.getInt(0) > 0;
|
|
}
|
|
}
|
|
|
|
private void fillList() {
|
|
final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
|
|
final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
|
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|
final String mccmnc = mSubscriptionInfo == null ? "" : tm.getSimOperator(subId);
|
|
Log.d(TAG, "mccmnc = " + mccmnc);
|
|
StringBuilder where = new StringBuilder("numeric=\"" + mccmnc +
|
|
"\" AND NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND user_visible!=0");
|
|
|
|
if (mHideImsApn) {
|
|
where.append(" AND NOT (type='ims')");
|
|
}
|
|
|
|
Cursor cursor = getContentResolver().query(FILTERED_URI,
|
|
new String[] {"_id", "name", "apn", "type", "mvno_type", "mvno_match_data"},
|
|
where.toString(), null, Telephony.Carriers.DEFAULT_SORT_ORDER);
|
|
|
|
if (cursor != null) {
|
|
IccRecords r = null;
|
|
if (mUiccController != null && mSubscriptionInfo != null) {
|
|
r = mUiccController.getIccRecords(
|
|
SubscriptionManager.getPhoneId(subId), UiccController.APP_FAM_3GPP);
|
|
}
|
|
PreferenceGroup apnList = (PreferenceGroup) findPreference("apn_list");
|
|
apnList.removeAll();
|
|
|
|
ArrayList<ApnPreference> mnoApnList = new ArrayList<ApnPreference>();
|
|
ArrayList<ApnPreference> mvnoApnList = new ArrayList<ApnPreference>();
|
|
ArrayList<ApnPreference> mnoMmsApnList = new ArrayList<ApnPreference>();
|
|
ArrayList<ApnPreference> mvnoMmsApnList = new ArrayList<ApnPreference>();
|
|
|
|
mSelectedKey = getSelectedApnKey();
|
|
cursor.moveToFirst();
|
|
boolean enforced = isDpcApnEnforced();
|
|
while (!cursor.isAfterLast()) {
|
|
String name = cursor.getString(NAME_INDEX);
|
|
String apn = cursor.getString(APN_INDEX);
|
|
String key = cursor.getString(ID_INDEX);
|
|
String type = cursor.getString(TYPES_INDEX);
|
|
String mvnoType = cursor.getString(MVNO_TYPE_INDEX);
|
|
String mvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX);
|
|
|
|
ApnPreference pref = new ApnPreference(getPrefContext());
|
|
|
|
pref.setKey(key);
|
|
pref.setTitle(name);
|
|
pref.setSummary(apn);
|
|
pref.setPersistent(false);
|
|
pref.setOnPreferenceChangeListener(this);
|
|
pref.setSubId(subId);
|
|
pref.setDpcEnforced(enforced);
|
|
|
|
boolean selectable = ((type == null) || !type.equals("mms"));
|
|
pref.setSelectable(selectable);
|
|
if (selectable) {
|
|
if ((mSelectedKey != null) && mSelectedKey.equals(key)) {
|
|
pref.setChecked();
|
|
}
|
|
addApnToList(pref, mnoApnList, mvnoApnList, r, mvnoType, mvnoMatchData);
|
|
} else {
|
|
addApnToList(pref, mnoMmsApnList, mvnoMmsApnList, r, mvnoType, mvnoMatchData);
|
|
}
|
|
cursor.moveToNext();
|
|
}
|
|
cursor.close();
|
|
|
|
if (!mvnoApnList.isEmpty()) {
|
|
mnoApnList = mvnoApnList;
|
|
mnoMmsApnList = mvnoMmsApnList;
|
|
|
|
// Also save the mvno info
|
|
}
|
|
|
|
for (Preference preference : mnoApnList) {
|
|
apnList.addPreference(preference);
|
|
}
|
|
for (Preference preference : mnoMmsApnList) {
|
|
apnList.addPreference(preference);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addApnToList(ApnPreference pref, ArrayList<ApnPreference> mnoList,
|
|
ArrayList<ApnPreference> mvnoList, IccRecords r, String mvnoType,
|
|
String mvnoMatchData) {
|
|
if (r != null && !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)) {
|
|
if (ApnSetting.mvnoMatches(r, mvnoType, mvnoMatchData)) {
|
|
mvnoList.add(pref);
|
|
// Since adding to mvno list, save mvno info
|
|
mMvnoType = mvnoType;
|
|
mMvnoMatchData = mvnoMatchData;
|
|
}
|
|
} else {
|
|
mnoList.add(pref);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
if (!mUnavailable && !isDpcApnEnforced()) {
|
|
if (mAllowAddingApns) {
|
|
menu.add(0, MENU_NEW, 0,
|
|
getResources().getString(R.string.menu_new))
|
|
.setIcon(R.drawable.ic_menu_add_white)
|
|
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
|
}
|
|
menu.add(0, MENU_RESTORE, 0, getResources().getString(R.string.menu_restore))
|
|
.setIcon(android.R.drawable.ic_menu_upload);
|
|
}
|
|
|
|
super.onCreateOptionsMenu(menu, inflater);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case MENU_NEW:
|
|
addNewApn();
|
|
return true;
|
|
|
|
case MENU_RESTORE:
|
|
restoreDefaultApn();
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
private void addNewApn() {
|
|
Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI);
|
|
int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
|
|
: SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|
intent.putExtra(SUB_ID, subId);
|
|
if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
|
|
intent.putExtra(MVNO_TYPE, mMvnoType);
|
|
intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData);
|
|
}
|
|
startActivity(intent);
|
|
}
|
|
|
|
@Override
|
|
public boolean onPreferenceTreeClick(Preference preference) {
|
|
int pos = Integer.parseInt(preference.getKey());
|
|
Uri url = ContentUris.withAppendedId(isDpcApnEnforced() ? FILTERED_URI : CONTENT_URI, pos);
|
|
startActivity(new Intent(Intent.ACTION_EDIT, url));
|
|
return true;
|
|
}
|
|
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
Log.d(TAG, "onPreferenceChange(): Preference - " + preference
|
|
+ ", newValue - " + newValue + ", newValue type - "
|
|
+ newValue.getClass());
|
|
if (newValue instanceof String) {
|
|
setSelectedApnKey((String) newValue);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void setSelectedApnKey(String key) {
|
|
mSelectedKey = key;
|
|
ContentResolver resolver = getContentResolver();
|
|
|
|
ContentValues values = new ContentValues();
|
|
values.put(APN_ID, mSelectedKey);
|
|
resolver.update(getUriForCurrSubId(PREFERAPN_URI), values, null, null);
|
|
}
|
|
|
|
private String getSelectedApnKey() {
|
|
String key = null;
|
|
|
|
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() {
|
|
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;
|
|
}
|
|
|
|
// Append subId to the Uri
|
|
private Uri getUriForCurrSubId(Uri uri) {
|
|
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:
|
|
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();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private class RestoreApnProcessHandler extends Handler {
|
|
private Handler mRestoreApnUiHandler;
|
|
|
|
public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
|
|
super(looper);
|
|
this.mRestoreApnUiHandler = restoreApnUiHandler;
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_RESTORE_DEFAULTAPN_START:
|
|
ContentResolver resolver = getContentResolver();
|
|
resolver.delete(getUriForCurrSubId(DEFAULTAPN_URI), null, null);
|
|
mRestoreApnUiHandler
|
|
.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(int id) {
|
|
if (id == DIALOG_RESTORE_DEFAULTAPN) {
|
|
ProgressDialog dialog = new ProgressDialog(getActivity()) {
|
|
public boolean onTouchEvent(MotionEvent event) {
|
|
return true;
|
|
}
|
|
};
|
|
dialog.setMessage(getResources().getString(R.string.restore_default_apn));
|
|
dialog.setCancelable(false);
|
|
return dialog;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int getDialogMetricsCategory(int dialogId) {
|
|
if (dialogId == DIALOG_RESTORE_DEFAULTAPN) {
|
|
return MetricsEvent.DIALOG_APN_RESTORE_DEFAULT;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|