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 onException(mButtonChangePW, (CommandException) ar.exception); 240 mButtonChangePW.setEnabled(true); 241 } else if (ar.userObj instanceof Throwable) { 242 onError(mButtonChangePW, RESPONSE_ERROR); 243 } else { 244 // Successful change. 245 displayMessage(R.string.call_barring_change_pwd_success); 246 } 247 resetPwChangeState(); 248 break; 249 } 250 // When disabling all call barring, either fail and display a toast, 251 // or just update the UI. 252 case EVENT_DISABLE_ALL_COMPLETE: { 253 onFinished(mButtonDisableAll, false); 254 if (ar.exception != null) { 255 if (DBG) { 256 Log.d(LOG_TAG, "can not disable all call barring with exception: " 257 + ar.exception); 258 } 259 onException(mButtonDisableAll, (CommandException) ar.exception); 260 mButtonDisableAll.setEnabled(true); 261 } else if (ar.userObj instanceof Throwable) { 262 onError(mButtonDisableAll, RESPONSE_ERROR); 263 } else { 264 // Reset to normal behaviour on successful change. 265 displayMessage(R.string.call_barring_deactivate_success); 266 resetCallBarringPrefState(false); 267 } 268 break; 269 } 270 default: { 271 if (DBG) { 272 Log.d(LOG_TAG, "Unknown message id: " + msg.what); 273 } 274 break; 275 } 276 } 277 } 278 }; 279 280 /** 281 * The next two functions are for updating the message field on the dialog. 282 */ displayPwChangeDialog()283 private void displayPwChangeDialog() { 284 displayPwChangeDialog(0, true); 285 } 286 displayPwChangeDialog(int strId, boolean shouldDisplay)287 private void displayPwChangeDialog(int strId, boolean shouldDisplay) { 288 int msgId = 0; 289 switch (mPwChangeState) { 290 case PW_CHANGE_OLD: 291 msgId = R.string.call_barring_old_pwd; 292 break; 293 case PW_CHANGE_NEW: 294 msgId = R.string.call_barring_new_pwd; 295 break; 296 case PW_CHANGE_REENTER: 297 msgId = R.string.call_barring_confirm_pwd; 298 break; 299 default: 300 break; 301 } 302 303 // Append the note/additional message, if needed. 304 if (strId != 0) { 305 mButtonChangePW.setDialogMessage(getText(msgId) + "\n" + getText(strId)); 306 } else { 307 mButtonChangePW.setDialogMessage(msgId); 308 } 309 310 // Only display if requested. 311 if (shouldDisplay) { 312 mButtonChangePW.showPinDialog(); 313 } 314 mPwChangeDialogStrId = strId; 315 } 316 317 /** 318 * Reset the state of the password change dialog. 319 */ resetPwChangeState()320 private void resetPwChangeState() { 321 mPwChangeState = PW_CHANGE_OLD; 322 displayPwChangeDialog(0, false); 323 mOldPassword = ""; 324 mNewPassword = ""; 325 } 326 327 /** 328 * Reset the state of the all call barring setting to disable. 329 */ resetCallBarringPrefState(boolean enable)330 private void resetCallBarringPrefState(boolean enable) { 331 for (CallBarringEditPreference pref : mPreferences) { 332 pref.mIsActivated = enable; 333 pref.updateSummaryText(); 334 } 335 } 336 337 /** 338 * Validate the password entry. 339 * 340 * @param password This is the password to validate 341 */ validatePassword(String password)342 private boolean validatePassword(String password) { 343 return password != null && password.length() == PW_LENGTH; 344 } 345 346 @Override onCreate(Bundle icicle)347 protected void onCreate(Bundle icicle) { 348 super.onCreate(icicle); 349 if (DBG) { 350 Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file"); 351 } 352 addPreferencesFromResource(R.xml.callbarring_options); 353 354 mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent()); 355 mPhone = mSubscriptionInfoHelper.getPhone(); 356 if (DBG) { 357 Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file finished!"); 358 } 359 360 CarrierConfigManager configManager = (CarrierConfigManager) 361 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 362 PersistableBundle carrierConfig; 363 if (mSubscriptionInfoHelper.hasSubId()) { 364 carrierConfig = configManager.getConfigForSubId(mSubscriptionInfoHelper.getSubId()); 365 } else { 366 carrierConfig = configManager.getConfig(); 367 } 368 boolean isPwChangeButtonVisible = true; 369 boolean isDisableAllButtonVisible = true; 370 if (carrierConfig != null) { 371 isPwChangeButtonVisible = carrierConfig.getBoolean( 372 CarrierConfigManager.KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); 373 isDisableAllButtonVisible = carrierConfig.getBoolean( 374 CarrierConfigManager.KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); 375 } else { 376 Log.w(LOG_TAG, "Couldn't access CarrierConfig bundle"); 377 } 378 379 // Get UI object references 380 PreferenceScreen prefSet = getPreferenceScreen(); 381 mButtonBAOC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOC_KEY); 382 mButtonBAOIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOIC_KEY); 383 mButtonBAOICxH = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOICxH_KEY); 384 mButtonBAIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAIC_KEY); 385 mButtonBAICr = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAICr_KEY); 386 mButtonDisableAll = (CallBarringDeselectAllPreference) 387 prefSet.findPreference(BUTTON_BA_ALL_KEY); 388 mButtonChangePW = (EditPinPreference) prefSet.findPreference(BUTTON_BA_CHANGE_PW_KEY); 389 390 // Some carriers do not use PW change and disable all buttons. Hide them if this is the 391 // case. 392 if (!isDisableAllButtonVisible) { 393 prefSet.removePreference(mButtonDisableAll); 394 } 395 if (!isPwChangeButtonVisible) { 396 prefSet.removePreference(mButtonChangePW); 397 } 398 399 // Assign click listener and update state 400 mButtonBAOC.setOnPinEnteredListener(this); 401 mButtonBAOIC.setOnPinEnteredListener(this); 402 mButtonBAOICxH.setOnPinEnteredListener(this); 403 mButtonBAIC.setOnPinEnteredListener(this); 404 mButtonBAICr.setOnPinEnteredListener(this); 405 mButtonDisableAll.setOnPinEnteredListener(this); 406 mButtonChangePW.setOnPinEnteredListener(this); 407 408 // Store CallBarringEditPreferencence objects in array list. 409 mPreferences.add(mButtonBAOC); 410 mPreferences.add(mButtonBAOIC); 411 mPreferences.add(mButtonBAOICxH); 412 mPreferences.add(mButtonBAIC); 413 mPreferences.add(mButtonBAICr); 414 415 // Find out if the sim card is ready. 416 boolean isSimReady = TelephonyManager.from(this).getSimState( 417 SubscriptionManager.getSlotIndex(mPhone.getSubId())) 418 == TelephonyManager.SIM_STATE_READY; 419 420 // Deactivate all option and Change password option are unavailable 421 // when sim card is not ready. 422 if (isSimReady) { 423 mButtonDisableAll.setEnabled(true); 424 mButtonChangePW.setEnabled(true); 425 } else { 426 mButtonDisableAll.setEnabled(false); 427 mButtonChangePW.setEnabled(false); 428 mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled); 429 } 430 431 // Wait to do the initialization until onResume so that the TimeConsumingPreferenceActivity 432 // dialog can display as it relies on onResume / onPause to maintain its foreground state. 433 mFirstResume = true; 434 mIcicle = icicle; 435 436 ActionBar actionBar = getActionBar(); 437 if (actionBar != null) { 438 // android.R.id.home will be triggered in onOptionsItemSelected() 439 actionBar.setDisplayHomeAsUpEnabled(true); 440 } 441 442 if (mIcicle != null && !mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) { 443 if (DBG) { 444 Log.d(LOG_TAG, "restore stored states"); 445 } 446 mInitIndex = mPreferences.size(); 447 448 for (CallBarringEditPreference pref : mPreferences) { 449 Bundle bundle = mIcicle.getParcelable(pref.getKey()); 450 if (bundle != null) { 451 pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS)); 452 pref.init(this, true, mPhone); 453 pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled())); 454 } 455 } 456 mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY); 457 mOldPassword = mIcicle.getString(OLD_PW_KEY); 458 mNewPassword = mIcicle.getString(NEW_PW_KEY); 459 displayPwChangeDialog(mIcicle.getInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId), false); 460 mButtonChangePW.setText(mIcicle.getString(DIALOG_PW_ENTRY_KEY)); 461 } 462 } 463 464 @Override onResume()465 public void onResume() { 466 super.onResume(); 467 468 if (mFirstResume) { 469 if (mIcicle == null || mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) { 470 if (DBG) { 471 Log.d(LOG_TAG, "onResume: start to init "); 472 } 473 resetPwChangeState(); 474 mPreferences.get(mInitIndex).init(this, false, mPhone); 475 476 // Request removing BUSY_SAVING_DIALOG because reading is restarted. 477 // (If it doesn't exist, nothing happen.) 478 removeDialog(BUSY_SAVING_DIALOG); 479 } 480 mFirstResume = false; 481 mIcicle = null; 482 } 483 } 484 485 @Override onSaveInstanceState(Bundle outState)486 protected void onSaveInstanceState(Bundle outState) { 487 super.onSaveInstanceState(outState); 488 489 for (CallBarringEditPreference pref : mPreferences) { 490 Bundle bundle = new Bundle(); 491 bundle.putBoolean(KEY_STATUS, pref.mIsActivated); 492 bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()); 493 outState.putParcelable(pref.getKey(), bundle); 494 } 495 outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState); 496 outState.putString(OLD_PW_KEY, mOldPassword); 497 outState.putString(NEW_PW_KEY, mNewPassword); 498 outState.putInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId); 499 outState.putString(DIALOG_PW_ENTRY_KEY, mButtonChangePW.getText()); 500 501 outState.putBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY, 502 mProgressDialog != null && mProgressDialog.isShowing()); 503 } 504 505 /** 506 * Finish initialization of this preference and start next. 507 * 508 * @param preference The preference. 509 * @param reading If true to dismiss the busy reading dialog, 510 * false to dismiss the busy saving dialog. 511 */ onFinished(Preference preference, boolean reading)512 public void onFinished(Preference preference, boolean reading) { 513 if (mInitIndex < mPreferences.size() - 1 && !isFinishing()) { 514 mInitIndex++; 515 mPreferences.get(mInitIndex).init(this, false, mPhone); 516 } 517 super.onFinished(preference, reading); 518 } 519 520 @Override onOptionsItemSelected(MenuItem item)521 public boolean onOptionsItemSelected(MenuItem item) { 522 final int itemId = item.getItemId(); 523 if (itemId == android.R.id.home) { 524 CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper); 525 return true; 526 } 527 return super.onOptionsItemSelected(item); 528 } 529 530 @Override onPrepareDialog(int id, Dialog dialog, Bundle args)531 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { 532 super.onPrepareDialog(id, dialog, args); 533 if (id == BUSY_READING_DIALOG || id == BUSY_SAVING_DIALOG) { 534 // For onSaveInstanceState, treat the SAVING dialog as the same as the READING. As 535 // the result, if the activity is recreated while waiting for SAVING, it starts reading 536 // all the newest data. 537 mProgressDialog = dialog; 538 } 539 } 540 } 541