1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.network; 18 19 import android.app.Activity; 20 import android.app.Dialog; 21 import android.app.ProgressDialog; 22 import android.app.settings.SettingsEnums; 23 import android.content.BroadcastReceiver; 24 import android.content.ContentResolver; 25 import android.content.ContentValues; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.database.Cursor; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.os.Message; 36 import android.os.PersistableBundle; 37 import android.os.UserHandle; 38 import android.os.UserManager; 39 import android.provider.Telephony; 40 import android.telephony.CarrierConfigManager; 41 import android.telephony.SubscriptionInfo; 42 import android.telephony.SubscriptionManager; 43 import android.telephony.TelephonyManager; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.view.Menu; 47 import android.view.MenuInflater; 48 import android.view.MenuItem; 49 import android.view.MotionEvent; 50 import android.widget.Toast; 51 52 import androidx.preference.Preference; 53 import androidx.preference.PreferenceGroup; 54 55 import com.android.internal.telephony.PhoneConstants; 56 import com.android.internal.telephony.TelephonyIntents; 57 import com.android.internal.telephony.uicc.UiccController; 58 import com.android.settings.R; 59 import com.android.settings.RestrictedSettingsFragment; 60 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 61 62 import java.util.ArrayList; 63 64 public class ApnSettings extends RestrictedSettingsFragment 65 implements Preference.OnPreferenceChangeListener { 66 static final String TAG = "ApnSettings"; 67 68 public static final String EXTRA_POSITION = "position"; 69 public static final String RESTORE_CARRIERS_URI = 70 "content://telephony/carriers/restore"; 71 public static final String PREFERRED_APN_URI = 72 "content://telephony/carriers/preferapn"; 73 74 public static final String APN_ID = "apn_id"; 75 public static final String SUB_ID = "sub_id"; 76 public static final String MVNO_TYPE = "mvno_type"; 77 public static final String MVNO_MATCH_DATA = "mvno_match_data"; 78 79 private static final String[] CARRIERS_PROJECTION = new String[] { 80 Telephony.Carriers._ID, 81 Telephony.Carriers.NAME, 82 Telephony.Carriers.APN, 83 Telephony.Carriers.TYPE, 84 Telephony.Carriers.MVNO_TYPE, 85 Telephony.Carriers.MVNO_MATCH_DATA, 86 Telephony.Carriers.EDITED_STATUS, 87 }; 88 89 private static final int ID_INDEX = 0; 90 private static final int NAME_INDEX = 1; 91 private static final int APN_INDEX = 2; 92 private static final int TYPES_INDEX = 3; 93 private static final int MVNO_TYPE_INDEX = 4; 94 private static final int MVNO_MATCH_DATA_INDEX = 5; 95 private static final int EDITED_INDEX = 6; 96 97 private static final int MENU_NEW = Menu.FIRST; 98 private static final int MENU_RESTORE = Menu.FIRST + 1; 99 100 private static final int EVENT_RESTORE_DEFAULTAPN_START = 1; 101 private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2; 102 103 private static final int DIALOG_RESTORE_DEFAULTAPN = 1001; 104 105 private static final Uri DEFAULTAPN_URI = Uri.parse(RESTORE_CARRIERS_URI); 106 private static final Uri PREFERAPN_URI = Uri.parse(PREFERRED_APN_URI); 107 108 private static boolean mRestoreDefaultApnMode; 109 110 private UserManager mUserManager; 111 private RestoreApnUiHandler mRestoreApnUiHandler; 112 private RestoreApnProcessHandler mRestoreApnProcessHandler; 113 private HandlerThread mRestoreDefaultApnThread; 114 private SubscriptionInfo mSubscriptionInfo; 115 private int mSubId; 116 private UiccController mUiccController; 117 private String mMvnoType; 118 private String mMvnoMatchData; 119 120 private String mSelectedKey; 121 122 private IntentFilter mIntentFilter; 123 124 private boolean mUnavailable; 125 126 private boolean mHideImsApn; 127 private boolean mAllowAddingApns; 128 private boolean mHidePresetApnDetails; 129 ApnSettings()130 public ApnSettings() { 131 super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); 132 } 133 134 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 135 @Override 136 public void onReceive(Context context, Intent intent) { 137 if (intent.getAction().equals( 138 TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 139 PhoneConstants.DataState state = getMobileDataState(intent); 140 switch (state) { 141 case CONNECTED: 142 if (!mRestoreDefaultApnMode) { 143 fillList(); 144 } else { 145 showDialog(DIALOG_RESTORE_DEFAULTAPN); 146 } 147 break; 148 } 149 } else if(intent.getAction().equals( 150 TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) { 151 if (!mRestoreDefaultApnMode) { 152 int extraSubId = intent.getIntExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, 153 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 154 if (extraSubId != mSubId) { 155 // subscription has changed 156 mSubId = extraSubId; 157 mSubscriptionInfo = getSubscriptionInfo(mSubId); 158 } 159 fillList(); 160 } 161 } 162 } 163 }; 164 getMobileDataState(Intent intent)165 private static PhoneConstants.DataState getMobileDataState(Intent intent) { 166 String str = intent.getStringExtra(PhoneConstants.STATE_KEY); 167 if (str != null) { 168 return Enum.valueOf(PhoneConstants.DataState.class, str); 169 } else { 170 return PhoneConstants.DataState.DISCONNECTED; 171 } 172 } 173 174 @Override getMetricsCategory()175 public int getMetricsCategory() { 176 return SettingsEnums.APN; 177 } 178 179 @Override onCreate(Bundle icicle)180 public void onCreate(Bundle icicle) { 181 super.onCreate(icicle); 182 final Activity activity = getActivity(); 183 mSubId = activity.getIntent().getIntExtra(SUB_ID, 184 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 185 186 mIntentFilter = new IntentFilter( 187 TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 188 mIntentFilter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED); 189 190 setIfOnlyAvailableForAdmins(true); 191 192 mSubscriptionInfo = getSubscriptionInfo(mSubId); 193 mUiccController = UiccController.getInstance(); 194 195 CarrierConfigManager configManager = (CarrierConfigManager) 196 getSystemService(Context.CARRIER_CONFIG_SERVICE); 197 PersistableBundle b = configManager.getConfigForSubId(mSubId); 198 mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL); 199 mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); 200 if (mAllowAddingApns) { 201 String[] readOnlyApnTypes = b.getStringArray( 202 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY); 203 // if no apn type can be edited, do not allow adding APNs 204 if (ApnEditor.hasAllApns(readOnlyApnTypes)) { 205 Log.d(TAG, "not allowing adding APN because all APN types are read only"); 206 mAllowAddingApns = false; 207 } 208 } 209 mHidePresetApnDetails = b.getBoolean(CarrierConfigManager.KEY_HIDE_PRESET_APN_DETAILS_BOOL); 210 mUserManager = UserManager.get(activity); 211 } 212 213 @Override onActivityCreated(Bundle savedInstanceState)214 public void onActivityCreated(Bundle savedInstanceState) { 215 super.onActivityCreated(savedInstanceState); 216 217 getEmptyTextView().setText(R.string.apn_settings_not_available); 218 mUnavailable = isUiRestricted(); 219 setHasOptionsMenu(!mUnavailable); 220 if (mUnavailable) { 221 addPreferencesFromResource(R.xml.placeholder_prefs); 222 return; 223 } 224 225 addPreferencesFromResource(R.xml.apn_settings); 226 } 227 228 @Override onResume()229 public void onResume() { 230 super.onResume(); 231 232 if (mUnavailable) { 233 return; 234 } 235 236 getActivity().registerReceiver(mReceiver, mIntentFilter); 237 238 if (!mRestoreDefaultApnMode) { 239 fillList(); 240 } 241 } 242 243 @Override onPause()244 public void onPause() { 245 super.onPause(); 246 247 if (mUnavailable) { 248 return; 249 } 250 251 getActivity().unregisterReceiver(mReceiver); 252 } 253 254 @Override onDestroy()255 public void onDestroy() { 256 super.onDestroy(); 257 258 if (mRestoreDefaultApnThread != null) { 259 mRestoreDefaultApnThread.quit(); 260 } 261 } 262 263 @Override getRestrictionEnforcedAdmin()264 public EnforcedAdmin getRestrictionEnforcedAdmin() { 265 final UserHandle user = UserHandle.of(mUserManager.getUserHandle()); 266 if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, user) 267 && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, 268 user)) { 269 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 270 } 271 return null; 272 } 273 getSubscriptionInfo(int subId)274 private SubscriptionInfo getSubscriptionInfo(int subId) { 275 return SubscriptionManager.from(getActivity()).getActiveSubscriptionInfo(subId); 276 } 277 fillList()278 private void fillList() { 279 final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId() 280 : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 281 final Uri simApnUri = Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, 282 String.valueOf(subId)); 283 StringBuilder where = new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND " 284 + "user_visible!=0"); 285 286 if (mHideImsApn) { 287 where.append(" AND NOT (type='ims')"); 288 } 289 290 Cursor cursor = getContentResolver().query(simApnUri, 291 CARRIERS_PROJECTION, where.toString(), null, 292 Telephony.Carriers.DEFAULT_SORT_ORDER); 293 294 if (cursor != null) { 295 PreferenceGroup apnPrefList = (PreferenceGroup) findPreference("apn_list"); 296 apnPrefList.removeAll(); 297 298 ArrayList<ApnPreference> apnList = new ArrayList<ApnPreference>(); 299 ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>(); 300 301 mSelectedKey = getSelectedApnKey(); 302 cursor.moveToFirst(); 303 while (!cursor.isAfterLast()) { 304 String name = cursor.getString(NAME_INDEX); 305 String apn = cursor.getString(APN_INDEX); 306 String key = cursor.getString(ID_INDEX); 307 String type = cursor.getString(TYPES_INDEX); 308 int edited = cursor.getInt(EDITED_INDEX); 309 mMvnoType = cursor.getString(MVNO_TYPE_INDEX); 310 mMvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX); 311 312 ApnPreference pref = new ApnPreference(getPrefContext()); 313 314 pref.setKey(key); 315 pref.setTitle(name); 316 pref.setPersistent(false); 317 pref.setOnPreferenceChangeListener(this); 318 pref.setSubId(subId); 319 if (mHidePresetApnDetails && edited == Telephony.Carriers.UNEDITED) { 320 pref.setHideDetails(); 321 } else { 322 pref.setSummary(apn); 323 } 324 325 boolean selectable = ((type == null) || !type.equals("mms")); 326 pref.setSelectable(selectable); 327 if (selectable) { 328 if ((mSelectedKey != null) && mSelectedKey.equals(key)) { 329 pref.setChecked(); 330 } 331 apnList.add(pref); 332 } else { 333 mmsApnList.add(pref); 334 } 335 cursor.moveToNext(); 336 } 337 cursor.close(); 338 339 for (Preference preference : apnList) { 340 apnPrefList.addPreference(preference); 341 } 342 for (Preference preference : mmsApnList) { 343 apnPrefList.addPreference(preference); 344 } 345 } 346 } 347 348 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)349 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 350 if (!mUnavailable) { 351 if (mAllowAddingApns) { 352 menu.add(0, MENU_NEW, 0, 353 getResources().getString(R.string.menu_new)) 354 .setIcon(R.drawable.ic_add_24dp) 355 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 356 } 357 menu.add(0, MENU_RESTORE, 0, 358 getResources().getString(R.string.menu_restore)) 359 .setIcon(android.R.drawable.ic_menu_upload); 360 } 361 362 super.onCreateOptionsMenu(menu, inflater); 363 } 364 365 @Override onOptionsItemSelected(MenuItem item)366 public boolean onOptionsItemSelected(MenuItem item) { 367 switch (item.getItemId()) { 368 case MENU_NEW: 369 addNewApn(); 370 return true; 371 372 case MENU_RESTORE: 373 restoreDefaultApn(); 374 return true; 375 } 376 return super.onOptionsItemSelected(item); 377 } 378 addNewApn()379 private void addNewApn() { 380 Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI); 381 int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId() 382 : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 383 intent.putExtra(SUB_ID, subId); 384 if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) { 385 intent.putExtra(MVNO_TYPE, mMvnoType); 386 intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData); 387 } 388 startActivity(intent); 389 } 390 391 @Override onPreferenceChange(Preference preference, Object newValue)392 public boolean onPreferenceChange(Preference preference, Object newValue) { 393 Log.d(TAG, "onPreferenceChange(): Preference - " + preference 394 + ", newValue - " + newValue + ", newValue type - " 395 + newValue.getClass()); 396 if (newValue instanceof String) { 397 setSelectedApnKey((String) newValue); 398 } 399 400 return true; 401 } 402 setSelectedApnKey(String key)403 private void setSelectedApnKey(String key) { 404 mSelectedKey = key; 405 ContentResolver resolver = getContentResolver(); 406 407 ContentValues values = new ContentValues(); 408 values.put(APN_ID, mSelectedKey); 409 resolver.update(getUriForCurrSubId(PREFERAPN_URI), values, null, null); 410 } 411 getSelectedApnKey()412 private String getSelectedApnKey() { 413 String key = null; 414 415 Cursor cursor = getContentResolver().query(getUriForCurrSubId(PREFERAPN_URI), 416 new String[] {"_id"}, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 417 if (cursor.getCount() > 0) { 418 cursor.moveToFirst(); 419 key = cursor.getString(ID_INDEX); 420 } 421 cursor.close(); 422 return key; 423 } 424 restoreDefaultApn()425 private boolean restoreDefaultApn() { 426 showDialog(DIALOG_RESTORE_DEFAULTAPN); 427 mRestoreDefaultApnMode = true; 428 429 if (mRestoreApnUiHandler == null) { 430 mRestoreApnUiHandler = new RestoreApnUiHandler(); 431 } 432 433 if (mRestoreApnProcessHandler == null || 434 mRestoreDefaultApnThread == null) { 435 mRestoreDefaultApnThread = new HandlerThread( 436 "Restore default APN Handler: Process Thread"); 437 mRestoreDefaultApnThread.start(); 438 mRestoreApnProcessHandler = new RestoreApnProcessHandler( 439 mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler); 440 } 441 442 mRestoreApnProcessHandler 443 .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START); 444 return true; 445 } 446 447 // Append subId to the Uri getUriForCurrSubId(Uri uri)448 private Uri getUriForCurrSubId(Uri uri) { 449 int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId() 450 : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 451 if (SubscriptionManager.isValidSubscriptionId(subId)) { 452 return Uri.withAppendedPath(uri, "subId/" + String.valueOf(subId)); 453 } else { 454 return uri; 455 } 456 } 457 458 private class RestoreApnUiHandler extends Handler { 459 @Override handleMessage(Message msg)460 public void handleMessage(Message msg) { 461 switch (msg.what) { 462 case EVENT_RESTORE_DEFAULTAPN_COMPLETE: 463 Activity activity = getActivity(); 464 if (activity == null) { 465 mRestoreDefaultApnMode = false; 466 return; 467 } 468 fillList(); 469 getPreferenceScreen().setEnabled(true); 470 mRestoreDefaultApnMode = false; 471 removeDialog(DIALOG_RESTORE_DEFAULTAPN); 472 Toast.makeText( 473 activity, 474 getResources().getString( 475 R.string.restore_default_apn_completed), 476 Toast.LENGTH_LONG).show(); 477 break; 478 } 479 } 480 } 481 482 private class RestoreApnProcessHandler extends Handler { 483 private Handler mRestoreApnUiHandler; 484 RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler)485 public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) { 486 super(looper); 487 this.mRestoreApnUiHandler = restoreApnUiHandler; 488 } 489 490 @Override handleMessage(Message msg)491 public void handleMessage(Message msg) { 492 switch (msg.what) { 493 case EVENT_RESTORE_DEFAULTAPN_START: 494 ContentResolver resolver = getContentResolver(); 495 resolver.delete(getUriForCurrSubId(DEFAULTAPN_URI), null, null); 496 mRestoreApnUiHandler 497 .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE); 498 break; 499 } 500 } 501 } 502 503 @Override onCreateDialog(int id)504 public Dialog onCreateDialog(int id) { 505 if (id == DIALOG_RESTORE_DEFAULTAPN) { 506 ProgressDialog dialog = new ProgressDialog(getActivity()) { 507 public boolean onTouchEvent(MotionEvent event) { 508 return true; 509 } 510 }; 511 dialog.setMessage(getResources().getString(R.string.restore_default_apn)); 512 dialog.setCancelable(false); 513 return dialog; 514 } 515 return null; 516 } 517 518 @Override getDialogMetricsCategory(int dialogId)519 public int getDialogMetricsCategory(int dialogId) { 520 if (dialogId == DIALOG_RESTORE_DEFAULTAPN) { 521 return SettingsEnums.DIALOG_APN_RESTORE_DEFAULT; 522 } 523 return 0; 524 } 525 } 526