/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.phone; import android.app.ActionBar; import android.app.Dialog; import android.content.Context; import android.os.AsyncResult; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.PersistableBundle; import android.preference.Preference; import android.preference.PreferenceScreen; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; import android.view.MenuItem; import android.widget.Toast; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.GsmCdmaPhone; import com.android.internal.telephony.Phone; import com.android.phone.settings.fdn.EditPinPreference; import java.util.ArrayList; /** * Implements the preference to enable/disable calling barring options and * the dialogs to change the passward. */ public class GsmUmtsCallBarringOptions extends TimeConsumingPreferenceActivity implements EditPinPreference.OnPinEnteredListener { private static final String LOG_TAG = "GsmUmtsCallBarringOptions"; private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); // String keys for preference lookup // Preference is handled solely in xml. // Block all outgoing calls private static final String BUTTON_BAOC_KEY = "button_baoc_key"; // Block all outgoing international calls private static final String BUTTON_BAOIC_KEY = "button_baoic_key"; // Block all outgoing international roaming calls private static final String BUTTON_BAOICxH_KEY = "button_baoicxh_key"; // Block all incoming calls private static final String BUTTON_BAIC_KEY = "button_baic_key"; // Block all incoming international roaming calls private static final String BUTTON_BAICr_KEY = "button_baicr_key"; // Disable all barring private static final String BUTTON_BA_ALL_KEY = "button_ba_all_key"; // Change passward private static final String BUTTON_BA_CHANGE_PW_KEY = "button_change_pw_key"; private static final String PW_CHANGE_STATE_KEY = "pin_change_state_key"; private static final String OLD_PW_KEY = "old_pw_key"; private static final String NEW_PW_KEY = "new_pw_key"; private static final String DIALOG_MESSAGE_KEY = "dialog_message_key"; private static final String DIALOG_PW_ENTRY_KEY = "dialog_pw_enter_key"; private static final String KEY_STATUS = "toggle"; private static final String PREFERENCE_ENABLED_KEY = "PREFERENCE_ENABLED"; private static final String SAVED_BEFORE_LOAD_COMPLETED_KEY = "PROGRESS_SHOWING"; private CallBarringEditPreference mButtonBAOC; private CallBarringEditPreference mButtonBAOIC; private CallBarringEditPreference mButtonBAOICxH; private CallBarringEditPreference mButtonBAIC; private CallBarringEditPreference mButtonBAICr; private CallBarringDeselectAllPreference mButtonDisableAll; private EditPinPreference mButtonChangePW; // State variables private int mPwChangeState; private String mOldPassword; private String mNewPassword; private int mPwChangeDialogStrId; private static final int PW_CHANGE_OLD = 0; private static final int PW_CHANGE_NEW = 1; private static final int PW_CHANGE_REENTER = 2; private static final int BUSY_READING_DIALOG = 100; private static final int BUSY_SAVING_DIALOG = 200; // Password change complete event private static final int EVENT_PW_CHANGE_COMPLETE = 100; // Disable all complete event private static final int EVENT_DISABLE_ALL_COMPLETE = 200; private static final int PW_LENGTH = 4; private Phone mPhone; private ArrayList mPreferences = new ArrayList(); private int mInitIndex = 0; private boolean mFirstResume; private Bundle mIcicle; private SubscriptionInfoHelper mSubscriptionInfoHelper; private Dialog mProgressDialog; @Override public void onPinEntered(EditPinPreference preference, boolean positiveResult) { if (preference == mButtonChangePW) { updatePWChangeState(positiveResult); } else if (preference == mButtonDisableAll) { disableAllBarring(positiveResult); } } /** * Display a toast for message. */ private void displayMessage(int strId) { Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); } /** * Attempt to disable all for call barring settings. */ private void disableAllBarring(boolean positiveResult) { if (!positiveResult) { // Return on cancel return; } String password = mButtonDisableAll.getText(); // Validate the length of password first, before submitting it to the // RIL for CB disable. if (!validatePassword(password)) { mButtonDisableAll.setText(""); displayMessage(R.string.call_barring_right_pwd_number); return; } // Submit the disable all request mButtonDisableAll.setText(""); Message onComplete = mHandler.obtainMessage(EVENT_DISABLE_ALL_COMPLETE); mPhone.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, false, password, onComplete, 0); this.onStarted(mButtonDisableAll, false); } /** * Attempt to change the password for call barring settings. */ private void updatePWChangeState(boolean positiveResult) { if (!positiveResult) { // Reset the state on cancel resetPwChangeState(); return; } // Progress through the dialog states, generally in this order: // 1. Enter old password // 2. Enter new password // 3. Re-Enter new password // In general, if any invalid entries are made, the dialog re- // appears with text to indicate what the issue is. switch (mPwChangeState) { case PW_CHANGE_OLD: mOldPassword = mButtonChangePW.getText(); mButtonChangePW.setText(""); if (validatePassword(mOldPassword)) { mPwChangeState = PW_CHANGE_NEW; displayPwChangeDialog(); } else { displayPwChangeDialog(R.string.call_barring_right_pwd_number, true); } break; case PW_CHANGE_NEW: mNewPassword = mButtonChangePW.getText(); mButtonChangePW.setText(""); if (validatePassword(mNewPassword)) { mPwChangeState = PW_CHANGE_REENTER; displayPwChangeDialog(); } else { displayPwChangeDialog(R.string.call_barring_right_pwd_number, true); } break; case PW_CHANGE_REENTER: // If the re-entered password is not valid, display a message // and reset the state. if (!mNewPassword.equals(mButtonChangePW.getText())) { mPwChangeState = PW_CHANGE_NEW; mButtonChangePW.setText(""); displayPwChangeDialog(R.string.call_barring_pwd_not_match, true); } else { // If the password is valid, then submit the change password request mButtonChangePW.setText(""); Message onComplete = mHandler.obtainMessage(EVENT_PW_CHANGE_COMPLETE); ((GsmCdmaPhone) mPhone).changeCallBarringPassword( CommandsInterface.CB_FACILITY_BA_ALL, mOldPassword, mNewPassword, onComplete); this.onStarted(mButtonChangePW, false); } break; default: if (DBG) { Log.d(LOG_TAG, "updatePWChangeState: Unknown password change state: " + mPwChangeState); } break; } } /** * Handler for asynchronous replies from the framework layer. */ private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { AsyncResult ar = (AsyncResult) msg.obj; switch (msg.what) { // Handle the response message for password change from the framework layer. case EVENT_PW_CHANGE_COMPLETE: { onFinished(mButtonChangePW, false); // Unsuccessful change, display a toast to user with failure reason. if (ar.exception != null) { if (DBG) { Log.d(LOG_TAG, "change password for call barring failed with exception: " + ar.exception); } onException(mButtonChangePW, (CommandException) ar.exception); mButtonChangePW.setEnabled(true); } else if (ar.userObj instanceof Throwable) { onError(mButtonChangePW, RESPONSE_ERROR); } else { // Successful change. displayMessage(R.string.call_barring_change_pwd_success); } resetPwChangeState(); break; } // When disabling all call barring, either fail and display a toast, // or just update the UI. case EVENT_DISABLE_ALL_COMPLETE: { onFinished(mButtonDisableAll, false); if (ar.exception != null) { if (DBG) { Log.d(LOG_TAG, "can not disable all call barring with exception: " + ar.exception); } onException(mButtonDisableAll, (CommandException) ar.exception); mButtonDisableAll.setEnabled(true); } else if (ar.userObj instanceof Throwable) { onError(mButtonDisableAll, RESPONSE_ERROR); } else { // Reset to normal behaviour on successful change. displayMessage(R.string.call_barring_deactivate_success); resetCallBarringPrefState(false); } break; } default: { if (DBG) { Log.d(LOG_TAG, "Unknown message id: " + msg.what); } break; } } } }; /** * The next two functions are for updating the message field on the dialog. */ private void displayPwChangeDialog() { displayPwChangeDialog(0, true); } private void displayPwChangeDialog(int strId, boolean shouldDisplay) { int msgId = 0; switch (mPwChangeState) { case PW_CHANGE_OLD: msgId = R.string.call_barring_old_pwd; break; case PW_CHANGE_NEW: msgId = R.string.call_barring_new_pwd; break; case PW_CHANGE_REENTER: msgId = R.string.call_barring_confirm_pwd; break; default: break; } // Append the note/additional message, if needed. if (strId != 0) { mButtonChangePW.setDialogMessage(getText(msgId) + "\n" + getText(strId)); } else { mButtonChangePW.setDialogMessage(msgId); } // Only display if requested. if (shouldDisplay) { mButtonChangePW.showPinDialog(); } mPwChangeDialogStrId = strId; } /** * Reset the state of the password change dialog. */ private void resetPwChangeState() { mPwChangeState = PW_CHANGE_OLD; displayPwChangeDialog(0, false); mOldPassword = ""; mNewPassword = ""; } /** * Reset the state of the all call barring setting to disable. */ private void resetCallBarringPrefState(boolean enable) { for (CallBarringEditPreference pref : mPreferences) { pref.mIsActivated = enable; pref.updateSummaryText(); } } /** * Validate the password entry. * * @param password This is the password to validate */ private boolean validatePassword(String password) { return password != null && password.length() == PW_LENGTH; } @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); if (DBG) { Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file"); } addPreferencesFromResource(R.xml.callbarring_options); mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent()); mPhone = mSubscriptionInfoHelper.getPhone(); if (DBG) { Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file finished!"); } CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); PersistableBundle carrierConfig; if (mSubscriptionInfoHelper.hasSubId()) { carrierConfig = configManager.getConfigForSubId(mSubscriptionInfoHelper.getSubId()); } else { carrierConfig = configManager.getConfig(); } boolean isPwChangeButtonVisible = true; boolean isDisableAllButtonVisible = true; if (carrierConfig != null) { isPwChangeButtonVisible = carrierConfig.getBoolean( CarrierConfigManager.KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); isDisableAllButtonVisible = carrierConfig.getBoolean( CarrierConfigManager.KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); } else { Log.w(LOG_TAG, "Couldn't access CarrierConfig bundle"); } // Get UI object references PreferenceScreen prefSet = getPreferenceScreen(); mButtonBAOC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOC_KEY); mButtonBAOIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOIC_KEY); mButtonBAOICxH = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOICxH_KEY); mButtonBAIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAIC_KEY); mButtonBAICr = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAICr_KEY); mButtonDisableAll = (CallBarringDeselectAllPreference) prefSet.findPreference(BUTTON_BA_ALL_KEY); mButtonChangePW = (EditPinPreference) prefSet.findPreference(BUTTON_BA_CHANGE_PW_KEY); // Some carriers do not use PW change and disable all buttons. Hide them if this is the // case. if (!isDisableAllButtonVisible) { prefSet.removePreference(mButtonDisableAll); } if (!isPwChangeButtonVisible) { prefSet.removePreference(mButtonChangePW); } // Assign click listener and update state mButtonBAOC.setOnPinEnteredListener(this); mButtonBAOIC.setOnPinEnteredListener(this); mButtonBAOICxH.setOnPinEnteredListener(this); mButtonBAIC.setOnPinEnteredListener(this); mButtonBAICr.setOnPinEnteredListener(this); mButtonDisableAll.setOnPinEnteredListener(this); mButtonChangePW.setOnPinEnteredListener(this); // Store CallBarringEditPreferencence objects in array list. mPreferences.add(mButtonBAOC); mPreferences.add(mButtonBAOIC); mPreferences.add(mButtonBAOICxH); mPreferences.add(mButtonBAIC); mPreferences.add(mButtonBAICr); // Find out if the sim card is ready. boolean isSimReady = TelephonyManager.from(this).getSimState( SubscriptionManager.getSlotIndex(mPhone.getSubId())) == TelephonyManager.SIM_STATE_READY; // Deactivate all option and Change password option are unavailable // when sim card is not ready. if (isSimReady) { mButtonDisableAll.setEnabled(true); mButtonChangePW.setEnabled(true); } else { mButtonDisableAll.setEnabled(false); mButtonChangePW.setEnabled(false); mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled); } // Wait to do the initialization until onResume so that the TimeConsumingPreferenceActivity // dialog can display as it relies on onResume / onPause to maintain its foreground state. mFirstResume = true; mIcicle = icicle; ActionBar actionBar = getActionBar(); if (actionBar != null) { // android.R.id.home will be triggered in onOptionsItemSelected() actionBar.setDisplayHomeAsUpEnabled(true); } if (mIcicle != null && !mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) { if (DBG) { Log.d(LOG_TAG, "restore stored states"); } mInitIndex = mPreferences.size(); for (CallBarringEditPreference pref : mPreferences) { Bundle bundle = mIcicle.getParcelable(pref.getKey()); if (bundle != null) { pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS)); pref.init(this, true, mPhone); pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled())); } } mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY); mOldPassword = mIcicle.getString(OLD_PW_KEY); mNewPassword = mIcicle.getString(NEW_PW_KEY); displayPwChangeDialog(mIcicle.getInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId), false); mButtonChangePW.setText(mIcicle.getString(DIALOG_PW_ENTRY_KEY)); } } @Override public void onResume() { super.onResume(); if (mFirstResume) { if (mIcicle == null || mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) { if (DBG) { Log.d(LOG_TAG, "onResume: start to init "); } resetPwChangeState(); mPreferences.get(mInitIndex).init(this, false, mPhone); // Request removing BUSY_SAVING_DIALOG because reading is restarted. // (If it doesn't exist, nothing happen.) removeDialog(BUSY_SAVING_DIALOG); } mFirstResume = false; mIcicle = null; } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); for (CallBarringEditPreference pref : mPreferences) { Bundle bundle = new Bundle(); bundle.putBoolean(KEY_STATUS, pref.mIsActivated); bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()); outState.putParcelable(pref.getKey(), bundle); } outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState); outState.putString(OLD_PW_KEY, mOldPassword); outState.putString(NEW_PW_KEY, mNewPassword); outState.putInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId); outState.putString(DIALOG_PW_ENTRY_KEY, mButtonChangePW.getText()); outState.putBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY, mProgressDialog != null && mProgressDialog.isShowing()); } /** * Finish initialization of this preference and start next. * * @param preference The preference. * @param reading If true to dismiss the busy reading dialog, * false to dismiss the busy saving dialog. */ public void onFinished(Preference preference, boolean reading) { if (mInitIndex < mPreferences.size() - 1 && !isFinishing()) { mInitIndex++; mPreferences.get(mInitIndex).init(this, false, mPhone); } super.onFinished(preference, reading); } @Override public boolean onOptionsItemSelected(MenuItem item) { final int itemId = item.getItemId(); if (itemId == android.R.id.home) { CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper); return true; } return super.onOptionsItemSelected(item); } @Override protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { super.onPrepareDialog(id, dialog, args); if (id == BUSY_READING_DIALOG || id == BUSY_SAVING_DIALOG) { // For onSaveInstanceState, treat the SAVING dialog as the same as the READING. As // the result, if the activity is recreated while waiting for SAVING, it starts reading // all the newest data. mProgressDialog = dialog; } } }