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