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