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