1 package com.android.phone; 2 3 import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR; 4 import static com.android.phone.TimeConsumingPreferenceActivity.FDN_CHECK_FAILURE; 5 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR; 6 7 import android.content.Context; 8 import android.os.Handler; 9 import android.os.Message; 10 import android.os.PersistableBundle; 11 import android.preference.SwitchPreference; 12 import android.telephony.CarrierConfigManager; 13 import android.telephony.TelephonyManager; 14 import android.util.AttributeSet; 15 import android.util.Log; 16 17 import com.android.internal.telephony.Phone; 18 19 import java.util.concurrent.Executors; 20 import java.util.concurrent.ScheduledExecutorService; 21 import java.util.concurrent.TimeUnit; 22 import java.util.function.Consumer; 23 24 public class CallWaitingSwitchPreference extends SwitchPreference { 25 private static final String LOG_TAG = "CallWaitingSwitchPreference"; 26 private static final int DELAY_MILLIS_FOR_USSD = 1000; 27 private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); 28 29 private final MyHandler mHandler = new MyHandler(); 30 private Phone mPhone; 31 private TimeConsumingPreferenceListener mTcpListener; 32 private ScheduledExecutorService mExecutor; 33 private TelephonyManager mTelephonyManager; 34 private boolean mIsDuringUpdateProcess = false; 35 private int mUpdateStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR; 36 private int mQueryStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR; 37 private boolean mUssdMode = false; 38 private boolean mCwEnabled = false; 39 private boolean mCwClicked = false; 40 CallWaitingSwitchPreference(Context context, AttributeSet attrs, int defStyle)41 public CallWaitingSwitchPreference(Context context, AttributeSet attrs, int defStyle) { 42 super(context, attrs, defStyle); 43 } 44 CallWaitingSwitchPreference(Context context, AttributeSet attrs)45 public CallWaitingSwitchPreference(Context context, AttributeSet attrs) { 46 this(context, attrs, com.android.internal.R.attr.switchPreferenceStyle); 47 } 48 CallWaitingSwitchPreference(Context context)49 public CallWaitingSwitchPreference(Context context) { 50 this(context, null); 51 } 52 init( TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone)53 /* package */ void init( 54 TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) { 55 mPhone = phone; 56 mTcpListener = listener; 57 mExecutor = Executors.newSingleThreadScheduledExecutor(); 58 mTelephonyManager = getContext().getSystemService( 59 TelephonyManager.class).createForSubscriptionId(phone.getSubId()); 60 CarrierConfigManager configManager = getContext().getSystemService( 61 CarrierConfigManager.class); 62 PersistableBundle bundle = configManager.getConfigForSubId(phone.getSubId()); 63 mUssdMode = (bundle != null) ? bundle.getBoolean( 64 CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false) : false; 65 mCwEnabled = false; 66 67 if (!skipReading) { 68 Log.d(LOG_TAG, "init getCallWaitingStatus"); 69 mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack); 70 if (mTcpListener != null) { 71 mTcpListener.onStarted(this, true); 72 } 73 } 74 } 75 queryStatusCallBack(int result)76 private void queryStatusCallBack(int result) { 77 Log.d(LOG_TAG, "queryStatusCallBack: CW state " + result); 78 mQueryStatus = result; 79 mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MESSAGE_UPDATE_CALL_WAITING)); 80 } 81 updateStatusCallBack(int result)82 private void updateStatusCallBack(int result) { 83 Log.d(LOG_TAG, "updateStatusCallBack: CW state " + result + ", and re get"); 84 mUpdateStatus = result; 85 if (mUssdMode) { 86 Log.d(LOG_TAG, "updateStatusCallBack: USSD mode needs to wait 1s since Framework" 87 + " has the limitation"); 88 Consumer<Integer> resultListener = this::queryStatusCallBack; 89 try { 90 mExecutor.schedule(new Runnable() { 91 @Override 92 public void run() { 93 mTelephonyManager.getCallWaitingStatus(mExecutor, resultListener); 94 } 95 }, DELAY_MILLIS_FOR_USSD, TimeUnit.MILLISECONDS); 96 } catch (Exception e) { 97 Log.d(LOG_TAG, "Exception while waiting: " + e); 98 } 99 } else { 100 mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack); 101 } 102 } 103 104 @Override onClick()105 protected void onClick() { 106 super.onClick(); 107 mCwEnabled = isChecked(); 108 mCwClicked = true; 109 mTelephonyManager.setCallWaitingEnabled(mCwEnabled, mExecutor, this::updateStatusCallBack); 110 if (mTcpListener != null) { 111 mIsDuringUpdateProcess = true; 112 mTcpListener.onStarted(this, false); 113 } 114 } 115 116 private class MyHandler extends Handler { 117 static final int MESSAGE_UPDATE_CALL_WAITING = 0; 118 119 @Override handleMessage(Message msg)120 public void handleMessage(Message msg) { 121 switch (msg.what) { 122 case MESSAGE_UPDATE_CALL_WAITING: 123 updateUi(); 124 break; 125 } 126 } 127 updateUi()128 private void updateUi() { 129 if (mTcpListener != null) { 130 if (mIsDuringUpdateProcess) { 131 mTcpListener.onFinished(CallWaitingSwitchPreference.this, false); 132 } else { 133 mTcpListener.onFinished(CallWaitingSwitchPreference.this, true); 134 } 135 } 136 137 if (mQueryStatus != TelephonyManager.CALL_WAITING_STATUS_ENABLED 138 && mQueryStatus != TelephonyManager.CALL_WAITING_STATUS_DISABLED 139 && mQueryStatus != TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR) { 140 Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception:" + mQueryStatus); 141 int error = EXCEPTION_ERROR; 142 switch (mQueryStatus) { 143 case TelephonyManager.CALL_WAITING_STATUS_FDN_CHECK_FAILURE: 144 error = FDN_CHECK_FAILURE; 145 break; 146 default: 147 error = EXCEPTION_ERROR; 148 break; 149 } 150 if (mTcpListener != null) { 151 mTcpListener.onError(CallWaitingSwitchPreference.this, error); 152 } 153 handleCwFallbackOnError(); 154 setChecked(mCwEnabled); 155 } else if (mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR 156 || (mIsDuringUpdateProcess && ( 157 mUpdateStatus != TelephonyManager.CALL_WAITING_STATUS_ENABLED 158 && mUpdateStatus != TelephonyManager.CALL_WAITING_STATUS_DISABLED))) { 159 Log.d(LOG_TAG, "handleSetCallWaitingResponse: Exception"); 160 if (mTcpListener != null) { 161 mTcpListener.onError(CallWaitingSwitchPreference.this, RESPONSE_ERROR); 162 } 163 handleCwFallbackOnError(); 164 setChecked(mCwEnabled); 165 } else { 166 mCwEnabled = mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_ENABLED; 167 setChecked(mCwEnabled); 168 } 169 mIsDuringUpdateProcess = false; 170 mCwClicked = false; 171 } 172 } 173 handleCwFallbackOnError()174 private void handleCwFallbackOnError() { 175 // Recover initial state before onClick. 176 if (mCwClicked) { 177 mCwEnabled = !mCwEnabled; 178 } 179 } 180 } 181