1 /* 2 * Copyright (C) 2018 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.phone; 18 19 import android.app.ActionBar; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.os.AsyncResult; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.os.PersistableBundle; 27 import android.preference.Preference; 28 import android.preference.PreferenceScreen; 29 import android.telephony.CarrierConfigManager; 30 import android.telephony.SubscriptionManager; 31 import android.telephony.TelephonyManager; 32 import android.util.Log; 33 import android.view.MenuItem; 34 import android.widget.Toast; 35 36 import com.android.internal.telephony.CommandException; 37 import com.android.internal.telephony.CommandsInterface; 38 import com.android.internal.telephony.GsmCdmaPhone; 39 import com.android.internal.telephony.Phone; 40 import com.android.phone.settings.fdn.EditPinPreference; 41 42 import java.util.ArrayList; 43 44 /** 45 * Implements the preference to enable/disable calling barring options and 46 * the dialogs to change the passward. 47 */ 48 public class GsmUmtsCallBarringOptions extends TimeConsumingPreferenceActivity 49 implements EditPinPreference.OnPinEnteredListener { 50 private static final String LOG_TAG = "GsmUmtsCallBarringOptions"; 51 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); 52 53 // String keys for preference lookup 54 // Preference is handled solely in xml. 55 // Block all outgoing calls 56 private static final String BUTTON_BAOC_KEY = "button_baoc_key"; 57 // Block all outgoing international calls 58 private static final String BUTTON_BAOIC_KEY = "button_baoic_key"; 59 // Block all outgoing international roaming calls 60 private static final String BUTTON_BAOICxH_KEY = "button_baoicxh_key"; 61 // Block all incoming calls 62 private static final String BUTTON_BAIC_KEY = "button_baic_key"; 63 // Block all incoming international roaming calls 64 private static final String BUTTON_BAICr_KEY = "button_baicr_key"; 65 // Disable all barring 66 private static final String BUTTON_BA_ALL_KEY = "button_ba_all_key"; 67 // Change passward 68 private static final String BUTTON_BA_CHANGE_PW_KEY = "button_change_pw_key"; 69 70 private static final String PW_CHANGE_STATE_KEY = "pin_change_state_key"; 71 private static final String OLD_PW_KEY = "old_pw_key"; 72 private static final String NEW_PW_KEY = "new_pw_key"; 73 private static final String DIALOG_MESSAGE_KEY = "dialog_message_key"; 74 private static final String DIALOG_PW_ENTRY_KEY = "dialog_pw_enter_key"; 75 private static final String KEY_STATUS = "toggle"; 76 private static final String PREFERENCE_ENABLED_KEY = "PREFERENCE_ENABLED"; 77 private static final String SAVED_BEFORE_LOAD_COMPLETED_KEY = "PROGRESS_SHOWING"; 78 79 private CallBarringEditPreference mButtonBAOC; 80 private CallBarringEditPreference mButtonBAOIC; 81 private CallBarringEditPreference mButtonBAOICxH; 82 private CallBarringEditPreference mButtonBAIC; 83 private CallBarringEditPreference mButtonBAICr; 84 private CallBarringDeselectAllPreference mButtonDisableAll; 85 private EditPinPreference mButtonChangePW; 86 87 // State variables 88 private int mPwChangeState; 89 private String mOldPassword; 90 private String mNewPassword; 91 private int mPwChangeDialogStrId; 92 93 private static final int PW_CHANGE_OLD = 0; 94 private static final int PW_CHANGE_NEW = 1; 95 private static final int PW_CHANGE_REENTER = 2; 96 97 private static final int BUSY_READING_DIALOG = 100; 98 private static final int BUSY_SAVING_DIALOG = 200; 99 100 // Password change complete event 101 private static final int EVENT_PW_CHANGE_COMPLETE = 100; 102 // Disable all complete event 103 private static final int EVENT_DISABLE_ALL_COMPLETE = 200; 104 105 private static final int PW_LENGTH = 4; 106 107 private Phone mPhone; 108 private ArrayList<CallBarringEditPreference> mPreferences = 109 new ArrayList<CallBarringEditPreference>(); 110 private int mInitIndex = 0; 111 private boolean mFirstResume; 112 private Bundle mIcicle; 113 114 private SubscriptionInfoHelper mSubscriptionInfoHelper; 115 private Dialog mProgressDialog; 116 117 @Override onPinEntered(EditPinPreference preference, boolean positiveResult)118 public void onPinEntered(EditPinPreference preference, boolean positiveResult) { 119 if (preference == mButtonChangePW) { 120 updatePWChangeState(positiveResult); 121 } else if (preference == mButtonDisableAll) { 122 disableAllBarring(positiveResult); 123 } 124 } 125 126 /** 127 * Display a toast for message. 128 */ displayMessage(int strId)129 private void displayMessage(int strId) { 130 Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); 131 } 132 133 /** 134 * Attempt to disable all for call barring settings. 135 */ disableAllBarring(boolean positiveResult)136 private void disableAllBarring(boolean positiveResult) { 137 if (!positiveResult) { 138 // Return on cancel 139 return; 140 } 141 142 String password = mButtonDisableAll.getText(); 143 // Validate the length of password first, before submitting it to the 144 // RIL for CB disable. 145 if (!validatePassword(password)) { 146 mButtonDisableAll.setText(""); 147 displayMessage(R.string.call_barring_right_pwd_number); 148 return; 149 } 150 151 // Submit the disable all request 152 mButtonDisableAll.setText(""); 153 Message onComplete = mHandler.obtainMessage(EVENT_DISABLE_ALL_COMPLETE); 154 mPhone.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, false, password, onComplete, 0); 155 this.onStarted(mButtonDisableAll, false); 156 } 157 158 /** 159 * Attempt to change the password for call barring settings. 160 */ updatePWChangeState(boolean positiveResult)161 private void updatePWChangeState(boolean positiveResult) { 162 if (!positiveResult) { 163 // Reset the state on cancel 164 resetPwChangeState(); 165 return; 166 } 167 168 // Progress through the dialog states, generally in this order: 169 // 1. Enter old password 170 // 2. Enter new password 171 // 3. Re-Enter new password 172 // In general, if any invalid entries are made, the dialog re- 173 // appears with text to indicate what the issue is. 174 switch (mPwChangeState) { 175 case PW_CHANGE_OLD: 176 mOldPassword = mButtonChangePW.getText(); 177 mButtonChangePW.setText(""); 178 if (validatePassword(mOldPassword)) { 179 mPwChangeState = PW_CHANGE_NEW; 180 displayPwChangeDialog(); 181 } else { 182 displayPwChangeDialog(R.string.call_barring_right_pwd_number, true); 183 } 184 break; 185 case PW_CHANGE_NEW: 186 mNewPassword = mButtonChangePW.getText(); 187 mButtonChangePW.setText(""); 188 if (validatePassword(mNewPassword)) { 189 mPwChangeState = PW_CHANGE_REENTER; 190 displayPwChangeDialog(); 191 } else { 192 displayPwChangeDialog(R.string.call_barring_right_pwd_number, true); 193 } 194 break; 195 case PW_CHANGE_REENTER: 196 // If the re-entered password is not valid, display a message 197 // and reset the state. 198 if (!mNewPassword.equals(mButtonChangePW.getText())) { 199 mPwChangeState = PW_CHANGE_NEW; 200 mButtonChangePW.setText(""); 201 displayPwChangeDialog(R.string.call_barring_pwd_not_match, true); 202 } else { 203 // If the password is valid, then submit the change password request 204 mButtonChangePW.setText(""); 205 Message onComplete = mHandler.obtainMessage(EVENT_PW_CHANGE_COMPLETE); 206 ((GsmCdmaPhone) mPhone).changeCallBarringPassword( 207 CommandsInterface.CB_FACILITY_BA_ALL, 208 mOldPassword, mNewPassword, onComplete); 209 this.onStarted(mButtonChangePW, false); 210 } 211 break; 212 default: 213 if (DBG) { 214 Log.d(LOG_TAG, "updatePWChangeState: Unknown password change state: " 215 + mPwChangeState); 216 } 217 break; 218 } 219 } 220 221 /** 222 * Handler for asynchronous replies from the framework layer. 223 */ 224 private Handler mHandler = new Handler() { 225 @Override 226 public void handleMessage(Message msg) { 227 AsyncResult ar = (AsyncResult) msg.obj; 228 switch (msg.what) { 229 // Handle the response message for password change from the framework layer. 230 case EVENT_PW_CHANGE_COMPLETE: { 231 onFinished(mButtonChangePW, false); 232 // Unsuccessful change, display a toast to user with failure reason. 233 if (ar.exception != null) { 234 if (DBG) { 235 Log.d(LOG_TAG, 236 "change password for call barring failed with exception: " 237 + ar.exception); 238 } 239 CommandException commandException = (CommandException) ar.exception; 240 onException(mButtonChangePW, commandException); 241 if (commandException.getCommandError() 242 != CommandException.Error.FDN_CHECK_FAILURE) { 243 // Not a FDN_CHECK_FAILURE, enable mButtonChangePW 244 mButtonChangePW.setEnabled(true); 245 } 246 } else if (ar.userObj instanceof Throwable) { 247 onError(mButtonChangePW, RESPONSE_ERROR); 248 } else { 249 // Successful change. 250 displayMessage(R.string.call_barring_change_pwd_success); 251 } 252 resetPwChangeState(); 253 break; 254 } 255 // When disabling all call barring, either fail and display a toast, 256 // or just update the UI. 257 case EVENT_DISABLE_ALL_COMPLETE: { 258 onFinished(mButtonDisableAll, false); 259 if (ar.exception != null) { 260 if (DBG) { 261 Log.d(LOG_TAG, "can not disable all call barring with exception: " 262 + ar.exception); 263 } 264 onException(mButtonDisableAll, (CommandException) ar.exception); 265 mButtonDisableAll.setEnabled(true); 266 } else if (ar.userObj instanceof Throwable) { 267 onError(mButtonDisableAll, RESPONSE_ERROR); 268 } else { 269 // Reset to normal behaviour on successful change. 270 displayMessage(R.string.call_barring_deactivate_success); 271 resetCallBarringPrefState(false); 272 } 273 break; 274 } 275 default: { 276 if (DBG) { 277 Log.d(LOG_TAG, "Unknown message id: " + msg.what); 278 } 279 break; 280 } 281 } 282 } 283 }; 284 285 /** 286 * The next two functions are for updating the message field on the dialog. 287 */ displayPwChangeDialog()288 private void displayPwChangeDialog() { 289 displayPwChangeDialog(0, true); 290 } 291 displayPwChangeDialog(int strId, boolean shouldDisplay)292 private void displayPwChangeDialog(int strId, boolean shouldDisplay) { 293 int msgId = 0; 294 switch (mPwChangeState) { 295 case PW_CHANGE_OLD: 296 msgId = R.string.call_barring_old_pwd; 297 break; 298 case PW_CHANGE_NEW: 299 msgId = R.string.call_barring_new_pwd; 300 break; 301 case PW_CHANGE_REENTER: 302 msgId = R.string.call_barring_confirm_pwd; 303 break; 304 default: 305 break; 306 } 307 308 // Append the note/additional message, if needed. 309 if (strId != 0) { 310 mButtonChangePW.setDialogMessage(getText(msgId) + "\n" + getText(strId)); 311 } else { 312 mButtonChangePW.setDialogMessage(msgId); 313 } 314 315 // Only display if requested. 316 if (shouldDisplay) { 317 mButtonChangePW.showPinDialog(); 318 } 319 mPwChangeDialogStrId = strId; 320 } 321 322 /** 323 * Reset the state of the password change dialog. 324 */ resetPwChangeState()325 private void resetPwChangeState() { 326 mPwChangeState = PW_CHANGE_OLD; 327 displayPwChangeDialog(0, false); 328 mOldPassword = ""; 329 mNewPassword = ""; 330 } 331 332 /** 333 * Reset the state of the all call barring setting to disable. 334 */ resetCallBarringPrefState(boolean enable)335 private void resetCallBarringPrefState(boolean enable) { 336 for (CallBarringEditPreference pref : mPreferences) { 337 pref.mIsActivated = enable; 338 pref.updateSummaryText(); 339 } 340 } 341 342 /** 343 * Validate the password entry. 344 * 345 * @param password This is the password to validate 346 */ validatePassword(String password)347 private boolean validatePassword(String password) { 348 return password != null && password.length() == PW_LENGTH; 349 } 350 351 @Override onCreate(Bundle icicle)352 protected void onCreate(Bundle icicle) { 353 super.onCreate(icicle); 354 if (DBG) { 355 Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file"); 356 } 357 addPreferencesFromResource(R.xml.callbarring_options); 358 359 mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent()); 360 mPhone = mSubscriptionInfoHelper.getPhone(); 361 if (DBG) { 362 Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file finished!"); 363 } 364 365 CarrierConfigManager configManager = (CarrierConfigManager) 366 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 367 PersistableBundle carrierConfig; 368 if (mSubscriptionInfoHelper.hasSubId()) { 369 carrierConfig = configManager.getConfigForSubId(mSubscriptionInfoHelper.getSubId()); 370 } else { 371 carrierConfig = configManager.getConfig(); 372 } 373 boolean isPwChangeButtonVisible = true; 374 boolean isDisableAllButtonVisible = true; 375 if (carrierConfig != null) { 376 isPwChangeButtonVisible = carrierConfig.getBoolean( 377 CarrierConfigManager.KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); 378 isDisableAllButtonVisible = carrierConfig.getBoolean( 379 CarrierConfigManager.KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); 380 } else { 381 Log.w(LOG_TAG, "Couldn't access CarrierConfig bundle"); 382 } 383 384 // Get UI object references 385 PreferenceScreen prefSet = getPreferenceScreen(); 386 mButtonBAOC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOC_KEY); 387 mButtonBAOIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOIC_KEY); 388 mButtonBAOICxH = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOICxH_KEY); 389 mButtonBAIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAIC_KEY); 390 mButtonBAICr = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAICr_KEY); 391 mButtonDisableAll = (CallBarringDeselectAllPreference) 392 prefSet.findPreference(BUTTON_BA_ALL_KEY); 393 mButtonChangePW = (EditPinPreference) prefSet.findPreference(BUTTON_BA_CHANGE_PW_KEY); 394 395 // Some carriers do not use PW change and disable all buttons. Hide them if this is the 396 // case. 397 if (!isDisableAllButtonVisible) { 398 prefSet.removePreference(mButtonDisableAll); 399 } 400 if (!isPwChangeButtonVisible) { 401 prefSet.removePreference(mButtonChangePW); 402 } 403 404 // Assign click listener and update state 405 mButtonBAOC.setOnPinEnteredListener(this); 406 mButtonBAOIC.setOnPinEnteredListener(this); 407 mButtonBAOICxH.setOnPinEnteredListener(this); 408 mButtonBAIC.setOnPinEnteredListener(this); 409 mButtonBAICr.setOnPinEnteredListener(this); 410 mButtonDisableAll.setOnPinEnteredListener(this); 411 mButtonChangePW.setOnPinEnteredListener(this); 412 413 // Store CallBarringEditPreferencence objects in array list. 414 mPreferences.add(mButtonBAOC); 415 mPreferences.add(mButtonBAOIC); 416 mPreferences.add(mButtonBAOICxH); 417 mPreferences.add(mButtonBAIC); 418 mPreferences.add(mButtonBAICr); 419 420 // Find out if the sim card is ready. 421 boolean isSimReady = TelephonyManager.from(this).getSimState( 422 SubscriptionManager.getSlotIndex(mPhone.getSubId())) 423 == TelephonyManager.SIM_STATE_READY; 424 425 // Deactivate all option and Change password option are unavailable 426 // when sim card is not ready. 427 if (isSimReady) { 428 mButtonDisableAll.setEnabled(true); 429 mButtonChangePW.setEnabled(true); 430 } else { 431 mButtonDisableAll.setEnabled(false); 432 mButtonChangePW.setEnabled(false); 433 mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled); 434 } 435 436 // Wait to do the initialization until onResume so that the TimeConsumingPreferenceActivity 437 // dialog can display as it relies on onResume / onPause to maintain its foreground state. 438 mFirstResume = true; 439 mIcicle = icicle; 440 441 ActionBar actionBar = getActionBar(); 442 if (actionBar != null) { 443 // android.R.id.home will be triggered in onOptionsItemSelected() 444 actionBar.setDisplayHomeAsUpEnabled(true); 445 } 446 447 if (mIcicle != null && !mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) { 448 if (DBG) { 449 Log.d(LOG_TAG, "restore stored states"); 450 } 451 mInitIndex = mPreferences.size(); 452 453 for (CallBarringEditPreference pref : mPreferences) { 454 Bundle bundle = mIcicle.getParcelable(pref.getKey()); 455 if (bundle != null) { 456 pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS)); 457 pref.init(this, true, mPhone); 458 pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled())); 459 } 460 } 461 mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY); 462 mOldPassword = mIcicle.getString(OLD_PW_KEY); 463 mNewPassword = mIcicle.getString(NEW_PW_KEY); 464 displayPwChangeDialog(mIcicle.getInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId), false); 465 mButtonChangePW.setText(mIcicle.getString(DIALOG_PW_ENTRY_KEY)); 466 } 467 } 468 469 @Override onResume()470 public void onResume() { 471 super.onResume(); 472 473 if (mFirstResume) { 474 if (mIcicle == null || mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) { 475 if (DBG) { 476 Log.d(LOG_TAG, "onResume: start to init "); 477 } 478 resetPwChangeState(); 479 mPreferences.get(mInitIndex).init(this, false, mPhone); 480 481 // Request removing BUSY_SAVING_DIALOG because reading is restarted. 482 // (If it doesn't exist, nothing happen.) 483 removeDialog(BUSY_SAVING_DIALOG); 484 } 485 mFirstResume = false; 486 mIcicle = null; 487 } 488 } 489 490 @Override onSaveInstanceState(Bundle outState)491 protected void onSaveInstanceState(Bundle outState) { 492 super.onSaveInstanceState(outState); 493 494 for (CallBarringEditPreference pref : mPreferences) { 495 Bundle bundle = new Bundle(); 496 bundle.putBoolean(KEY_STATUS, pref.mIsActivated); 497 bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()); 498 outState.putParcelable(pref.getKey(), bundle); 499 } 500 outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState); 501 outState.putString(OLD_PW_KEY, mOldPassword); 502 outState.putString(NEW_PW_KEY, mNewPassword); 503 outState.putInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId); 504 outState.putString(DIALOG_PW_ENTRY_KEY, mButtonChangePW.getText()); 505 506 outState.putBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY, 507 mProgressDialog != null && mProgressDialog.isShowing()); 508 } 509 510 /** 511 * Finish initialization of this preference and start next. 512 * 513 * @param preference The preference. 514 * @param reading If true to dismiss the busy reading dialog, 515 * false to dismiss the busy saving dialog. 516 */ onFinished(Preference preference, boolean reading)517 public void onFinished(Preference preference, boolean reading) { 518 if (mInitIndex < mPreferences.size() - 1 && !isFinishing()) { 519 mInitIndex++; 520 mPreferences.get(mInitIndex).init(this, false, mPhone); 521 } 522 super.onFinished(preference, reading); 523 } 524 525 @Override onOptionsItemSelected(MenuItem item)526 public boolean onOptionsItemSelected(MenuItem item) { 527 final int itemId = item.getItemId(); 528 if (itemId == android.R.id.home) { 529 CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper); 530 return true; 531 } 532 return super.onOptionsItemSelected(item); 533 } 534 535 @Override onPrepareDialog(int id, Dialog dialog, Bundle args)536 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { 537 super.onPrepareDialog(id, dialog, args); 538 if (id == BUSY_READING_DIALOG || id == BUSY_SAVING_DIALOG) { 539 // For onSaveInstanceState, treat the SAVING dialog as the same as the READING. As 540 // the result, if the activity is recreated while waiting for SAVING, it starts reading 541 // all the newest data. 542 mProgressDialog = dialog; 543 } 544 } 545 } 546