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