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