1 /* 2 * Copyright (C) 2009 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 20 import android.app.Activity; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.app.admin.DevicePolicyManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.content.pm.PackageManager.NameNotFoundException; 30 import android.content.res.Resources; 31 import android.graphics.drawable.Drawable; 32 import android.net.ConnectivityManager; 33 import android.net.NetworkInfo; 34 import android.nfc.NfcAdapter; 35 import android.os.Bundle; 36 import android.os.SystemProperties; 37 import android.os.UserHandle; 38 import android.preference.CheckBoxPreference; 39 import android.preference.Preference; 40 import android.preference.Preference.OnPreferenceChangeListener; 41 import android.preference.PreferenceScreen; 42 import android.provider.Settings; 43 import android.telephony.TelephonyManager; 44 import android.text.TextUtils; 45 import android.util.Log; 46 47 import com.android.internal.telephony.SmsApplication; 48 import com.android.internal.telephony.SmsApplication.SmsApplicationData; 49 import com.android.internal.telephony.TelephonyIntents; 50 import com.android.internal.telephony.TelephonyProperties; 51 import com.android.settings.nfc.NfcEnabler; 52 53 import java.util.Collection; 54 55 public class WirelessSettings extends RestrictedSettingsFragment 56 implements OnPreferenceChangeListener { 57 private static final String TAG = "WirelessSettings"; 58 59 private static final String KEY_TOGGLE_AIRPLANE = "toggle_airplane"; 60 private static final String KEY_TOGGLE_NFC = "toggle_nfc"; 61 private static final String KEY_WIMAX_SETTINGS = "wimax_settings"; 62 private static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings"; 63 private static final String KEY_VPN_SETTINGS = "vpn_settings"; 64 private static final String KEY_TETHER_SETTINGS = "tether_settings"; 65 private static final String KEY_PROXY_SETTINGS = "proxy_settings"; 66 private static final String KEY_MOBILE_NETWORK_SETTINGS = "mobile_network_settings"; 67 private static final String KEY_MANAGE_MOBILE_PLAN = "manage_mobile_plan"; 68 private static final String KEY_SMS_APPLICATION = "sms_application"; 69 private static final String KEY_TOGGLE_NSD = "toggle_nsd"; //network service discovery 70 private static final String KEY_CELL_BROADCAST_SETTINGS = "cell_broadcast_settings"; 71 72 public static final String EXIT_ECM_RESULT = "exit_ecm_result"; 73 public static final int REQUEST_CODE_EXIT_ECM = 1; 74 75 private AirplaneModeEnabler mAirplaneModeEnabler; 76 private CheckBoxPreference mAirplaneModePreference; 77 private NfcEnabler mNfcEnabler; 78 private NfcAdapter mNfcAdapter; 79 private NsdEnabler mNsdEnabler; 80 81 private ConnectivityManager mCm; 82 private TelephonyManager mTm; 83 84 private static final int MANAGE_MOBILE_PLAN_DIALOG_ID = 1; 85 private static final String SAVED_MANAGE_MOBILE_PLAN_MSG = "mManageMobilePlanMessage"; 86 87 private SmsListPreference mSmsApplicationPreference; 88 WirelessSettings()89 public WirelessSettings() { 90 super(null); 91 } 92 /** 93 * Invoked on each preference click in this hierarchy, overrides 94 * PreferenceActivity's implementation. Used to make sure we track the 95 * preference click events. 96 */ 97 @Override onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)98 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 99 if (ensurePinRestrictedPreference(preference)) { 100 return true; 101 } 102 log("onPreferenceTreeClick: preference=" + preference); 103 if (preference == mAirplaneModePreference && Boolean.parseBoolean( 104 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) { 105 // In ECM mode launch ECM app dialog 106 startActivityForResult( 107 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), 108 REQUEST_CODE_EXIT_ECM); 109 return true; 110 } else if (preference == findPreference(KEY_MANAGE_MOBILE_PLAN)) { 111 onManageMobilePlanClick(); 112 } 113 // Let the intents be launched by the Preference manager 114 return super.onPreferenceTreeClick(preferenceScreen, preference); 115 } 116 117 private String mManageMobilePlanMessage; 118 private static final String CONNECTED_TO_PROVISIONING_NETWORK_ACTION 119 = "com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION"; onManageMobilePlanClick()120 public void onManageMobilePlanClick() { 121 log("onManageMobilePlanClick:"); 122 mManageMobilePlanMessage = null; 123 Resources resources = getActivity().getResources(); 124 125 NetworkInfo ni = mCm.getProvisioningOrActiveNetworkInfo(); 126 if (mTm.hasIccCard() && (ni != null)) { 127 // Get provisioning URL 128 String url = mCm.getMobileProvisioningUrl(); 129 if (!TextUtils.isEmpty(url)) { 130 Intent intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION); 131 intent.putExtra("EXTRA_URL", url); 132 Context context = getActivity().getBaseContext(); 133 context.sendBroadcast(intent); 134 mManageMobilePlanMessage = null; 135 } else { 136 // No provisioning URL 137 String operatorName = mTm.getSimOperatorName(); 138 if (TextUtils.isEmpty(operatorName)) { 139 // Use NetworkOperatorName as second choice in case there is no 140 // SPN (Service Provider Name on the SIM). Such as with T-mobile. 141 operatorName = mTm.getNetworkOperatorName(); 142 if (TextUtils.isEmpty(operatorName)) { 143 mManageMobilePlanMessage = resources.getString( 144 R.string.mobile_unknown_sim_operator); 145 } else { 146 mManageMobilePlanMessage = resources.getString( 147 R.string.mobile_no_provisioning_url, operatorName); 148 } 149 } else { 150 mManageMobilePlanMessage = resources.getString( 151 R.string.mobile_no_provisioning_url, operatorName); 152 } 153 } 154 } else if (mTm.hasIccCard() == false) { 155 // No sim card 156 mManageMobilePlanMessage = resources.getString(R.string.mobile_insert_sim_card); 157 } else { 158 // NetworkInfo is null, there is no connection 159 mManageMobilePlanMessage = resources.getString(R.string.mobile_connect_to_internet); 160 } 161 if (!TextUtils.isEmpty(mManageMobilePlanMessage)) { 162 log("onManageMobilePlanClick: message=" + mManageMobilePlanMessage); 163 showDialog(MANAGE_MOBILE_PLAN_DIALOG_ID); 164 } 165 } 166 updateSmsApplicationSetting()167 private void updateSmsApplicationSetting() { 168 log("updateSmsApplicationSetting:"); 169 ComponentName appName = SmsApplication.getDefaultSmsApplication(getActivity(), true); 170 if (appName != null) { 171 String packageName = appName.getPackageName(); 172 173 CharSequence[] values = mSmsApplicationPreference.getEntryValues(); 174 for (int i = 0; i < values.length; i++) { 175 if (packageName.contentEquals(values[i])) { 176 mSmsApplicationPreference.setValueIndex(i); 177 mSmsApplicationPreference.setSummary(mSmsApplicationPreference.getEntries()[i]); 178 break; 179 } 180 } 181 } 182 } 183 initSmsApplicationSetting()184 private void initSmsApplicationSetting() { 185 log("initSmsApplicationSetting:"); 186 Collection<SmsApplicationData> smsApplications = 187 SmsApplication.getApplicationCollection(getActivity()); 188 189 // If the list is empty the dialog will be empty, but we will not crash. 190 int count = smsApplications.size(); 191 CharSequence[] entries = new CharSequence[count]; 192 CharSequence[] entryValues = new CharSequence[count]; 193 Drawable[] entryImages = new Drawable[count]; 194 195 PackageManager packageManager = getPackageManager(); 196 int i = 0; 197 for (SmsApplicationData smsApplicationData : smsApplications) { 198 entries[i] = smsApplicationData.mApplicationName; 199 entryValues[i] = smsApplicationData.mPackageName; 200 try { 201 entryImages[i] = packageManager.getApplicationIcon(smsApplicationData.mPackageName); 202 } catch (NameNotFoundException e) { 203 entryImages[i] = packageManager.getDefaultActivityIcon(); 204 } 205 i++; 206 } 207 mSmsApplicationPreference.setEntries(entries); 208 mSmsApplicationPreference.setEntryValues(entryValues); 209 mSmsApplicationPreference.setEntryDrawables(entryImages); 210 updateSmsApplicationSetting(); 211 } 212 213 @Override onCreateDialog(int dialogId)214 public Dialog onCreateDialog(int dialogId) { 215 log("onCreateDialog: dialogId=" + dialogId); 216 switch (dialogId) { 217 case MANAGE_MOBILE_PLAN_DIALOG_ID: 218 return new AlertDialog.Builder(getActivity()) 219 .setMessage(mManageMobilePlanMessage) 220 .setCancelable(false) 221 .setPositiveButton(com.android.internal.R.string.ok, 222 new DialogInterface.OnClickListener() { 223 @Override 224 public void onClick(DialogInterface dialog, int id) { 225 log("MANAGE_MOBILE_PLAN_DIALOG.onClickListener id=" + id); 226 mManageMobilePlanMessage = null; 227 } 228 }) 229 .create(); 230 } 231 return super.onCreateDialog(dialogId); 232 } 233 234 private void log(String s) { 235 Log.d(TAG, s); 236 } 237 238 public static boolean isRadioAllowed(Context context, String type) { 239 if (!AirplaneModeEnabler.isAirplaneModeOn(context)) { 240 return true; 241 } 242 // Here we use the same logic in onCreate(). 243 String toggleable = Settings.Global.getString(context.getContentResolver(), 244 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 245 return toggleable != null && toggleable.contains(type); 246 } 247 248 private boolean isSmsSupported() { 249 // Some tablet has sim card but could not do telephony operations. Skip those. 250 return (mTm.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE); 251 } 252 253 @Override 254 public void onCreate(Bundle savedInstanceState) { 255 super.onCreate(savedInstanceState); 256 if (savedInstanceState != null) { 257 mManageMobilePlanMessage = savedInstanceState.getString(SAVED_MANAGE_MOBILE_PLAN_MSG); 258 } 259 log("onCreate: mManageMobilePlanMessage=" + mManageMobilePlanMessage); 260 261 mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 262 mTm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 263 264 addPreferencesFromResource(R.xml.wireless_settings); 265 266 final boolean isSecondaryUser = UserHandle.myUserId() != UserHandle.USER_OWNER; 267 268 final Activity activity = getActivity(); 269 mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE); 270 CheckBoxPreference nfc = (CheckBoxPreference) findPreference(KEY_TOGGLE_NFC); 271 PreferenceScreen androidBeam = (PreferenceScreen) findPreference(KEY_ANDROID_BEAM_SETTINGS); 272 CheckBoxPreference nsd = (CheckBoxPreference) findPreference(KEY_TOGGLE_NSD); 273 274 mAirplaneModeEnabler = new AirplaneModeEnabler(activity, mAirplaneModePreference); 275 mNfcEnabler = new NfcEnabler(activity, nfc, androidBeam); 276 277 mSmsApplicationPreference = (SmsListPreference) findPreference(KEY_SMS_APPLICATION); 278 mSmsApplicationPreference.setOnPreferenceChangeListener(this); 279 initSmsApplicationSetting(); 280 281 // Remove NSD checkbox by default 282 getPreferenceScreen().removePreference(nsd); 283 //mNsdEnabler = new NsdEnabler(activity, nsd); 284 285 String toggleable = Settings.Global.getString(activity.getContentResolver(), 286 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 287 288 //enable/disable wimax depending on the value in config.xml 289 boolean isWimaxEnabled = !isSecondaryUser && this.getResources().getBoolean( 290 com.android.internal.R.bool.config_wimaxEnabled); 291 if (!isWimaxEnabled) { 292 PreferenceScreen root = getPreferenceScreen(); 293 Preference ps = (Preference) findPreference(KEY_WIMAX_SETTINGS); 294 if (ps != null) root.removePreference(ps); 295 } else { 296 if (toggleable == null || !toggleable.contains(Settings.Global.RADIO_WIMAX ) 297 && isWimaxEnabled) { 298 Preference ps = (Preference) findPreference(KEY_WIMAX_SETTINGS); 299 ps.setDependency(KEY_TOGGLE_AIRPLANE); 300 } 301 } 302 protectByRestrictions(KEY_WIMAX_SETTINGS); 303 304 // Manually set dependencies for Wifi when not toggleable. 305 if (toggleable == null || !toggleable.contains(Settings.Global.RADIO_WIFI)) { 306 findPreference(KEY_VPN_SETTINGS).setDependency(KEY_TOGGLE_AIRPLANE); 307 } 308 if (isSecondaryUser) { // Disable VPN 309 removePreference(KEY_VPN_SETTINGS); 310 } 311 protectByRestrictions(KEY_VPN_SETTINGS); 312 // Manually set dependencies for Bluetooth when not toggleable. 313 if (toggleable == null || !toggleable.contains(Settings.Global.RADIO_BLUETOOTH)) { 314 // No bluetooth-dependent items in the list. Code kept in case one is added later. 315 } 316 317 // Manually set dependencies for NFC when not toggleable. 318 if (toggleable == null || !toggleable.contains(Settings.Global.RADIO_NFC)) { 319 findPreference(KEY_TOGGLE_NFC).setDependency(KEY_TOGGLE_AIRPLANE); 320 findPreference(KEY_ANDROID_BEAM_SETTINGS).setDependency(KEY_TOGGLE_AIRPLANE); 321 } 322 323 // Remove NFC if its not available 324 mNfcAdapter = NfcAdapter.getDefaultAdapter(activity); 325 if (mNfcAdapter == null) { 326 getPreferenceScreen().removePreference(nfc); 327 getPreferenceScreen().removePreference(androidBeam); 328 mNfcEnabler = null; 329 } 330 331 // Remove Mobile Network Settings and Manage Mobile Plan if it's a wifi-only device. 332 if (isSecondaryUser || Utils.isWifiOnly(getActivity())) { 333 removePreference(KEY_MOBILE_NETWORK_SETTINGS); 334 removePreference(KEY_MANAGE_MOBILE_PLAN); 335 } 336 // Remove Mobile Network Settings and Manage Mobile Plan 337 // if config_show_mobile_plan sets false. 338 boolean isMobilePlanEnabled = this.getResources().getBoolean( 339 R.bool.config_show_mobile_plan); 340 if (!isMobilePlanEnabled) { 341 Preference pref = findPreference(KEY_MANAGE_MOBILE_PLAN); 342 if (pref != null) { 343 removePreference(KEY_MANAGE_MOBILE_PLAN); 344 } 345 } 346 protectByRestrictions(KEY_MOBILE_NETWORK_SETTINGS); 347 protectByRestrictions(KEY_MANAGE_MOBILE_PLAN); 348 349 // Remove SMS Application if the device does not support SMS 350 if (!isSmsSupported()) { 351 removePreference(KEY_SMS_APPLICATION); 352 } 353 354 // Remove Airplane Mode settings if it's a stationary device such as a TV. 355 if (getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) { 356 removePreference(KEY_TOGGLE_AIRPLANE); 357 } 358 359 // Enable Proxy selector settings if allowed. 360 Preference mGlobalProxy = findPreference(KEY_PROXY_SETTINGS); 361 DevicePolicyManager mDPM = (DevicePolicyManager) 362 activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 363 // proxy UI disabled until we have better app support 364 getPreferenceScreen().removePreference(mGlobalProxy); 365 mGlobalProxy.setEnabled(mDPM.getGlobalProxyAdmin() == null); 366 367 // Disable Tethering if it's not allowed or if it's a wifi-only device 368 ConnectivityManager cm = 369 (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE); 370 if (isSecondaryUser || !cm.isTetheringSupported()) { 371 getPreferenceScreen().removePreference(findPreference(KEY_TETHER_SETTINGS)); 372 } else { 373 Preference p = findPreference(KEY_TETHER_SETTINGS); 374 p.setTitle(Utils.getTetheringLabel(cm)); 375 } 376 protectByRestrictions(KEY_TETHER_SETTINGS); 377 378 // Enable link to CMAS app settings depending on the value in config.xml. 379 boolean isCellBroadcastAppLinkEnabled = this.getResources().getBoolean( 380 com.android.internal.R.bool.config_cellBroadcastAppLinks); 381 try { 382 if (isCellBroadcastAppLinkEnabled) { 383 PackageManager pm = getPackageManager(); 384 if (pm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver") 385 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { 386 isCellBroadcastAppLinkEnabled = false; // CMAS app disabled 387 } 388 } 389 } catch (IllegalArgumentException ignored) { 390 isCellBroadcastAppLinkEnabled = false; // CMAS app not installed 391 } 392 if (isSecondaryUser || !isCellBroadcastAppLinkEnabled) { 393 PreferenceScreen root = getPreferenceScreen(); 394 Preference ps = findPreference(KEY_CELL_BROADCAST_SETTINGS); 395 if (ps != null) root.removePreference(ps); 396 } 397 protectByRestrictions(KEY_CELL_BROADCAST_SETTINGS); 398 } 399 400 @Override 401 public void onStart() { 402 super.onStart(); 403 404 initSmsApplicationSetting(); 405 } 406 407 @Override 408 public void onResume() { 409 super.onResume(); 410 411 mAirplaneModeEnabler.resume(); 412 if (mNfcEnabler != null) { 413 mNfcEnabler.resume(); 414 } 415 if (mNsdEnabler != null) { 416 mNsdEnabler.resume(); 417 } 418 } 419 420 @Override 421 public void onSaveInstanceState(Bundle outState) { 422 super.onSaveInstanceState(outState); 423 424 if (!TextUtils.isEmpty(mManageMobilePlanMessage)) { 425 outState.putString(SAVED_MANAGE_MOBILE_PLAN_MSG, mManageMobilePlanMessage); 426 } 427 } 428 429 @Override 430 public void onPause() { 431 super.onPause(); 432 433 mAirplaneModeEnabler.pause(); 434 if (mNfcEnabler != null) { 435 mNfcEnabler.pause(); 436 } 437 if (mNsdEnabler != null) { 438 mNsdEnabler.pause(); 439 } 440 } 441 442 @Override 443 public void onActivityResult(int requestCode, int resultCode, Intent data) { 444 if (requestCode == REQUEST_CODE_EXIT_ECM) { 445 Boolean isChoiceYes = data.getBooleanExtra(EXIT_ECM_RESULT, false); 446 // Set Airplane mode based on the return value and checkbox state 447 mAirplaneModeEnabler.setAirplaneModeInECM(isChoiceYes, 448 mAirplaneModePreference.isChecked()); 449 } 450 super.onActivityResult(requestCode, resultCode, data); 451 } 452 453 @Override 454 protected int getHelpResource() { 455 return R.string.help_url_more_networks; 456 } 457 458 @Override 459 public boolean onPreferenceChange(Preference preference, Object newValue) { 460 if (preference == mSmsApplicationPreference && newValue != null) { 461 SmsApplication.setDefaultApplication(newValue.toString(), getActivity()); 462 updateSmsApplicationSetting(); 463 return true; 464 } 465 return false; 466 } 467 } 468