1 /* 2 * Copyright (C) 2008 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; 18 19 import android.app.Activity; 20 import android.app.Dialog; 21 import android.app.ProgressDialog; 22 import android.content.BroadcastReceiver; 23 import android.content.ContentResolver; 24 import android.content.ContentUris; 25 import android.content.ContentValues; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.database.Cursor; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.os.Message; 36 import android.os.PersistableBundle; 37 import android.os.UserHandle; 38 import android.os.UserManager; 39 import android.preference.Preference; 40 import android.preference.PreferenceGroup; 41 import android.preference.PreferenceScreen; 42 import android.provider.Telephony; 43 import android.telephony.CarrierConfigManager; 44 import android.telephony.SubscriptionInfo; 45 import android.telephony.SubscriptionManager; 46 import android.text.TextUtils; 47 import android.util.Log; 48 import android.view.Menu; 49 import android.view.MenuInflater; 50 import android.view.MenuItem; 51 import android.widget.TextView; 52 import android.widget.Toast; 53 54 import com.android.internal.logging.MetricsLogger; 55 import com.android.internal.telephony.PhoneConstants; 56 import com.android.internal.telephony.TelephonyIntents; 57 import com.android.internal.telephony.dataconnection.ApnSetting; 58 import com.android.internal.telephony.uicc.IccRecords; 59 import com.android.internal.telephony.uicc.UiccController; 60 61 import android.telephony.TelephonyManager; 62 63 import java.util.ArrayList; 64 65 public class ApnSettings extends SettingsPreferenceFragment implements 66 Preference.OnPreferenceChangeListener { 67 static final String TAG = "ApnSettings"; 68 69 public static final String EXTRA_POSITION = "position"; 70 public static final String RESTORE_CARRIERS_URI = 71 "content://telephony/carriers/restore"; 72 public static final String PREFERRED_APN_URI = 73 "content://telephony/carriers/preferapn"; 74 75 public static final String APN_ID = "apn_id"; 76 public static final String SUB_ID = "sub_id"; 77 public static final String MVNO_TYPE = "mvno_type"; 78 public static final String MVNO_MATCH_DATA = "mvno_match_data"; 79 80 private static final int ID_INDEX = 0; 81 private static final int NAME_INDEX = 1; 82 private static final int APN_INDEX = 2; 83 private static final int TYPES_INDEX = 3; 84 private static final int MVNO_TYPE_INDEX = 4; 85 private static final int MVNO_MATCH_DATA_INDEX = 5; 86 87 private static final int MENU_NEW = Menu.FIRST; 88 private static final int MENU_RESTORE = Menu.FIRST + 1; 89 90 private static final int EVENT_RESTORE_DEFAULTAPN_START = 1; 91 private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2; 92 93 private static final int DIALOG_RESTORE_DEFAULTAPN = 1001; 94 95 private static final Uri DEFAULTAPN_URI = Uri.parse(RESTORE_CARRIERS_URI); 96 private static final Uri PREFERAPN_URI = Uri.parse(PREFERRED_APN_URI); 97 98 private static boolean mRestoreDefaultApnMode; 99 100 private RestoreApnUiHandler mRestoreApnUiHandler; 101 private RestoreApnProcessHandler mRestoreApnProcessHandler; 102 private HandlerThread mRestoreDefaultApnThread; 103 private SubscriptionInfo mSubscriptionInfo; 104 private UiccController mUiccController; 105 private String mMvnoType; 106 private String mMvnoMatchData; 107 108 private UserManager mUm; 109 110 private String mSelectedKey; 111 112 private IntentFilter mMobileStateFilter; 113 114 private boolean mUnavailable; 115 116 private boolean mHideImsApn; 117 private boolean mAllowAddingApns; 118 119 private final BroadcastReceiver mMobileStateReceiver = new BroadcastReceiver() { 120 @Override 121 public void onReceive(Context context, Intent intent) { 122 if (intent.getAction().equals( 123 TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 124 PhoneConstants.DataState state = getMobileDataState(intent); 125 switch (state) { 126 case CONNECTED: 127 if (!mRestoreDefaultApnMode) { 128 fillList(); 129 } else { 130 showDialog(DIALOG_RESTORE_DEFAULTAPN); 131 } 132 break; 133 } 134 } 135 } 136 }; 137 getMobileDataState(Intent intent)138 private static PhoneConstants.DataState getMobileDataState(Intent intent) { 139 String str = intent.getStringExtra(PhoneConstants.STATE_KEY); 140 if (str != null) { 141 return Enum.valueOf(PhoneConstants.DataState.class, str); 142 } else { 143 return PhoneConstants.DataState.DISCONNECTED; 144 } 145 } 146 147 @Override getMetricsCategory()148 protected int getMetricsCategory() { 149 return MetricsLogger.APN; 150 } 151 152 @Override onCreate(Bundle icicle)153 public void onCreate(Bundle icicle) { 154 super.onCreate(icicle); 155 final Activity activity = getActivity(); 156 final int subId = activity.getIntent().getIntExtra(SUB_ID, 157 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 158 159 mUm = (UserManager) getSystemService(Context.USER_SERVICE); 160 161 mMobileStateFilter = new IntentFilter( 162 TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 163 164 if (!mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { 165 setHasOptionsMenu(true); 166 } 167 168 mSubscriptionInfo = SubscriptionManager.from(activity).getActiveSubscriptionInfo(subId); 169 mUiccController = UiccController.getInstance(); 170 171 CarrierConfigManager configManager = (CarrierConfigManager) 172 getSystemService(Context.CARRIER_CONFIG_SERVICE); 173 PersistableBundle b = configManager.getConfig(); 174 mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL); 175 mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); 176 } 177 178 @Override onActivityCreated(Bundle savedInstanceState)179 public void onActivityCreated(Bundle savedInstanceState) { 180 super.onActivityCreated(savedInstanceState); 181 182 TextView empty = (TextView) getView().findViewById(android.R.id.empty); 183 if (empty != null) { 184 empty.setText(R.string.apn_settings_not_available); 185 getListView().setEmptyView(empty); 186 } 187 188 if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) 189 || UserHandle.myUserId()!= UserHandle.USER_OWNER) { 190 mUnavailable = true; 191 setPreferenceScreen(new PreferenceScreen(getActivity(), null)); 192 return; 193 } 194 195 addPreferencesFromResource(R.xml.apn_settings); 196 197 getListView().setItemsCanFocus(true); 198 } 199 200 @Override onResume()201 public void onResume() { 202 super.onResume(); 203 204 if (mUnavailable) { 205 return; 206 } 207 208 getActivity().registerReceiver(mMobileStateReceiver, mMobileStateFilter); 209 210 if (!mRestoreDefaultApnMode) { 211 fillList(); 212 } 213 } 214 215 @Override onPause()216 public void onPause() { 217 super.onPause(); 218 219 if (mUnavailable) { 220 return; 221 } 222 223 getActivity().unregisterReceiver(mMobileStateReceiver); 224 } 225 226 @Override onDestroy()227 public void onDestroy() { 228 super.onDestroy(); 229 230 if (mRestoreDefaultApnThread != null) { 231 mRestoreDefaultApnThread.quit(); 232 } 233 } 234 fillList()235 private void fillList() { 236 final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 237 final String mccmnc = mSubscriptionInfo == null ? "" 238 : tm.getSimOperator(mSubscriptionInfo.getSubscriptionId()); 239 Log.d(TAG, "mccmnc = " + mccmnc); 240 StringBuilder where = new StringBuilder("numeric=\"" + mccmnc + 241 "\" AND NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND user_visible!=0"); 242 243 if (mHideImsApn) { 244 where.append(" AND NOT (type='ims')"); 245 } 246 247 Cursor cursor = getContentResolver().query(Telephony.Carriers.CONTENT_URI, new String[] { 248 "_id", "name", "apn", "type", "mvno_type", "mvno_match_data"}, where.toString(), 249 null, Telephony.Carriers.DEFAULT_SORT_ORDER); 250 251 if (cursor != null) { 252 IccRecords r = null; 253 if (mUiccController != null && mSubscriptionInfo != null) { 254 r = mUiccController.getIccRecords(SubscriptionManager.getPhoneId( 255 mSubscriptionInfo.getSubscriptionId()), UiccController.APP_FAM_3GPP); 256 } 257 PreferenceGroup apnList = (PreferenceGroup) findPreference("apn_list"); 258 apnList.removeAll(); 259 260 ArrayList<ApnPreference> mnoApnList = new ArrayList<ApnPreference>(); 261 ArrayList<ApnPreference> mvnoApnList = new ArrayList<ApnPreference>(); 262 ArrayList<ApnPreference> mnoMmsApnList = new ArrayList<ApnPreference>(); 263 ArrayList<ApnPreference> mvnoMmsApnList = new ArrayList<ApnPreference>(); 264 265 mSelectedKey = getSelectedApnKey(); 266 cursor.moveToFirst(); 267 while (!cursor.isAfterLast()) { 268 String name = cursor.getString(NAME_INDEX); 269 String apn = cursor.getString(APN_INDEX); 270 String key = cursor.getString(ID_INDEX); 271 String type = cursor.getString(TYPES_INDEX); 272 String mvnoType = cursor.getString(MVNO_TYPE_INDEX); 273 String mvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX); 274 275 ApnPreference pref = new ApnPreference(getActivity()); 276 277 pref.setKey(key); 278 pref.setTitle(name); 279 pref.setSummary(apn); 280 pref.setPersistent(false); 281 pref.setOnPreferenceChangeListener(this); 282 283 boolean selectable = ((type == null) || !type.equals("mms")); 284 pref.setSelectable(selectable); 285 if (selectable) { 286 if ((mSelectedKey != null) && mSelectedKey.equals(key)) { 287 pref.setChecked(); 288 } 289 addApnToList(pref, mnoApnList, mvnoApnList, r, mvnoType, mvnoMatchData); 290 } else { 291 addApnToList(pref, mnoMmsApnList, mvnoMmsApnList, r, mvnoType, mvnoMatchData); 292 } 293 cursor.moveToNext(); 294 } 295 cursor.close(); 296 297 if (!mvnoApnList.isEmpty()) { 298 mnoApnList = mvnoApnList; 299 mnoMmsApnList = mvnoMmsApnList; 300 301 // Also save the mvno info 302 } 303 304 for (Preference preference : mnoApnList) { 305 apnList.addPreference(preference); 306 } 307 for (Preference preference : mnoMmsApnList) { 308 apnList.addPreference(preference); 309 } 310 } 311 } 312 addApnToList(ApnPreference pref, ArrayList<ApnPreference> mnoList, ArrayList<ApnPreference> mvnoList, IccRecords r, String mvnoType, String mvnoMatchData)313 private void addApnToList(ApnPreference pref, ArrayList<ApnPreference> mnoList, 314 ArrayList<ApnPreference> mvnoList, IccRecords r, String mvnoType, 315 String mvnoMatchData) { 316 if (r != null && !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData)) { 317 if (ApnSetting.mvnoMatches(r, mvnoType, mvnoMatchData)) { 318 mvnoList.add(pref); 319 // Since adding to mvno list, save mvno info 320 mMvnoType = mvnoType; 321 mMvnoMatchData = mvnoMatchData; 322 } 323 } else { 324 mnoList.add(pref); 325 } 326 } 327 328 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)329 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 330 if (!mUnavailable) { 331 if (mAllowAddingApns) { 332 menu.add(0, MENU_NEW, 0, 333 getResources().getString(R.string.menu_new)) 334 .setIcon(android.R.drawable.ic_menu_add) 335 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 336 } 337 menu.add(0, MENU_RESTORE, 0, 338 getResources().getString(R.string.menu_restore)) 339 .setIcon(android.R.drawable.ic_menu_upload); 340 } 341 342 super.onCreateOptionsMenu(menu, inflater); 343 } 344 345 @Override onOptionsItemSelected(MenuItem item)346 public boolean onOptionsItemSelected(MenuItem item) { 347 switch (item.getItemId()) { 348 case MENU_NEW: 349 addNewApn(); 350 return true; 351 352 case MENU_RESTORE: 353 restoreDefaultApn(); 354 return true; 355 } 356 return super.onOptionsItemSelected(item); 357 } 358 addNewApn()359 private void addNewApn() { 360 Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI); 361 int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId() 362 : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 363 intent.putExtra(SUB_ID, subId); 364 if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) { 365 intent.putExtra(MVNO_TYPE, mMvnoType); 366 intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData); 367 } 368 startActivity(intent); 369 } 370 371 @Override onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)372 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 373 int pos = Integer.parseInt(preference.getKey()); 374 Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos); 375 startActivity(new Intent(Intent.ACTION_EDIT, url)); 376 return true; 377 } 378 onPreferenceChange(Preference preference, Object newValue)379 public boolean onPreferenceChange(Preference preference, Object newValue) { 380 Log.d(TAG, "onPreferenceChange(): Preference - " + preference 381 + ", newValue - " + newValue + ", newValue type - " 382 + newValue.getClass()); 383 if (newValue instanceof String) { 384 setSelectedApnKey((String) newValue); 385 } 386 387 return true; 388 } 389 setSelectedApnKey(String key)390 private void setSelectedApnKey(String key) { 391 mSelectedKey = key; 392 ContentResolver resolver = getContentResolver(); 393 394 ContentValues values = new ContentValues(); 395 values.put(APN_ID, mSelectedKey); 396 resolver.update(PREFERAPN_URI, values, null, null); 397 } 398 getSelectedApnKey()399 private String getSelectedApnKey() { 400 String key = null; 401 402 Cursor cursor = getContentResolver().query(PREFERAPN_URI, new String[] {"_id"}, 403 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 404 if (cursor.getCount() > 0) { 405 cursor.moveToFirst(); 406 key = cursor.getString(ID_INDEX); 407 } 408 cursor.close(); 409 return key; 410 } 411 restoreDefaultApn()412 private boolean restoreDefaultApn() { 413 showDialog(DIALOG_RESTORE_DEFAULTAPN); 414 mRestoreDefaultApnMode = true; 415 416 if (mRestoreApnUiHandler == null) { 417 mRestoreApnUiHandler = new RestoreApnUiHandler(); 418 } 419 420 if (mRestoreApnProcessHandler == null || 421 mRestoreDefaultApnThread == null) { 422 mRestoreDefaultApnThread = new HandlerThread( 423 "Restore default APN Handler: Process Thread"); 424 mRestoreDefaultApnThread.start(); 425 mRestoreApnProcessHandler = new RestoreApnProcessHandler( 426 mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler); 427 } 428 429 mRestoreApnProcessHandler 430 .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START); 431 return true; 432 } 433 434 private class RestoreApnUiHandler extends Handler { 435 @Override handleMessage(Message msg)436 public void handleMessage(Message msg) { 437 switch (msg.what) { 438 case EVENT_RESTORE_DEFAULTAPN_COMPLETE: 439 Activity activity = getActivity(); 440 if (activity == null) { 441 mRestoreDefaultApnMode = false; 442 return; 443 } 444 fillList(); 445 getPreferenceScreen().setEnabled(true); 446 mRestoreDefaultApnMode = false; 447 removeDialog(DIALOG_RESTORE_DEFAULTAPN); 448 Toast.makeText( 449 activity, 450 getResources().getString( 451 R.string.restore_default_apn_completed), 452 Toast.LENGTH_LONG).show(); 453 break; 454 } 455 } 456 } 457 458 private class RestoreApnProcessHandler extends Handler { 459 private Handler mRestoreApnUiHandler; 460 RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler)461 public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) { 462 super(looper); 463 this.mRestoreApnUiHandler = restoreApnUiHandler; 464 } 465 466 @Override handleMessage(Message msg)467 public void handleMessage(Message msg) { 468 switch (msg.what) { 469 case EVENT_RESTORE_DEFAULTAPN_START: 470 ContentResolver resolver = getContentResolver(); 471 resolver.delete(DEFAULTAPN_URI, null, null); 472 mRestoreApnUiHandler 473 .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE); 474 break; 475 } 476 } 477 } 478 479 @Override onCreateDialog(int id)480 public Dialog onCreateDialog(int id) { 481 if (id == DIALOG_RESTORE_DEFAULTAPN) { 482 ProgressDialog dialog = new ProgressDialog(getActivity()); 483 dialog.setMessage(getResources().getString(R.string.restore_default_apn)); 484 dialog.setCancelable(false); 485 return dialog; 486 } 487 return null; 488 } 489 } 490