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: mHandoverConnections= " + mHandoverConnections.toString()); 137 } 138 handleRadioAvailable()139 protected void handleRadioAvailable() { 140 pollCallsWhenSafe(); 141 } 142 143 /** 144 * Obtain a complete message that indicates that this operation 145 * does not require polling of getCurrentCalls(). However, if other 146 * operations that do need getCurrentCalls() are pending or are 147 * scheduled while this operation is pending, the invocation 148 * of getCurrentCalls() will be postponed until this 149 * operation is also complete. 150 */ 151 protected Message obtainNoPollCompleteMessage(int what)152 obtainNoPollCompleteMessage(int what) { 153 mPendingOperations++; 154 mLastRelevantPoll = null; 155 return obtainMessage(what); 156 } 157 158 /** 159 * @return true if we're idle or there's a call to getCurrentCalls() pending 160 * but nothing else 161 */ 162 private boolean checkNoOperationsPending()163 checkNoOperationsPending() { 164 if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" + 165 mPendingOperations); 166 return mPendingOperations == 0; 167 } 168 convertNumberIfNecessary(Phone phone, String dialNumber)169 protected String convertNumberIfNecessary(Phone phone, String dialNumber) { 170 if (dialNumber == null) { 171 return dialNumber; 172 } 173 String[] convertMaps = null; 174 CarrierConfigManager configManager = (CarrierConfigManager) 175 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 176 PersistableBundle bundle = configManager.getConfigForSubId(phone.getSubId()); 177 if (bundle != null) { 178 convertMaps = 179 bundle.getStringArray(CarrierConfigManager.KEY_DIAL_STRING_REPLACE_STRING_ARRAY); 180 } 181 if (convertMaps == null) { 182 // By default no replacement is necessary 183 log("convertNumberIfNecessary convertMaps is null"); 184 return dialNumber; 185 } 186 187 log("convertNumberIfNecessary Roaming" 188 + " convertMaps.length " + convertMaps.length 189 + " dialNumber.length() " + dialNumber.length()); 190 191 if (convertMaps.length < 1 || dialNumber.length() < VALID_COMPARE_LENGTH) { 192 return dialNumber; 193 } 194 195 String[] entry; 196 String outNumber = ""; 197 for(String convertMap : convertMaps) { 198 log("convertNumberIfNecessary: " + convertMap); 199 // entry format is "dialStringToReplace:dialStringReplacement" 200 entry = convertMap.split(":"); 201 if (entry != null && entry.length > 1) { 202 String dsToReplace = entry[0]; 203 String dsReplacement = entry[1]; 204 if (!TextUtils.isEmpty(dsToReplace) && dialNumber.equals(dsToReplace)) { 205 // Needs to be converted 206 if (!TextUtils.isEmpty(dsReplacement) && dsReplacement.endsWith("MDN")) { 207 String mdn = phone.getLine1Number(); 208 if (!TextUtils.isEmpty(mdn)) { 209 if (mdn.startsWith("+")) { 210 outNumber = mdn; 211 } else { 212 outNumber = dsReplacement.substring(0, dsReplacement.length() -3) 213 + mdn; 214 } 215 } 216 } else { 217 outNumber = dsReplacement; 218 } 219 break; 220 } 221 } 222 } 223 224 if (!TextUtils.isEmpty(outNumber)) { 225 log("convertNumberIfNecessary: convert service number"); 226 mNumberConverted = true; 227 return outNumber; 228 } 229 230 return dialNumber; 231 232 } 233 compareGid1(Phone phone, String serviceGid1)234 private boolean compareGid1(Phone phone, String serviceGid1) { 235 String gid1 = phone.getGroupIdLevel1(); 236 int gid_length = serviceGid1.length(); 237 boolean ret = true; 238 239 if (serviceGid1 == null || serviceGid1.equals("")) { 240 log("compareGid1 serviceGid is empty, return " + ret); 241 return ret; 242 } 243 // Check if gid1 match service GID1 244 if (!((gid1 != null) && (gid1.length() >= gid_length) && 245 gid1.substring(0, gid_length).equalsIgnoreCase(serviceGid1))) { 246 log(" gid1 " + gid1 + " serviceGid1 " + serviceGid1); 247 ret = false; 248 } 249 log("compareGid1 is " + (ret?"Same":"Different")); 250 return ret; 251 } 252 253 /** 254 * Get the ringing connections which during SRVCC handover. 255 */ getRingingHandoverConnection()256 public Connection getRingingHandoverConnection() { 257 for (Connection hoConn : mHandoverConnections) { 258 if (hoConn.getCall().isRinging()) { 259 return hoConn; 260 } 261 } 262 return null; 263 } 264 265 //***** Overridden from Handler 266 @Override handleMessage(Message msg)267 public abstract void handleMessage (Message msg); registerForVoiceCallStarted(Handler h, int what, Object obj)268 public abstract void registerForVoiceCallStarted(Handler h, int what, Object obj); unregisterForVoiceCallStarted(Handler h)269 public abstract void unregisterForVoiceCallStarted(Handler h); 270 @UnsupportedAppUsage registerForVoiceCallEnded(Handler h, int what, Object obj)271 public abstract void registerForVoiceCallEnded(Handler h, int what, Object obj); unregisterForVoiceCallEnded(Handler h)272 public abstract void unregisterForVoiceCallEnded(Handler h); 273 @UnsupportedAppUsage getState()274 public abstract PhoneConstants.State getState(); 275 @UnsupportedAppUsage log(String msg)276 protected abstract void log(String msg); 277 278 /** 279 * Called when the call tracker should attempt to reconcile its calls against its underlying 280 * phone implementation and cleanup any stale calls. 281 */ cleanupCalls()282 public void cleanupCalls() { 283 // no base implementation 284 } 285 dump(FileDescriptor fd, PrintWriter pw, String[] args)286 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 287 pw.println("CallTracker:"); 288 pw.println(" mPendingOperations=" + mPendingOperations); 289 pw.println(" mNeedsPoll=" + mNeedsPoll); 290 pw.println(" mLastRelevantPoll=" + mLastRelevantPoll); 291 } 292 } 293