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