1 /* 2 * Copyright (C) 2006 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.internal.telephony; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.content.Context; 21 import android.os.AsyncResult; 22 import android.os.Handler; 23 import android.os.Message; 24 import android.os.PersistableBundle; 25 import android.telephony.CarrierConfigManager; 26 import android.text.TextUtils; 27 28 import java.io.FileDescriptor; 29 import java.io.PrintWriter; 30 import java.util.ArrayList; 31 32 33 /** 34 * {@hide} 35 */ 36 public abstract class CallTracker extends Handler { 37 38 private static final boolean DBG_POLL = false; 39 40 //***** Constants 41 42 static final int POLL_DELAY_MSEC = 250; 43 44 @UnsupportedAppUsage 45 protected int mPendingOperations; 46 @UnsupportedAppUsage 47 protected boolean mNeedsPoll; 48 protected Message mLastRelevantPoll; 49 protected ArrayList<Connection> mHandoverConnections = new ArrayList<Connection>(); 50 51 @UnsupportedAppUsage 52 public CommandsInterface mCi; 53 54 @UnsupportedAppUsage 55 protected boolean mNumberConverted = false; 56 private final int VALID_COMPARE_LENGTH = 3; 57 58 //***** Events 59 60 protected static final int EVENT_POLL_CALLS_RESULT = 1; 61 protected static final int EVENT_CALL_STATE_CHANGE = 2; 62 protected static final int EVENT_REPOLL_AFTER_DELAY = 3; 63 protected static final int EVENT_OPERATION_COMPLETE = 4; 64 protected static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5; 65 66 protected static final int EVENT_SWITCH_RESULT = 8; 67 protected static final int EVENT_RADIO_AVAILABLE = 9; 68 protected static final int EVENT_RADIO_NOT_AVAILABLE = 10; 69 protected static final int EVENT_CONFERENCE_RESULT = 11; 70 protected static final int EVENT_SEPARATE_RESULT = 12; 71 protected static final int EVENT_ECT_RESULT = 13; 72 protected static final int EVENT_EXIT_ECM_RESPONSE_CDMA = 14; 73 protected static final int EVENT_CALL_WAITING_INFO_CDMA = 15; 74 protected static final int EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA = 16; 75 protected static final int EVENT_THREE_WAY_DIAL_BLANK_FLASH = 20; 76 77 @UnsupportedAppUsage CallTracker()78 public CallTracker() { 79 } 80 pollCallsWhenSafe()81 protected void pollCallsWhenSafe() { 82 mNeedsPoll = true; 83 84 if (checkNoOperationsPending()) { 85 mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); 86 mCi.getCurrentCalls(mLastRelevantPoll); 87 } 88 } 89 90 protected void pollCallsAfterDelay()91 pollCallsAfterDelay() { 92 Message msg = obtainMessage(); 93 94 msg.what = EVENT_REPOLL_AFTER_DELAY; 95 sendMessageDelayed(msg, POLL_DELAY_MSEC); 96 } 97 98 protected boolean isCommandExceptionRadioNotAvailable(Throwable e)99 isCommandExceptionRadioNotAvailable(Throwable e) { 100 return e != null && e instanceof CommandException 101 && ((CommandException)e).getCommandError() 102 == CommandException.Error.RADIO_NOT_AVAILABLE; 103 } 104 handlePollCalls(AsyncResult ar)105 protected abstract void handlePollCalls(AsyncResult ar); 106 getPhone()107 protected abstract Phone getPhone(); 108 getHoConnection(DriverCall dc)109 protected Connection getHoConnection(DriverCall dc) { 110 for (Connection hoConn : mHandoverConnections) { 111 log("getHoConnection - compare number: hoConn= " + hoConn.toString()); 112 if (hoConn.getAddress() != null && hoConn.getAddress().contains(dc.number)) { 113 log("getHoConnection: Handover connection match found = " + hoConn.toString()); 114 return hoConn; 115 } 116 } 117 for (Connection hoConn : mHandoverConnections) { 118 log("getHoConnection: compare state hoConn= " + hoConn.toString()); 119 if (hoConn.getStateBeforeHandover() == Call.stateFromDCState(dc.state)) { 120 log("getHoConnection: Handover connection match found = " + hoConn.toString()); 121 return hoConn; 122 } 123 } 124 return null; 125 } 126 notifySrvccState(Call.SrvccState state, ArrayList<Connection> c)127 protected void notifySrvccState(Call.SrvccState state, ArrayList<Connection> c) { 128 if (state == Call.SrvccState.STARTED && c != null) { 129 // SRVCC started. Prepare handover connections list 130 mHandoverConnections.addAll(c); 131 } else if (state != Call.SrvccState.COMPLETED) { 132 // SRVCC FAILED/CANCELED. Clear the handover connections list 133 // Individual connections will be removed from the list in handlePollCalls() 134 mHandoverConnections.clear(); 135 } 136 log("notifySrvccState: state=" + state.name() + ", mHandoverConnections= " 137 + mHandoverConnections.toString()); 138 } 139 handleRadioAvailable()140 protected void handleRadioAvailable() { 141 pollCallsWhenSafe(); 142 } 143 144 /** 145 * Obtain a complete message that indicates that this operation 146 * does not require polling of getCurrentCalls(). However, if other 147 * operations that do need getCurrentCalls() are pending or are 148 * scheduled while this operation is pending, the invocation 149 * of getCurrentCalls() will be postponed until this 150 * operation is also complete. 151 */ 152 protected Message obtainNoPollCompleteMessage(int what)153 obtainNoPollCompleteMessage(int what) { 154 mPendingOperations++; 155 mLastRelevantPoll = null; 156 return obtainMessage(what); 157 } 158 159 /** 160 * @return true if we're idle or there's a call to getCurrentCalls() pending 161 * but nothing else 162 */ 163 private boolean checkNoOperationsPending()164 checkNoOperationsPending() { 165 if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" + 166 mPendingOperations); 167 return mPendingOperations == 0; 168 } 169 convertNumberIfNecessary(Phone phone, String dialNumber)170 protected String convertNumberIfNecessary(Phone phone, String dialNumber) { 171 if (dialNumber == null) { 172 return dialNumber; 173 } 174 String[] convertMaps = null; 175 CarrierConfigManager configManager = (CarrierConfigManager) 176 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 177 PersistableBundle bundle = configManager.getConfigForSubId(phone.getSubId()); 178 if (bundle != null) { 179 convertMaps = 180 bundle.getStringArray(CarrierConfigManager.KEY_DIAL_STRING_REPLACE_STRING_ARRAY); 181 } 182 if (convertMaps == null) { 183 // By default no replacement is necessary 184 log("convertNumberIfNecessary convertMaps is null"); 185 return dialNumber; 186 } 187 188 log("convertNumberIfNecessary Roaming" 189 + " convertMaps.length " + convertMaps.length 190 + " dialNumber.length() " + dialNumber.length()); 191 192 if (convertMaps.length < 1 || dialNumber.length() < VALID_COMPARE_LENGTH) { 193 return dialNumber; 194 } 195 196 String[] entry; 197 String outNumber = ""; 198 for(String convertMap : convertMaps) { 199 log("convertNumberIfNecessary: " + convertMap); 200 // entry format is "dialStringToReplace:dialStringReplacement" 201 entry = convertMap.split(":"); 202 if (entry != null && entry.length > 1) { 203 String dsToReplace = entry[0]; 204 String dsReplacement = entry[1]; 205 if (!TextUtils.isEmpty(dsToReplace) && dialNumber.equals(dsToReplace)) { 206 // Needs to be converted 207 if (!TextUtils.isEmpty(dsReplacement) && dsReplacement.endsWith("MDN")) { 208 String mdn = phone.getLine1Number(); 209 if (!TextUtils.isEmpty(mdn)) { 210 if (mdn.startsWith("+")) { 211 outNumber = mdn; 212 } else { 213 outNumber = dsReplacement.substring(0, dsReplacement.length() -3) 214 + mdn; 215 } 216 } 217 } else { 218 outNumber = dsReplacement; 219 } 220 break; 221 } 222 } 223 } 224 225 if (!TextUtils.isEmpty(outNumber)) { 226 log("convertNumberIfNecessary: convert service number"); 227 mNumberConverted = true; 228 return outNumber; 229 } 230 231 return dialNumber; 232 233 } 234 compareGid1(Phone phone, String serviceGid1)235 private boolean compareGid1(Phone phone, String serviceGid1) { 236 String gid1 = phone.getGroupIdLevel1(); 237 int gid_length = serviceGid1.length(); 238 boolean ret = true; 239 240 if (serviceGid1 == null || serviceGid1.equals("")) { 241 log("compareGid1 serviceGid is empty, return " + ret); 242 return ret; 243 } 244 // Check if gid1 match service GID1 245 if (!((gid1 != null) && (gid1.length() >= gid_length) && 246 gid1.substring(0, gid_length).equalsIgnoreCase(serviceGid1))) { 247 log(" gid1 " + gid1 + " serviceGid1 " + serviceGid1); 248 ret = false; 249 } 250 log("compareGid1 is " + (ret?"Same":"Different")); 251 return ret; 252 } 253 254 /** 255 * Get the ringing connections which during SRVCC handover. 256 */ getRingingHandoverConnection()257 public Connection getRingingHandoverConnection() { 258 for (Connection hoConn : mHandoverConnections) { 259 if (hoConn.getCall().isRinging()) { 260 return hoConn; 261 } 262 } 263 return null; 264 } 265 266 //***** Overridden from Handler 267 @Override handleMessage(Message msg)268 public abstract void handleMessage (Message msg); registerForVoiceCallStarted(Handler h, int what, Object obj)269 public abstract void registerForVoiceCallStarted(Handler h, int what, Object obj); unregisterForVoiceCallStarted(Handler h)270 public abstract void unregisterForVoiceCallStarted(Handler h); 271 @UnsupportedAppUsage registerForVoiceCallEnded(Handler h, int what, Object obj)272 public abstract void registerForVoiceCallEnded(Handler h, int what, Object obj); unregisterForVoiceCallEnded(Handler h)273 public abstract void unregisterForVoiceCallEnded(Handler h); 274 @UnsupportedAppUsage getState()275 public abstract PhoneConstants.State getState(); 276 @UnsupportedAppUsage log(String msg)277 protected abstract void log(String msg); 278 279 /** 280 * Called when the call tracker should attempt to reconcile its calls against its underlying 281 * phone implementation and cleanup any stale calls. 282 */ cleanupCalls()283 public void cleanupCalls() { 284 // no base implementation 285 } 286 dump(FileDescriptor fd, PrintWriter pw, String[] args)287 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 288 pw.println("CallTracker:"); 289 pw.println(" mPendingOperations=" + mPendingOperations); 290 pw.println(" mNeedsPoll=" + mNeedsPoll); 291 pw.println(" mLastRelevantPoll=" + mLastRelevantPoll); 292 } 293 } 294