1 /* 2 * Copyright (C) 2006 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.apn; 18 19 import android.app.Dialog; 20 import android.app.settings.SettingsEnums; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.database.Cursor; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.os.PersistableBundle; 28 import android.os.UserManager; 29 import android.provider.Telephony; 30 import android.telephony.CarrierConfigManager; 31 import android.telephony.SubscriptionInfo; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.TelephonyManager; 34 import android.text.TextUtils; 35 import android.util.Log; 36 import android.view.KeyEvent; 37 import android.view.Menu; 38 import android.view.MenuInflater; 39 import android.view.MenuItem; 40 import android.view.View; 41 import android.view.View.OnKeyListener; 42 43 import androidx.annotation.Nullable; 44 import androidx.annotation.VisibleForTesting; 45 import androidx.appcompat.app.AlertDialog; 46 import androidx.preference.EditTextPreference; 47 import androidx.preference.ListPreference; 48 import androidx.preference.MultiSelectListPreference; 49 import androidx.preference.Preference; 50 import androidx.preference.Preference.OnPreferenceChangeListener; 51 import androidx.preference.TwoStatePreference; 52 53 import com.android.internal.util.ArrayUtils; 54 import com.android.settings.R; 55 import com.android.settings.SettingsPreferenceFragment; 56 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 57 import com.android.settings.network.ProxySubscriptionManager; 58 import com.android.settingslib.utils.ThreadUtils; 59 60 import java.util.Arrays; 61 import java.util.HashSet; 62 import java.util.List; 63 import java.util.Objects; 64 import java.util.Set; 65 66 /** Use to edit apn settings. */ 67 public class ApnEditor extends SettingsPreferenceFragment 68 implements OnPreferenceChangeListener, OnKeyListener { 69 70 private static final String TAG = ApnEditor.class.getSimpleName(); 71 private static final boolean VDBG = false; // STOPSHIP if true 72 73 private static final String KEY_AUTH_TYPE = "auth_type"; 74 private static final String KEY_APN_TYPE = "apn_type"; 75 private static final String KEY_PROTOCOL = "apn_protocol"; 76 private static final String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol"; 77 private static final String KEY_CARRIER_ENABLED = "carrier_enabled"; 78 private static final String KEY_BEARER_MULTI = "bearer_multi"; 79 private static final String KEY_MVNO_TYPE = "mvno_type"; 80 private static final String KEY_PASSWORD = "apn_password"; 81 82 @VisibleForTesting 83 static final int MENU_DELETE = Menu.FIRST; 84 private static final int MENU_SAVE = Menu.FIRST + 1; 85 private static final int MENU_CANCEL = Menu.FIRST + 2; 86 87 @VisibleForTesting 88 static String sNotSet; 89 @VisibleForTesting 90 EditTextPreference mName; 91 @VisibleForTesting 92 EditTextPreference mApn; 93 @VisibleForTesting 94 EditTextPreference mProxy; 95 @VisibleForTesting 96 EditTextPreference mPort; 97 @VisibleForTesting 98 EditTextPreference mUser; 99 @VisibleForTesting 100 EditTextPreference mServer; 101 @VisibleForTesting 102 EditTextPreference mPassword; 103 @VisibleForTesting 104 EditTextPreference mMmsc; 105 @VisibleForTesting 106 EditTextPreference mMcc; 107 @VisibleForTesting 108 EditTextPreference mMnc; 109 @VisibleForTesting 110 EditTextPreference mMmsProxy; 111 @VisibleForTesting 112 EditTextPreference mMmsPort; 113 @VisibleForTesting 114 ListPreference mAuthType; 115 @VisibleForTesting 116 EditTextPreference mApnType; 117 @VisibleForTesting 118 ListPreference mProtocol; 119 @VisibleForTesting 120 ListPreference mRoamingProtocol; 121 @VisibleForTesting 122 TwoStatePreference mCarrierEnabled; 123 @VisibleForTesting 124 MultiSelectListPreference mBearerMulti; 125 @VisibleForTesting 126 ListPreference mMvnoType; 127 @VisibleForTesting 128 EditTextPreference mMvnoMatchData; 129 130 @VisibleForTesting 131 ApnData mApnData; 132 133 private String mCurMnc; 134 private String mCurMcc; 135 136 private boolean mNewApn; 137 private int mSubId; 138 @VisibleForTesting 139 ProxySubscriptionManager mProxySubscriptionMgr; 140 private int mBearerInitialVal = 0; 141 private String mMvnoTypeStr; 142 private String mMvnoMatchDataStr; 143 @VisibleForTesting 144 String[] mReadOnlyApnTypes; 145 @VisibleForTesting 146 String[] mDefaultApnTypes; 147 @VisibleForTesting 148 String mDefaultApnProtocol; 149 @VisibleForTesting 150 String mDefaultApnRoamingProtocol; 151 private String[] mReadOnlyApnFields; 152 private boolean mReadOnlyApn; 153 /** 154 * The APN deletion feature within menu is aligned with the APN adding feature. 155 * Having only one of them could lead to a UX which not that make sense from user's 156 * perspective. 157 * 158 * mIsAddApnAllowed stores the configuration value reading from 159 * CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL to support the presentation 160 * control of the menu options. When false, delete option would be invisible to 161 * the end user. 162 */ 163 private boolean mIsAddApnAllowed; 164 private Uri mCarrierUri; 165 private boolean mIsCarrierIdApn; 166 167 /** 168 * APN types for data connections. These are usage categories for an APN 169 * entry. One APN entry may support multiple APN types, eg, a single APN 170 * may service regular internet traffic ("default") as well as MMS-specific 171 * connections.<br/> 172 * APN_TYPE_ALL is a special type to indicate that this APN entry can 173 * service all data connections. 174 */ 175 public static final String APN_TYPE_ALL = "*"; 176 /** APN type for default data traffic */ 177 public static final String APN_TYPE_DEFAULT = "default"; 178 /** APN type for MMS traffic */ 179 public static final String APN_TYPE_MMS = "mms"; 180 /** APN type for SUPL assisted GPS */ 181 public static final String APN_TYPE_SUPL = "supl"; 182 /** APN type for DUN traffic */ 183 public static final String APN_TYPE_DUN = "dun"; 184 /** APN type for HiPri traffic */ 185 public static final String APN_TYPE_HIPRI = "hipri"; 186 /** APN type for FOTA */ 187 public static final String APN_TYPE_FOTA = "fota"; 188 /** APN type for IMS */ 189 public static final String APN_TYPE_IMS = "ims"; 190 /** APN type for CBS */ 191 public static final String APN_TYPE_CBS = "cbs"; 192 /** APN type for IA Initial Attach APN */ 193 public static final String APN_TYPE_IA = "ia"; 194 /** APN type for Emergency PDN. This is not an IA apn, but is used 195 * for access to carrier services in an emergency call situation. */ 196 public static final String APN_TYPE_EMERGENCY = "emergency"; 197 /** APN type for Mission Critical Services */ 198 public static final String APN_TYPE_MCX = "mcx"; 199 /** APN type for XCAP */ 200 public static final String APN_TYPE_XCAP = "xcap"; 201 /** Array of all APN types */ 202 public static final String[] APN_TYPES = {APN_TYPE_DEFAULT, 203 APN_TYPE_MMS, 204 APN_TYPE_SUPL, 205 APN_TYPE_DUN, 206 APN_TYPE_HIPRI, 207 APN_TYPE_FOTA, 208 APN_TYPE_IMS, 209 APN_TYPE_CBS, 210 APN_TYPE_IA, 211 APN_TYPE_EMERGENCY, 212 APN_TYPE_MCX, 213 APN_TYPE_XCAP, 214 }; 215 216 /** 217 * Standard projection for the interesting columns of a normal note. 218 */ 219 private static final String[] sProjection = new String[] { 220 Telephony.Carriers._ID, // 0 221 Telephony.Carriers.NAME, // 1 222 Telephony.Carriers.APN, // 2 223 Telephony.Carriers.PROXY, // 3 224 Telephony.Carriers.PORT, // 4 225 Telephony.Carriers.USER, // 5 226 Telephony.Carriers.SERVER, // 6 227 Telephony.Carriers.PASSWORD, // 7 228 Telephony.Carriers.MMSC, // 8 229 Telephony.Carriers.MCC, // 9 230 Telephony.Carriers.MNC, // 10 231 Telephony.Carriers.NUMERIC, // 11 232 Telephony.Carriers.MMSPROXY, // 12 233 Telephony.Carriers.MMSPORT, // 13 234 Telephony.Carriers.AUTH_TYPE, // 14 235 Telephony.Carriers.TYPE, // 15 236 Telephony.Carriers.PROTOCOL, // 16 237 Telephony.Carriers.CARRIER_ENABLED, // 17 238 Telephony.Carriers.BEARER, // 18 239 Telephony.Carriers.BEARER_BITMASK, // 19 240 Telephony.Carriers.ROAMING_PROTOCOL, // 20 241 Telephony.Carriers.MVNO_TYPE, // 21 242 Telephony.Carriers.MVNO_MATCH_DATA, // 22 243 Telephony.Carriers.EDITED_STATUS, // 23 244 Telephony.Carriers.USER_EDITABLE, // 24 245 Telephony.Carriers.CARRIER_ID // 25 246 }; 247 248 private static final int ID_INDEX = 0; 249 @VisibleForTesting 250 static final int NAME_INDEX = 1; 251 @VisibleForTesting 252 static final int APN_INDEX = 2; 253 private static final int PROXY_INDEX = 3; 254 private static final int PORT_INDEX = 4; 255 private static final int USER_INDEX = 5; 256 private static final int SERVER_INDEX = 6; 257 private static final int PASSWORD_INDEX = 7; 258 private static final int MMSC_INDEX = 8; 259 @VisibleForTesting 260 static final int MCC_INDEX = 9; 261 @VisibleForTesting 262 static final int MNC_INDEX = 10; 263 private static final int MMSPROXY_INDEX = 12; 264 private static final int MMSPORT_INDEX = 13; 265 private static final int AUTH_TYPE_INDEX = 14; 266 @VisibleForTesting 267 static final int TYPE_INDEX = 15; 268 @VisibleForTesting 269 static final int PROTOCOL_INDEX = 16; 270 @VisibleForTesting 271 static final int CARRIER_ENABLED_INDEX = 17; 272 private static final int BEARER_INDEX = 18; 273 private static final int BEARER_BITMASK_INDEX = 19; 274 @VisibleForTesting 275 static final int ROAMING_PROTOCOL_INDEX = 20; 276 private static final int MVNO_TYPE_INDEX = 21; 277 private static final int MVNO_MATCH_DATA_INDEX = 22; 278 private static final int EDITED_INDEX = 23; 279 private static final int USER_EDITABLE_INDEX = 24; 280 private static final int CARRIER_ID_INDEX = 25; 281 282 @Override onCreate(Bundle icicle)283 public void onCreate(Bundle icicle) { 284 super.onCreate(icicle); 285 if (isUserRestricted()) { 286 Log.e(TAG, "This setting isn't available due to user restriction."); 287 finish(); 288 return; 289 } 290 291 setLifecycleForAllControllers(); 292 293 final Intent intent = getIntent(); 294 final String action = intent.getAction(); 295 if (TextUtils.isEmpty(action)) { 296 finish(); 297 return; 298 } 299 mSubId = intent.getIntExtra(ApnSettings.SUB_ID, 300 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 301 302 initApnEditorUi(); 303 getCarrierCustomizedConfig(getContext()); 304 305 Uri uri = null; 306 if (action.equals(Intent.ACTION_EDIT)) { 307 uri = intent.getData(); 308 if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { 309 Log.e(TAG, "Edit request not for carrier table. Uri: " + uri); 310 finish(); 311 return; 312 } 313 } else if (action.equals(Intent.ACTION_INSERT)) { 314 mCarrierUri = intent.getData(); 315 if (!mCarrierUri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { 316 Log.e(TAG, "Insert request not for carrier table. Uri: " + mCarrierUri); 317 finish(); 318 return; 319 } 320 mNewApn = true; 321 mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE); 322 mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA); 323 } else { 324 finish(); 325 return; 326 } 327 328 // Creates an ApnData to store the apn data temporary, so that we don't need the cursor to 329 // get the apn data. The uri is null if the action is ACTION_INSERT, that mean there is no 330 // record in the database, so create a empty ApnData to represent a empty row of database. 331 if (uri != null) { 332 mApnData = getApnDataFromUri(uri); 333 } else { 334 mApnData = new ApnData(sProjection.length); 335 } 336 final int carrierId = mApnData.getInteger(CARRIER_ID_INDEX, 337 TelephonyManager.UNKNOWN_CARRIER_ID); 338 mIsCarrierIdApn = (carrierId > TelephonyManager.UNKNOWN_CARRIER_ID); 339 340 final boolean isUserEdited = mApnData.getInteger(EDITED_INDEX, 341 Telephony.Carriers.USER_EDITED) == Telephony.Carriers.USER_EDITED; 342 343 Log.d(TAG, "onCreate: EDITED " + isUserEdited); 344 // if it's not a USER_EDITED apn, check if it's read-only 345 if (!isUserEdited && (mApnData.getInteger(USER_EDITABLE_INDEX, 1) == 0 346 || apnTypesMatch(mReadOnlyApnTypes, mApnData.getString(TYPE_INDEX)))) { 347 Log.d(TAG, "onCreate: apnTypesMatch; read-only APN"); 348 mReadOnlyApn = true; 349 disableAllFields(); 350 } else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) { 351 disableFields(mReadOnlyApnFields); 352 } 353 // Make sure that a user cannot break carrier id APN matching 354 if (mIsCarrierIdApn) { 355 disableFieldsForCarrieridApn(); 356 } 357 358 for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { 359 getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this); 360 } 361 } 362 363 /** 364 * Enable ProxySubscriptionMgr with Lifecycle support for all controllers 365 * live within this fragment 366 */ setLifecycleForAllControllers()367 private void setLifecycleForAllControllers() { 368 if (mProxySubscriptionMgr == null) { 369 mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext()); 370 } 371 mProxySubscriptionMgr.setLifecycle(getLifecycle()); 372 } 373 374 @Override onViewStateRestored(@ullable Bundle savedInstanceState)375 public void onViewStateRestored(@Nullable Bundle savedInstanceState) { 376 super.onViewStateRestored(savedInstanceState); 377 fillUI(savedInstanceState == null); 378 setCarrierCustomizedConfigToUi(); 379 } 380 381 @VisibleForTesting formatInteger(String value)382 static String formatInteger(String value) { 383 try { 384 final int intValue = Integer.parseInt(value); 385 return String.format(getCorrectDigitsFormat(value), intValue); 386 } catch (NumberFormatException e) { 387 return value; 388 } 389 } 390 391 /** 392 * Get the digits format so we preserve leading 0's. 393 * MCCs are 3 digits and MNCs are either 2 or 3. 394 */ getCorrectDigitsFormat(String value)395 static String getCorrectDigitsFormat(String value) { 396 if (value.length() == 2) return "%02d"; 397 else return "%03d"; 398 } 399 400 401 /** 402 * Check if passed in array of APN types indicates all APN types 403 * @param apnTypes array of APN types. "*" indicates all types. 404 * @return true if all apn types are included in the array, false otherwise 405 */ hasAllApns(String[] apnTypes)406 static boolean hasAllApns(String[] apnTypes) { 407 if (ArrayUtils.isEmpty(apnTypes)) { 408 return false; 409 } 410 411 final List apnList = Arrays.asList(apnTypes); 412 if (apnList.contains(APN_TYPE_ALL)) { 413 Log.d(TAG, "hasAllApns: true because apnList.contains(APN_TYPE_ALL)"); 414 return true; 415 } 416 for (String apn : APN_TYPES) { 417 if (!apnList.contains(apn)) { 418 return false; 419 } 420 } 421 422 Log.d(TAG, "hasAllApns: true"); 423 return true; 424 } 425 426 /** 427 * Check if APN types overlap. 428 * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all 429 * types 430 * @param apnTypes2 comma separated string of APN types. Empty string represents all types. 431 * @return if any apn type matches return true, otherwise return false 432 */ apnTypesMatch(String[] apnTypesArray1, String apnTypes2)433 private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) { 434 if (ArrayUtils.isEmpty(apnTypesArray1)) { 435 return false; 436 } 437 438 final String[] apnTypesArray1LowerCase = new String[apnTypesArray1.length]; 439 for (int i = 0; i < apnTypesArray1.length; i++) { 440 apnTypesArray1LowerCase[i] = apnTypesArray1[i].toLowerCase(); 441 } 442 443 if (hasAllApns(apnTypesArray1LowerCase) || TextUtils.isEmpty(apnTypes2)) { 444 return true; 445 } 446 447 final List apnTypesList1 = Arrays.asList(apnTypesArray1LowerCase); 448 final String[] apnTypesArray2 = apnTypes2.split(","); 449 450 for (String apn : apnTypesArray2) { 451 if (apnTypesList1.contains(apn.trim().toLowerCase())) { 452 Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim()); 453 return true; 454 } 455 } 456 457 Log.d(TAG, "apnTypesMatch: false"); 458 return false; 459 } 460 461 /** 462 * Function to get Preference obj corresponding to an apnField 463 * @param apnField apn field name for which pref is needed 464 * @return Preference obj corresponding to passed in apnField 465 */ getPreferenceFromFieldName(String apnField)466 private Preference getPreferenceFromFieldName(String apnField) { 467 switch (apnField) { 468 case Telephony.Carriers.NAME: 469 return mName; 470 case Telephony.Carriers.APN: 471 return mApn; 472 case Telephony.Carriers.PROXY: 473 return mProxy; 474 case Telephony.Carriers.PORT: 475 return mPort; 476 case Telephony.Carriers.USER: 477 return mUser; 478 case Telephony.Carriers.SERVER: 479 return mServer; 480 case Telephony.Carriers.PASSWORD: 481 return mPassword; 482 case Telephony.Carriers.MMSPROXY: 483 return mMmsProxy; 484 case Telephony.Carriers.MMSPORT: 485 return mMmsPort; 486 case Telephony.Carriers.MMSC: 487 return mMmsc; 488 case Telephony.Carriers.MCC: 489 return mMcc; 490 case Telephony.Carriers.MNC: 491 return mMnc; 492 case Telephony.Carriers.TYPE: 493 return mApnType; 494 case Telephony.Carriers.AUTH_TYPE: 495 return mAuthType; 496 case Telephony.Carriers.PROTOCOL: 497 return mProtocol; 498 case Telephony.Carriers.ROAMING_PROTOCOL: 499 return mRoamingProtocol; 500 case Telephony.Carriers.CARRIER_ENABLED: 501 return mCarrierEnabled; 502 case Telephony.Carriers.BEARER: 503 case Telephony.Carriers.BEARER_BITMASK: 504 return mBearerMulti; 505 case Telephony.Carriers.MVNO_TYPE: 506 return mMvnoType; 507 case Telephony.Carriers.MVNO_MATCH_DATA: 508 return mMvnoMatchData; 509 } 510 return null; 511 } 512 513 /** 514 * Disables given fields so that user cannot modify them 515 * 516 * @param apnFields fields to be disabled 517 */ disableFields(String[] apnFields)518 private void disableFields(String[] apnFields) { 519 for (String apnField : apnFields) { 520 final Preference preference = getPreferenceFromFieldName(apnField); 521 if (preference != null) { 522 preference.setEnabled(false); 523 } 524 } 525 } 526 527 /** 528 * Disables all fields so that user cannot modify the APN 529 */ disableAllFields()530 private void disableAllFields() { 531 mName.setEnabled(false); 532 mApn.setEnabled(false); 533 mProxy.setEnabled(false); 534 mPort.setEnabled(false); 535 mUser.setEnabled(false); 536 mServer.setEnabled(false); 537 mPassword.setEnabled(false); 538 mMmsProxy.setEnabled(false); 539 mMmsPort.setEnabled(false); 540 mMmsc.setEnabled(false); 541 mMcc.setEnabled(false); 542 mMnc.setEnabled(false); 543 mApnType.setEnabled(false); 544 mAuthType.setEnabled(false); 545 mProtocol.setEnabled(false); 546 mRoamingProtocol.setEnabled(false); 547 mCarrierEnabled.setEnabled(false); 548 mBearerMulti.setEnabled(false); 549 mMvnoType.setEnabled(false); 550 mMvnoMatchData.setEnabled(false); 551 } 552 553 /** 554 * Disables fields for a carrier id APN to avoid breaking the match criteria 555 */ disableFieldsForCarrieridApn()556 private void disableFieldsForCarrieridApn() { 557 mMcc.setEnabled(false); 558 mMnc.setEnabled(false); 559 mMvnoType.setEnabled(false); 560 mMvnoMatchData.setEnabled(false); 561 } 562 563 @Override getMetricsCategory()564 public int getMetricsCategory() { 565 return SettingsEnums.APN_EDITOR; 566 } 567 568 @VisibleForTesting fillUI(boolean firstTime)569 void fillUI(boolean firstTime) { 570 if (firstTime) { 571 // Fill in all the values from the db in both text editor and summary 572 mName.setText(mApnData.getString(NAME_INDEX)); 573 mApn.setText(mApnData.getString(APN_INDEX)); 574 mProxy.setText(mApnData.getString(PROXY_INDEX)); 575 mPort.setText(mApnData.getString(PORT_INDEX)); 576 mUser.setText(mApnData.getString(USER_INDEX)); 577 mServer.setText(mApnData.getString(SERVER_INDEX)); 578 mPassword.setText(mApnData.getString(PASSWORD_INDEX)); 579 mMmsProxy.setText(mApnData.getString(MMSPROXY_INDEX)); 580 mMmsPort.setText(mApnData.getString(MMSPORT_INDEX)); 581 mMmsc.setText(mApnData.getString(MMSC_INDEX)); 582 mMcc.setText(mApnData.getString(MCC_INDEX)); 583 mMnc.setText(mApnData.getString(MNC_INDEX)); 584 mApnType.setText(mApnData.getString(TYPE_INDEX)); 585 if (mNewApn) { 586 final SubscriptionInfo subInfo = 587 mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId); 588 589 // Country code 590 final String mcc = (subInfo == null) ? null : subInfo.getMccString(); 591 // Network code 592 final String mnc = (subInfo == null) ? null : subInfo.getMncString(); 593 594 if (!TextUtils.isEmpty(mcc)) { 595 // Auto populate MNC and MCC for new entries, based on what SIM reports 596 mMcc.setText(mcc); 597 mMnc.setText(mnc); 598 mCurMnc = mnc; 599 mCurMcc = mcc; 600 } 601 } 602 final int authVal = mApnData.getInteger(AUTH_TYPE_INDEX, -1); 603 if (authVal != -1) { 604 mAuthType.setValueIndex(authVal); 605 } else { 606 mAuthType.setValue(null); 607 } 608 609 mProtocol.setValue(mApnData.getString(PROTOCOL_INDEX)); 610 mRoamingProtocol.setValue(mApnData.getString(ROAMING_PROTOCOL_INDEX)); 611 mCarrierEnabled.setChecked(mApnData.getInteger(CARRIER_ENABLED_INDEX, 1) == 1); 612 mBearerInitialVal = mApnData.getInteger(BEARER_INDEX, 0); 613 614 final HashSet<String> bearers = new HashSet<String>(); 615 int bearerBitmask = mApnData.getInteger(BEARER_BITMASK_INDEX, 0); 616 if (bearerBitmask == 0) { 617 if (mBearerInitialVal == 0) { 618 bearers.add("" + 0); 619 } 620 } else { 621 int i = 1; 622 while (bearerBitmask != 0) { 623 if ((bearerBitmask & 1) == 1) { 624 bearers.add("" + i); 625 } 626 bearerBitmask >>= 1; 627 i++; 628 } 629 } 630 631 if (mBearerInitialVal != 0 && !bearers.contains("" + mBearerInitialVal)) { 632 // add mBearerInitialVal to bearers 633 bearers.add("" + mBearerInitialVal); 634 } 635 mBearerMulti.setValues(bearers); 636 637 mMvnoType.setValue(mApnData.getString(MVNO_TYPE_INDEX)); 638 mMvnoMatchData.setEnabled(false); 639 mMvnoMatchData.setText(mApnData.getString(MVNO_MATCH_DATA_INDEX)); 640 if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) { 641 mMvnoType.setValue(mMvnoTypeStr); 642 mMvnoMatchData.setText(mMvnoMatchDataStr); 643 } 644 } 645 646 mName.setSummary(checkNull(mName.getText())); 647 mApn.setSummary(checkNull(mApn.getText())); 648 mProxy.setSummary(checkNull(mProxy.getText())); 649 mPort.setSummary(checkNull(mPort.getText())); 650 mUser.setSummary(checkNull(mUser.getText())); 651 mServer.setSummary(checkNull(mServer.getText())); 652 mPassword.setSummary(starify(mPassword.getText())); 653 mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); 654 mMmsPort.setSummary(checkNull(mMmsPort.getText())); 655 mMmsc.setSummary(checkNull(mMmsc.getText())); 656 mMcc.setSummary(formatInteger(checkNull(mMcc.getText()))); 657 mMnc.setSummary(formatInteger(checkNull(mMnc.getText()))); 658 mApnType.setSummary(checkNull(mApnType.getText())); 659 660 final String authVal = mAuthType.getValue(); 661 if (authVal != null) { 662 final int authValIndex = Integer.parseInt(authVal); 663 mAuthType.setValueIndex(authValIndex); 664 665 final String[] values = getResources().getStringArray(R.array.apn_auth_entries); 666 mAuthType.setSummary(values[authValIndex]); 667 } else { 668 mAuthType.setSummary(sNotSet); 669 } 670 671 mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol))); 672 mRoamingProtocol.setSummary( 673 checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol))); 674 mBearerMulti.setSummary( 675 checkNull(bearerMultiDescription(mBearerMulti.getValues()))); 676 mMvnoType.setSummary( 677 checkNull(mvnoDescription(mMvnoType.getValue()))); 678 mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText())); 679 // allow user to edit carrier_enabled for some APN 680 final boolean ceEditable = getResources().getBoolean( 681 R.bool.config_allow_edit_carrier_enabled); 682 if (ceEditable) { 683 mCarrierEnabled.setEnabled(true); 684 } else { 685 mCarrierEnabled.setEnabled(false); 686 } 687 } 688 689 /** 690 * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given 691 * raw value of the protocol preference (e.g., "IPV4V6"). If unknown, 692 * return null. 693 */ protocolDescription(String raw, ListPreference protocol)694 private String protocolDescription(String raw, ListPreference protocol) { 695 String uRaw = checkNull(raw).toUpperCase(); 696 uRaw = uRaw.equals("IPV4") ? "IP" : uRaw; 697 final int protocolIndex = protocol.findIndexOfValue(uRaw); 698 if (protocolIndex == -1) { 699 return null; 700 } else { 701 final String[] values = getResources().getStringArray(R.array.apn_protocol_entries); 702 try { 703 return values[protocolIndex]; 704 } catch (ArrayIndexOutOfBoundsException e) { 705 return null; 706 } 707 } 708 } 709 bearerMultiDescription(Set<String> raw)710 private String bearerMultiDescription(Set<String> raw) { 711 final String[] values = getResources().getStringArray(R.array.bearer_entries); 712 final StringBuilder retVal = new StringBuilder(); 713 boolean first = true; 714 for (String bearer : raw) { 715 int bearerIndex = mBearerMulti.findIndexOfValue(bearer); 716 try { 717 if (first) { 718 retVal.append(values[bearerIndex]); 719 first = false; 720 } else { 721 retVal.append(", " + values[bearerIndex]); 722 } 723 } catch (ArrayIndexOutOfBoundsException e) { 724 // ignore 725 } 726 } 727 final String val = retVal.toString(); 728 if (!TextUtils.isEmpty(val)) { 729 return val; 730 } 731 return null; 732 } 733 mvnoDescription(String newValue)734 private String mvnoDescription(String newValue) { 735 final int mvnoIndex = mMvnoType.findIndexOfValue(newValue); 736 final String oldValue = mMvnoType.getValue(); 737 738 if (mvnoIndex == -1) { 739 return null; 740 } else { 741 final String[] values = getResources().getStringArray(R.array.mvno_type_entries); 742 final boolean mvnoMatchDataUneditable = 743 mReadOnlyApn || (mReadOnlyApnFields != null 744 && Arrays.asList(mReadOnlyApnFields) 745 .contains(Telephony.Carriers.MVNO_MATCH_DATA)); 746 mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0); 747 if (newValue != null && !newValue.equals(oldValue)) { 748 if (values[mvnoIndex].equals("SPN")) { 749 TelephonyManager telephonyManager = (TelephonyManager) 750 getContext().getSystemService(TelephonyManager.class); 751 final TelephonyManager telephonyManagerForSubId = 752 telephonyManager.createForSubscriptionId(mSubId); 753 if (telephonyManagerForSubId != null) { 754 telephonyManager = telephonyManagerForSubId; 755 } 756 mMvnoMatchData.setText(telephonyManager.getSimOperatorName()); 757 } else if (values[mvnoIndex].equals("IMSI")) { 758 final SubscriptionInfo subInfo = 759 mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId); 760 final String mcc = (subInfo == null) ? "" : 761 Objects.toString(subInfo.getMccString(), ""); 762 final String mnc = (subInfo == null) ? "" : 763 Objects.toString(subInfo.getMncString(), ""); 764 mMvnoMatchData.setText(mcc + mnc + "x"); 765 } else if (values[mvnoIndex].equals("GID")) { 766 TelephonyManager telephonyManager = (TelephonyManager) 767 getContext().getSystemService(TelephonyManager.class); 768 final TelephonyManager telephonyManagerForSubId = 769 telephonyManager.createForSubscriptionId(mSubId); 770 if (telephonyManagerForSubId != null) { 771 telephonyManager = telephonyManagerForSubId; 772 } 773 mMvnoMatchData.setText(telephonyManager.getGroupIdLevel1()); 774 } else { 775 // mvno type 'none' case. At this time, mvnoIndex should be 0. 776 mMvnoMatchData.setText(""); 777 } 778 } 779 780 try { 781 return values[mvnoIndex]; 782 } catch (ArrayIndexOutOfBoundsException e) { 783 return null; 784 } 785 } 786 } 787 /** 788 * Callback when preference status changed. 789 */ onPreferenceChange(Preference preference, Object newValue)790 public boolean onPreferenceChange(Preference preference, Object newValue) { 791 String key = preference.getKey(); 792 if (KEY_AUTH_TYPE.equals(key)) { 793 try { 794 final int index = Integer.parseInt((String) newValue); 795 mAuthType.setValueIndex(index); 796 797 final String[] values = getResources().getStringArray(R.array.apn_auth_entries); 798 mAuthType.setSummary(values[index]); 799 } catch (NumberFormatException e) { 800 return false; 801 } 802 } else if (KEY_APN_TYPE.equals(key)) { 803 String data = (TextUtils.isEmpty((String) newValue) 804 && !ArrayUtils.isEmpty(mDefaultApnTypes)) 805 ? getEditableApnType(mDefaultApnTypes) : (String) newValue; 806 if (!TextUtils.isEmpty(data)) { 807 mApnType.setSummary(data); 808 } 809 } else if (KEY_PROTOCOL.equals(key)) { 810 final String protocol = protocolDescription((String) newValue, mProtocol); 811 if (protocol == null) { 812 return false; 813 } 814 mProtocol.setSummary(protocol); 815 mProtocol.setValue((String) newValue); 816 } else if (KEY_ROAMING_PROTOCOL.equals(key)) { 817 final String protocol = protocolDescription((String) newValue, mRoamingProtocol); 818 if (protocol == null) { 819 return false; 820 } 821 mRoamingProtocol.setSummary(protocol); 822 mRoamingProtocol.setValue((String) newValue); 823 } else if (KEY_BEARER_MULTI.equals(key)) { 824 final String bearer = bearerMultiDescription((Set<String>) newValue); 825 if (bearer == null) { 826 return false; 827 } 828 mBearerMulti.setValues((Set<String>) newValue); 829 mBearerMulti.setSummary(bearer); 830 } else if (KEY_MVNO_TYPE.equals(key)) { 831 final String mvno = mvnoDescription((String) newValue); 832 if (mvno == null) { 833 return false; 834 } 835 mMvnoType.setValue((String) newValue); 836 mMvnoType.setSummary(mvno); 837 mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText())); 838 } else if (KEY_PASSWORD.equals(key)) { 839 mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : "")); 840 } else if (KEY_CARRIER_ENABLED.equals(key)) { 841 // do nothing 842 } else { 843 preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null)); 844 } 845 return true; 846 } 847 848 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)849 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 850 super.onCreateOptionsMenu(menu, inflater); 851 // If it's a new APN, then cancel will delete the new entry in onPause 852 // If APN add is not allowed, delete might lead to issue regarding recovery 853 if (!mNewApn && !mReadOnlyApn && mIsAddApnAllowed) { 854 menu.add(0, MENU_DELETE, 0, R.string.menu_delete) 855 .setIcon(R.drawable.ic_delete); 856 } 857 if (!mReadOnlyApn) { 858 menu.add(0, MENU_SAVE, 0, R.string.menu_save) 859 .setIcon(android.R.drawable.ic_menu_save); 860 } 861 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) 862 .setIcon(android.R.drawable.ic_menu_close_clear_cancel); 863 } 864 865 @Override onOptionsItemSelected(MenuItem item)866 public boolean onOptionsItemSelected(MenuItem item) { 867 switch (item.getItemId()) { 868 case MENU_DELETE: 869 deleteApn(); 870 finish(); 871 return true; 872 case MENU_SAVE: 873 if (validateAndSaveApnData()) { 874 finish(); 875 } 876 return true; 877 case MENU_CANCEL: 878 finish(); 879 return true; 880 default: 881 return super.onOptionsItemSelected(item); 882 } 883 } 884 885 @Override onViewCreated(View view, Bundle savedInstanceState)886 public void onViewCreated(View view, Bundle savedInstanceState) { 887 super.onViewCreated(view, savedInstanceState); 888 view.setOnKeyListener(this); 889 view.setFocusableInTouchMode(true); 890 view.requestFocus(); 891 } 892 893 /** 894 * Try to save the apn data when pressed the back button. An error message will be displayed if 895 * the apn data is invalid. 896 * 897 * TODO(b/77339593): Try to keep the same behavior between back button and up navigate button. 898 * We will save the valid apn data to the database when pressed the back button, but discard all 899 * user changed when pressed the up navigate button. 900 */ 901 @Override onKey(View v, int keyCode, KeyEvent event)902 public boolean onKey(View v, int keyCode, KeyEvent event) { 903 if (event.getAction() != KeyEvent.ACTION_DOWN) return false; 904 switch (keyCode) { 905 case KeyEvent.KEYCODE_BACK: { 906 if (validateAndSaveApnData()) { 907 finish(); 908 } 909 return true; 910 } 911 } 912 return false; 913 } 914 915 /** 916 * Add key, value to {@code cv} and compare the value against the value at index in 917 * {@link #mApnData}. 918 * 919 * <p> 920 * The key, value will not add to {@code cv} if value is null. 921 * 922 * @return true if values are different. {@code assumeDiff} indicates if values can be assumed 923 * different in which case no comparison is needed. 924 */ setStringValueAndCheckIfDiff( ContentValues cv, String key, String value, boolean assumeDiff, int index)925 boolean setStringValueAndCheckIfDiff( 926 ContentValues cv, String key, String value, boolean assumeDiff, int index) { 927 final String valueFromLocalCache = mApnData.getString(index); 928 if (VDBG) { 929 Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff 930 + " key: " + key 931 + " value: '" + value 932 + "' valueFromDb: '" + valueFromLocalCache + "'"); 933 } 934 final boolean isDiff = assumeDiff 935 || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromLocalCache)) 936 || (value != null && value.equals(valueFromLocalCache))); 937 938 if (isDiff && value != null) { 939 cv.put(key, value); 940 } 941 return isDiff; 942 } 943 944 /** 945 * Add key, value to {@code cv} and compare the value against the value at index in 946 * {@link #mApnData}. 947 * 948 * @return true if values are different. {@code assumeDiff} indicates if values can be assumed 949 * different in which case no comparison is needed. 950 */ setIntValueAndCheckIfDiff( ContentValues cv, String key, int value, boolean assumeDiff, int index)951 boolean setIntValueAndCheckIfDiff( 952 ContentValues cv, String key, int value, boolean assumeDiff, int index) { 953 final Integer valueFromLocalCache = mApnData.getInteger(index); 954 if (VDBG) { 955 Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff 956 + " key: " + key 957 + " value: '" + value 958 + "' valueFromDb: '" + valueFromLocalCache + "'"); 959 } 960 961 final boolean isDiff = assumeDiff || value != valueFromLocalCache; 962 if (isDiff) { 963 cv.put(key, value); 964 } 965 return isDiff; 966 } 967 968 /** 969 * Validates the apn data and save it to the database if it's valid. 970 * 971 * <p> 972 * A dialog with error message will be displayed if the APN data is invalid. 973 * 974 * @return true if there is no error 975 */ 976 @VisibleForTesting validateAndSaveApnData()977 boolean validateAndSaveApnData() { 978 // Nothing to do if it's a read only APN 979 if (mReadOnlyApn) { 980 return true; 981 } 982 983 final String name = checkNotSet(mName.getText()); 984 final String apn = checkNotSet(mApn.getText()); 985 final String mcc = checkNotSet(mMcc.getText()); 986 final String mnc = checkNotSet(mMnc.getText()); 987 988 final String errorMsg = validateApnData(); 989 if (errorMsg != null) { 990 showError(); 991 return false; 992 } 993 994 final ContentValues values = new ContentValues(); 995 // call update() if it's a new APN. If not, check if any field differs from the db value; 996 // if any diff is found update() should be called 997 boolean callUpdate = mNewApn; 998 callUpdate = setStringValueAndCheckIfDiff(values, 999 Telephony.Carriers.NAME, 1000 name, 1001 callUpdate, 1002 NAME_INDEX); 1003 1004 callUpdate = setStringValueAndCheckIfDiff(values, 1005 Telephony.Carriers.APN, 1006 apn, 1007 callUpdate, 1008 APN_INDEX); 1009 1010 callUpdate = setStringValueAndCheckIfDiff(values, 1011 Telephony.Carriers.PROXY, 1012 checkNotSet(mProxy.getText()), 1013 callUpdate, 1014 PROXY_INDEX); 1015 1016 callUpdate = setStringValueAndCheckIfDiff(values, 1017 Telephony.Carriers.PORT, 1018 checkNotSet(mPort.getText()), 1019 callUpdate, 1020 PORT_INDEX); 1021 1022 callUpdate = setStringValueAndCheckIfDiff(values, 1023 Telephony.Carriers.MMSPROXY, 1024 checkNotSet(mMmsProxy.getText()), 1025 callUpdate, 1026 MMSPROXY_INDEX); 1027 1028 callUpdate = setStringValueAndCheckIfDiff(values, 1029 Telephony.Carriers.MMSPORT, 1030 checkNotSet(mMmsPort.getText()), 1031 callUpdate, 1032 MMSPORT_INDEX); 1033 1034 callUpdate = setStringValueAndCheckIfDiff(values, 1035 Telephony.Carriers.USER, 1036 checkNotSet(mUser.getText()), 1037 callUpdate, 1038 USER_INDEX); 1039 1040 callUpdate = setStringValueAndCheckIfDiff(values, 1041 Telephony.Carriers.SERVER, 1042 checkNotSet(mServer.getText()), 1043 callUpdate, 1044 SERVER_INDEX); 1045 1046 callUpdate = setStringValueAndCheckIfDiff(values, 1047 Telephony.Carriers.PASSWORD, 1048 checkNotSet(mPassword.getText()), 1049 callUpdate, 1050 PASSWORD_INDEX); 1051 1052 callUpdate = setStringValueAndCheckIfDiff(values, 1053 Telephony.Carriers.MMSC, 1054 checkNotSet(mMmsc.getText()), 1055 callUpdate, 1056 MMSC_INDEX); 1057 1058 final String authVal = mAuthType.getValue(); 1059 if (authVal != null) { 1060 callUpdate = setIntValueAndCheckIfDiff(values, 1061 Telephony.Carriers.AUTH_TYPE, 1062 Integer.parseInt(authVal), 1063 callUpdate, 1064 AUTH_TYPE_INDEX); 1065 } 1066 1067 callUpdate = setStringValueAndCheckIfDiff(values, 1068 Telephony.Carriers.PROTOCOL, 1069 checkNotSet(mProtocol.getValue()), 1070 callUpdate, 1071 PROTOCOL_INDEX); 1072 1073 callUpdate = setStringValueAndCheckIfDiff(values, 1074 Telephony.Carriers.ROAMING_PROTOCOL, 1075 checkNotSet(mRoamingProtocol.getValue()), 1076 callUpdate, 1077 ROAMING_PROTOCOL_INDEX); 1078 1079 callUpdate = setStringValueAndCheckIfDiff(values, 1080 Telephony.Carriers.TYPE, 1081 checkNotSet(getUserEnteredApnType()), 1082 callUpdate, 1083 TYPE_INDEX); 1084 1085 callUpdate = setStringValueAndCheckIfDiff(values, 1086 Telephony.Carriers.MCC, 1087 mcc, 1088 callUpdate, 1089 MCC_INDEX); 1090 1091 callUpdate = setStringValueAndCheckIfDiff(values, 1092 Telephony.Carriers.MNC, 1093 mnc, 1094 callUpdate, 1095 MNC_INDEX); 1096 1097 values.put(Telephony.Carriers.NUMERIC, mcc + mnc); 1098 1099 if (mCurMnc != null && mCurMcc != null) { 1100 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { 1101 values.put(Telephony.Carriers.CURRENT, 1); 1102 } 1103 } 1104 1105 final Set<String> bearerSet = mBearerMulti.getValues(); 1106 int bearerBitmask = 0; 1107 for (String bearer : bearerSet) { 1108 if (Integer.parseInt(bearer) == 0) { 1109 bearerBitmask = 0; 1110 break; 1111 } else { 1112 bearerBitmask |= getBitmaskForTech(Integer.parseInt(bearer)); 1113 } 1114 } 1115 callUpdate = setIntValueAndCheckIfDiff(values, 1116 Telephony.Carriers.BEARER_BITMASK, 1117 bearerBitmask, 1118 callUpdate, 1119 BEARER_BITMASK_INDEX); 1120 1121 int bearerVal; 1122 if (bearerBitmask == 0 || mBearerInitialVal == 0) { 1123 bearerVal = 0; 1124 } else if (bitmaskHasTech(bearerBitmask, mBearerInitialVal)) { 1125 bearerVal = mBearerInitialVal; 1126 } else { 1127 // bearer field was being used but bitmask has changed now and does not include the 1128 // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a 1129 // random tech from the new bitmask?? 1130 bearerVal = 0; 1131 } 1132 callUpdate = setIntValueAndCheckIfDiff(values, 1133 Telephony.Carriers.BEARER, 1134 bearerVal, 1135 callUpdate, 1136 BEARER_INDEX); 1137 1138 callUpdate = setStringValueAndCheckIfDiff(values, 1139 Telephony.Carriers.MVNO_TYPE, 1140 checkNotSet(mMvnoType.getValue()), 1141 callUpdate, 1142 MVNO_TYPE_INDEX); 1143 1144 callUpdate = setStringValueAndCheckIfDiff(values, 1145 Telephony.Carriers.MVNO_MATCH_DATA, 1146 checkNotSet(mMvnoMatchData.getText()), 1147 callUpdate, 1148 MVNO_MATCH_DATA_INDEX); 1149 1150 callUpdate = setIntValueAndCheckIfDiff(values, 1151 Telephony.Carriers.CARRIER_ENABLED, 1152 mCarrierEnabled.isChecked() ? 1 : 0, 1153 callUpdate, 1154 CARRIER_ENABLED_INDEX); 1155 1156 values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED); 1157 1158 if (callUpdate) { 1159 final Uri uri = mApnData.getUri() == null ? mCarrierUri : mApnData.getUri(); 1160 updateApnDataToDatabase(uri, values); 1161 } else { 1162 if (VDBG) Log.d(TAG, "validateAndSaveApnData: not calling update()"); 1163 } 1164 1165 return true; 1166 } 1167 updateApnDataToDatabase(Uri uri, ContentValues values)1168 private void updateApnDataToDatabase(Uri uri, ContentValues values) { 1169 ThreadUtils.postOnBackgroundThread(() -> { 1170 if (uri.equals(mCarrierUri)) { 1171 // Add a new apn to the database 1172 final Uri newUri = getContentResolver().insert(mCarrierUri, values); 1173 if (newUri == null) { 1174 Log.e(TAG, "Can't add a new apn to database " + mCarrierUri); 1175 } 1176 } else { 1177 // Update the existing apn 1178 getContentResolver().update( 1179 uri, values, null /* where */, null /* selection Args */); 1180 } 1181 }); 1182 } 1183 1184 /** 1185 * Validates whether the apn data is valid. 1186 * 1187 * @return An error message if the apn data is invalid, otherwise return null. 1188 */ 1189 @VisibleForTesting validateApnData()1190 String validateApnData() { 1191 String errorMsg = null; 1192 1193 final String name = checkNotSet(mName.getText()); 1194 final String apn = checkNotSet(mApn.getText()); 1195 final String mcc = checkNotSet(mMcc.getText()); 1196 final String mnc = checkNotSet(mMnc.getText()); 1197 boolean doNotCheckMccMnc = mIsCarrierIdApn && TextUtils.isEmpty(mcc) 1198 && TextUtils.isEmpty(mnc); 1199 if (TextUtils.isEmpty(name)) { 1200 errorMsg = getResources().getString(R.string.error_name_empty); 1201 } else if (TextUtils.isEmpty(apn)) { 1202 errorMsg = getResources().getString(R.string.error_apn_empty); 1203 } else if (doNotCheckMccMnc) { 1204 Log.d(TAG, "validateApnData: carrier id APN does not have mcc/mnc defined"); 1205 // no op, skip mcc mnc null check 1206 } else if (mcc == null || mcc.length() != 3) { 1207 errorMsg = getResources().getString(R.string.error_mcc_not3); 1208 } else if ((mnc == null || (mnc.length() & 0xFFFE) != 2)) { 1209 errorMsg = getResources().getString(R.string.error_mnc_not23); 1210 } 1211 1212 if (errorMsg == null) { 1213 // if carrier does not allow editing certain apn types, make sure type does not include 1214 // those 1215 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes) 1216 && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) { 1217 final StringBuilder stringBuilder = new StringBuilder(); 1218 for (String type : mReadOnlyApnTypes) { 1219 stringBuilder.append(type).append(", "); 1220 Log.d(TAG, "validateApnData: appending type: " + type); 1221 } 1222 // remove last ", " 1223 if (stringBuilder.length() >= 2) { 1224 stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length()); 1225 } 1226 errorMsg = String.format(getResources().getString(R.string.error_adding_apn_type), 1227 stringBuilder); 1228 } 1229 } 1230 1231 return errorMsg; 1232 } 1233 1234 @VisibleForTesting showError()1235 void showError() { 1236 ErrorDialog.showError(this); 1237 } 1238 deleteApn()1239 private void deleteApn() { 1240 if (mApnData.getUri() != null) { 1241 getContentResolver().delete(mApnData.getUri(), null, null); 1242 mApnData = new ApnData(sProjection.length); 1243 } 1244 } 1245 starify(String value)1246 private String starify(String value) { 1247 if (value == null || value.length() == 0) { 1248 return sNotSet; 1249 } else { 1250 final char[] password = new char[value.length()]; 1251 for (int i = 0; i < password.length; i++) { 1252 password[i] = '*'; 1253 } 1254 return new String(password); 1255 } 1256 } 1257 1258 /** 1259 * Returns {@link #sNotSet} if the given string {@code value} is null or empty. The string 1260 * {@link #sNotSet} typically used as the default display when an entry in the preference is 1261 * null or empty. 1262 */ checkNull(String value)1263 private String checkNull(String value) { 1264 return TextUtils.isEmpty(value) ? sNotSet : value; 1265 } 1266 1267 /** 1268 * To make traslation be diversity, use another string id for MVNO value. 1269 */ checkNullforMvnoValue(String value)1270 private String checkNullforMvnoValue(String value) { 1271 String notSetForMvnoValue = getResources().getString(R.string.apn_not_set_for_mvno); 1272 return TextUtils.isEmpty(value) ? notSetForMvnoValue : value; 1273 } 1274 1275 /** 1276 * Returns null if the given string {@code value} equals to {@link #sNotSet}. This method 1277 * should be used when convert a string value from preference to database. 1278 */ checkNotSet(String value)1279 private String checkNotSet(String value) { 1280 return sNotSet.equals(value) ? null : value; 1281 } 1282 1283 @VisibleForTesting getUserEnteredApnType()1284 String getUserEnteredApnType() { 1285 // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY" 1286 // but if user enter empty type, map it just for default 1287 String userEnteredApnType = mApnType.getText(); 1288 if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim(); 1289 if ((TextUtils.isEmpty(userEnteredApnType) 1290 || APN_TYPE_ALL.equals(userEnteredApnType))) { 1291 userEnteredApnType = getEditableApnType(APN_TYPES); 1292 } 1293 Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: " 1294 + userEnteredApnType); 1295 return userEnteredApnType; 1296 } 1297 getEditableApnType(String[] apnTypeList)1298 private String getEditableApnType(String[] apnTypeList) { 1299 final StringBuilder editableApnTypes = new StringBuilder(); 1300 final List<String> readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes); 1301 boolean first = true; 1302 for (String apnType : apnTypeList) { 1303 // add APN type if it is not read-only and is not wild-cardable 1304 if (!readOnlyApnTypes.contains(apnType) 1305 && !apnType.equals(APN_TYPE_IA) 1306 && !apnType.equals(APN_TYPE_EMERGENCY) 1307 && !apnType.equals(APN_TYPE_MCX) 1308 && !apnType.equals(APN_TYPE_IMS)) { 1309 if (first) { 1310 first = false; 1311 } else { 1312 editableApnTypes.append(","); 1313 } 1314 editableApnTypes.append(apnType); 1315 } 1316 } 1317 return editableApnTypes.toString(); 1318 } 1319 initApnEditorUi()1320 private void initApnEditorUi() { 1321 addPreferencesFromResource(R.xml.apn_editor); 1322 1323 sNotSet = getResources().getString(R.string.apn_not_set); 1324 mName = (EditTextPreference) findPreference("apn_name"); 1325 mApn = (EditTextPreference) findPreference("apn_apn"); 1326 mProxy = (EditTextPreference) findPreference("apn_http_proxy"); 1327 mPort = (EditTextPreference) findPreference("apn_http_port"); 1328 mUser = (EditTextPreference) findPreference("apn_user"); 1329 mServer = (EditTextPreference) findPreference("apn_server"); 1330 mPassword = (EditTextPreference) findPreference(KEY_PASSWORD); 1331 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); 1332 mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); 1333 mMmsc = (EditTextPreference) findPreference("apn_mmsc"); 1334 mMcc = (EditTextPreference) findPreference("apn_mcc"); 1335 mMnc = (EditTextPreference) findPreference("apn_mnc"); 1336 mApnType = (EditTextPreference) findPreference("apn_type"); 1337 mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE); 1338 mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); 1339 mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL); 1340 mCarrierEnabled = (TwoStatePreference) findPreference(KEY_CARRIER_ENABLED); 1341 mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI); 1342 mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE); 1343 mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data"); 1344 } 1345 1346 @VisibleForTesting getCarrierCustomizedConfig(Context context)1347 protected void getCarrierCustomizedConfig(Context context) { 1348 mReadOnlyApn = false; 1349 mReadOnlyApnTypes = null; 1350 mReadOnlyApnFields = null; 1351 mIsAddApnAllowed = true; 1352 1353 final CarrierConfigManager configManager = (CarrierConfigManager) 1354 context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 1355 if (configManager != null) { 1356 final PersistableBundle b = configManager.getConfigForSubId(mSubId); 1357 if (b != null) { 1358 mReadOnlyApnTypes = b.getStringArray( 1359 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY); 1360 if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) { 1361 Log.d(TAG, 1362 "onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes)); 1363 } 1364 mReadOnlyApnFields = b.getStringArray( 1365 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY); 1366 1367 mDefaultApnTypes = b.getStringArray( 1368 CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY); 1369 1370 if (!ArrayUtils.isEmpty(mDefaultApnTypes)) { 1371 Log.d(TAG, "onCreate: default apn types: " + Arrays.toString(mDefaultApnTypes)); 1372 } 1373 1374 mDefaultApnProtocol = b.getString( 1375 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING); 1376 if (!TextUtils.isEmpty(mDefaultApnProtocol)) { 1377 Log.d(TAG, "onCreate: default apn protocol: " + mDefaultApnProtocol); 1378 } 1379 1380 mDefaultApnRoamingProtocol = b.getString( 1381 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING); 1382 if (!TextUtils.isEmpty(mDefaultApnRoamingProtocol)) { 1383 Log.d(TAG, "onCreate: default apn roaming protocol: " 1384 + mDefaultApnRoamingProtocol); 1385 } 1386 1387 mIsAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); 1388 if (!mIsAddApnAllowed) { 1389 Log.d(TAG, "onCreate: not allow to add new APN"); 1390 } 1391 } 1392 } 1393 } 1394 setCarrierCustomizedConfigToUi()1395 private void setCarrierCustomizedConfigToUi() { 1396 if (TextUtils.isEmpty(mApnType.getText()) && !ArrayUtils.isEmpty(mDefaultApnTypes)) { 1397 String value = getEditableApnType(mDefaultApnTypes); 1398 mApnType.setText(value); 1399 mApnType.setSummary(value); 1400 } 1401 1402 String protocol = protocolDescription(mDefaultApnProtocol, mProtocol); 1403 if (TextUtils.isEmpty(mProtocol.getValue()) && !TextUtils.isEmpty(protocol)) { 1404 mProtocol.setValue(mDefaultApnProtocol); 1405 mProtocol.setSummary(protocol); 1406 } 1407 1408 String roamingProtocol = protocolDescription(mDefaultApnRoamingProtocol, mRoamingProtocol); 1409 if (TextUtils.isEmpty(mRoamingProtocol.getValue()) && !TextUtils.isEmpty(roamingProtocol)) { 1410 mRoamingProtocol.setValue(mDefaultApnRoamingProtocol); 1411 mRoamingProtocol.setSummary(roamingProtocol); 1412 } 1413 } 1414 1415 /** 1416 * Dialog of error message. 1417 */ 1418 public static class ErrorDialog extends InstrumentedDialogFragment { 1419 /** 1420 * Show error dialog. 1421 */ showError(ApnEditor editor)1422 public static void showError(ApnEditor editor) { 1423 final ErrorDialog dialog = new ErrorDialog(); 1424 dialog.setTargetFragment(editor, 0); 1425 dialog.show(editor.getFragmentManager(), "error"); 1426 } 1427 1428 @Override onCreateDialog(Bundle savedInstanceState)1429 public Dialog onCreateDialog(Bundle savedInstanceState) { 1430 final String msg = ((ApnEditor) getTargetFragment()).validateApnData(); 1431 1432 return new AlertDialog.Builder(getContext()) 1433 .setTitle(R.string.error_title) 1434 .setPositiveButton(android.R.string.ok, null) 1435 .setMessage(msg) 1436 .create(); 1437 } 1438 1439 @Override getMetricsCategory()1440 public int getMetricsCategory() { 1441 return SettingsEnums.DIALOG_APN_EDITOR_ERROR; 1442 } 1443 } 1444 1445 @VisibleForTesting getApnDataFromUri(Uri uri)1446 ApnData getApnDataFromUri(Uri uri) { 1447 ApnData apnData = null; 1448 try (Cursor cursor = getContentResolver().query( 1449 uri, 1450 sProjection, 1451 null /* selection */, 1452 null /* selectionArgs */, 1453 null /* sortOrder */)) { 1454 if (cursor != null && cursor.moveToFirst()) { 1455 apnData = new ApnData(uri, cursor); 1456 } 1457 } 1458 1459 if (apnData == null) { 1460 Log.d(TAG, "Can't get apnData from Uri " + uri); 1461 } 1462 1463 return apnData; 1464 } 1465 1466 @VisibleForTesting isUserRestricted()1467 boolean isUserRestricted() { 1468 UserManager userManager = getContext().getSystemService(UserManager.class); 1469 if (userManager == null) { 1470 return false; 1471 } 1472 if (!userManager.isAdminUser()) { 1473 Log.e(TAG, "User is not an admin"); 1474 return true; 1475 } 1476 if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { 1477 Log.e(TAG, "User is not allowed to configure mobile network"); 1478 return true; 1479 } 1480 return false; 1481 } 1482 1483 @VisibleForTesting 1484 static class ApnData { 1485 /** 1486 * The uri correspond to a database row of the apn data. This should be null if the apn 1487 * is not in the database. 1488 */ 1489 Uri mUri; 1490 1491 /** Each element correspond to a column of the database row. */ 1492 Object[] mData; 1493 ApnData(int numberOfField)1494 ApnData(int numberOfField) { 1495 mData = new Object[numberOfField]; 1496 } 1497 ApnData(Uri uri, Cursor cursor)1498 ApnData(Uri uri, Cursor cursor) { 1499 mUri = uri; 1500 mData = new Object[cursor.getColumnCount()]; 1501 for (int i = 0; i < mData.length; i++) { 1502 switch (cursor.getType(i)) { 1503 case Cursor.FIELD_TYPE_FLOAT: 1504 mData[i] = cursor.getFloat(i); 1505 break; 1506 case Cursor.FIELD_TYPE_INTEGER: 1507 mData[i] = cursor.getInt(i); 1508 break; 1509 case Cursor.FIELD_TYPE_STRING: 1510 mData[i] = cursor.getString(i); 1511 break; 1512 case Cursor.FIELD_TYPE_BLOB: 1513 mData[i] = cursor.getBlob(i); 1514 break; 1515 default: 1516 mData[i] = null; 1517 } 1518 } 1519 } 1520 getUri()1521 Uri getUri() { 1522 return mUri; 1523 } 1524 setUri(Uri uri)1525 void setUri(Uri uri) { 1526 mUri = uri; 1527 } 1528 getInteger(int index)1529 Integer getInteger(int index) { 1530 return (Integer) mData[index]; 1531 } 1532 getInteger(int index, Integer defaultValue)1533 Integer getInteger(int index, Integer defaultValue) { 1534 final Integer val = getInteger(index); 1535 return val == null ? defaultValue : val; 1536 } 1537 getString(int index)1538 String getString(int index) { 1539 return (String) mData[index]; 1540 } 1541 } 1542 getBitmaskForTech(int radioTech)1543 private static int getBitmaskForTech(int radioTech) { 1544 if (radioTech >= 1) { 1545 return (1 << (radioTech - 1)); 1546 } 1547 return 0; 1548 } 1549 bitmaskHasTech(int bearerBitmask, int radioTech)1550 private static boolean bitmaskHasTech(int bearerBitmask, int radioTech) { 1551 if (bearerBitmask == 0) { 1552 return true; 1553 } else if (radioTech >= 1) { 1554 return ((bearerBitmask & (1 << (radioTech - 1))) != 0); 1555 } 1556 return false; 1557 } 1558 } 1559