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