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.content.Context; 20 import android.os.AsyncResult; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.os.PersistableBundle; 24 import android.os.SystemProperties; 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 protected int mPendingOperations; 45 protected boolean mNeedsPoll; 46 protected Message mLastRelevantPoll; 47 protected ArrayList<Connection> mHandoverConnections = new ArrayList<Connection>(); 48 49 public CommandsInterface mCi; 50 51 protected boolean mNumberConverted = false; 52 private final int VALID_COMPARE_LENGTH = 3; 53 54 //***** Events 55 56 protected static final int EVENT_POLL_CALLS_RESULT = 1; 57 protected static final int EVENT_CALL_STATE_CHANGE = 2; 58 protected static final int EVENT_REPOLL_AFTER_DELAY = 3; 59 protected static final int EVENT_OPERATION_COMPLETE = 4; 60 protected static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5; 61 62 protected static final int EVENT_SWITCH_RESULT = 8; 63 protected static final int EVENT_RADIO_AVAILABLE = 9; 64 protected static final int EVENT_RADIO_NOT_AVAILABLE = 10; 65 protected static final int EVENT_CONFERENCE_RESULT = 11; 66 protected static final int EVENT_SEPARATE_RESULT = 12; 67 protected static final int EVENT_ECT_RESULT = 13; 68 protected static final int EVENT_EXIT_ECM_RESPONSE_CDMA = 14; 69 protected static final int EVENT_CALL_WAITING_INFO_CDMA = 15; 70 protected static final int EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA = 16; 71 protected static final int EVENT_THREE_WAY_DIAL_BLANK_FLASH = 20; 72 pollCallsWhenSafe()73 protected void pollCallsWhenSafe() { 74 mNeedsPoll = true; 75 76 if (checkNoOperationsPending()) { 77 mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); 78 mCi.getCurrentCalls(mLastRelevantPoll); 79 } 80 } 81 82 protected void pollCallsAfterDelay()83 pollCallsAfterDelay() { 84 Message msg = obtainMessage(); 85 86 msg.what = EVENT_REPOLL_AFTER_DELAY; 87 sendMessageDelayed(msg, POLL_DELAY_MSEC); 88 } 89 90 protected boolean isCommandExceptionRadioNotAvailable(Throwable e)91 isCommandExceptionRadioNotAvailable(Throwable e) { 92 return e != null && e instanceof CommandException 93 && ((CommandException)e).getCommandError() 94 == CommandException.Error.RADIO_NOT_AVAILABLE; 95 } 96 handlePollCalls(AsyncResult ar)97 protected abstract void handlePollCalls(AsyncResult ar); 98 getHoConnection(DriverCall dc)99 protected Connection getHoConnection(DriverCall dc) { 100 for (Connection hoConn : mHandoverConnections) { 101 log("getHoConnection - compare number: hoConn= " + hoConn.toString()); 102 if (hoConn.getAddress() != null && hoConn.getAddress().contains(dc.number)) { 103 log("getHoConnection: Handover connection match found = " + hoConn.toString()); 104 return hoConn; 105 } 106 } 107 for (Connection hoConn : mHandoverConnections) { 108 log("getHoConnection: compare state hoConn= " + hoConn.toString()); 109 if (hoConn.getStateBeforeHandover() == Call.stateFromDCState(dc.state)) { 110 log("getHoConnection: Handover connection match found = " + hoConn.toString()); 111 return hoConn; 112 } 113 } 114 return null; 115 } 116 notifySrvccState(Call.SrvccState state, ArrayList<Connection> c)117 protected void notifySrvccState(Call.SrvccState state, ArrayList<Connection> c) { 118 if (state == Call.SrvccState.STARTED && c != null) { 119 // SRVCC started. Prepare handover connections list 120 mHandoverConnections.addAll(c); 121 } else if (state != Call.SrvccState.COMPLETED) { 122 // SRVCC FAILED/CANCELED. Clear the handover connections list 123 // Individual connections will be removed from the list in handlePollCalls() 124 mHandoverConnections.clear(); 125 } 126 log("notifySrvccState: mHandoverConnections= " + mHandoverConnections.toString()); 127 } 128 handleRadioAvailable()129 protected void handleRadioAvailable() { 130 pollCallsWhenSafe(); 131 } 132 133 /** 134 * Obtain a complete message that indicates that this operation 135 * does not require polling of getCurrentCalls(). However, if other 136 * operations that do need getCurrentCalls() are pending or are 137 * scheduled while this operation is pending, the invocation 138 * of getCurrentCalls() will be postponed until this 139 * operation is also complete. 140 */ 141 protected Message obtainNoPollCompleteMessage(int what)142 obtainNoPollCompleteMessage(int what) { 143 mPendingOperations++; 144 mLastRelevantPoll = null; 145 return obtainMessage(what); 146 } 147 148 /** 149 * @return true if we're idle or there's a call to getCurrentCalls() pending 150 * but nothing else 151 */ 152 private boolean checkNoOperationsPending()153 checkNoOperationsPending() { 154 if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" + 155 mPendingOperations); 156 return mPendingOperations == 0; 157 } 158 159 /** 160 * Routine called from dial to check if the number is a test Emergency number 161 * and if so remap the number. This allows a short emergency number to be remapped 162 * to a regular number for testing how the frameworks handles emergency numbers 163 * without actually calling an emergency number. 164 * 165 * This is not a full test and is not a substitute for testing real emergency 166 * numbers but can be useful. 167 * 168 * To use this feature set a system property ril.test.emergencynumber to a pair of 169 * numbers separated by a colon. If the first number matches the number parameter 170 * this routine returns the second number. Example: 171 * 172 * ril.test.emergencynumber=112:1-123-123-45678 173 * 174 * To test Dial 112 take call then hang up on MO device to enter ECM 175 * see RIL#processSolicited RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND 176 * 177 * @param dialString to test if it should be remapped 178 * @return the same number or the remapped number. 179 */ checkForTestEmergencyNumber(String dialString)180 protected String checkForTestEmergencyNumber(String dialString) { 181 String testEn = SystemProperties.get("ril.test.emergencynumber"); 182 if (DBG_POLL) { 183 log("checkForTestEmergencyNumber: dialString=" + dialString + 184 " testEn=" + testEn); 185 } 186 if (!TextUtils.isEmpty(testEn)) { 187 String values[] = testEn.split(":"); 188 log("checkForTestEmergencyNumber: values.length=" + values.length); 189 if (values.length == 2) { 190 if (values[0].equals( 191 android.telephony.PhoneNumberUtils.stripSeparators(dialString))) { 192 // mCi will be null for ImsPhoneCallTracker. 193 if (mCi != null) { 194 mCi.testingEmergencyCall(); 195 } 196 log("checkForTestEmergencyNumber: remap " + 197 dialString + " to " + values[1]); 198 dialString = values[1]; 199 } 200 } 201 } 202 return dialString; 203 } 204 convertNumberIfNecessary(Phone phone, String dialNumber)205 protected String convertNumberIfNecessary(Phone phone, String dialNumber) { 206 if (dialNumber == null) { 207 return dialNumber; 208 } 209 String[] convertMaps = null; 210 CarrierConfigManager configManager = (CarrierConfigManager) 211 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 212 PersistableBundle bundle = configManager.getConfig(); 213 if (bundle != null) { 214 convertMaps = 215 bundle.getStringArray(CarrierConfigManager.KEY_DIAL_STRING_REPLACE_STRING_ARRAY); 216 } 217 if (convertMaps == null) { 218 // By default no replacement is necessary 219 log("convertNumberIfNecessary convertMaps is null"); 220 return dialNumber; 221 } 222 223 log("convertNumberIfNecessary Roaming" 224 + " convertMaps.length " + convertMaps.length 225 + " dialNumber.length() " + dialNumber.length()); 226 227 if (convertMaps.length < 1 || dialNumber.length() < VALID_COMPARE_LENGTH) { 228 return dialNumber; 229 } 230 231 String[] entry; 232 String outNumber = ""; 233 for(String convertMap : convertMaps) { 234 log("convertNumberIfNecessary: " + convertMap); 235 // entry format is "dialStringToReplace:dialStringReplacement" 236 entry = convertMap.split(":"); 237 if (entry != null && entry.length > 1) { 238 String dsToReplace = entry[0]; 239 String dsReplacement = entry[1]; 240 if (!TextUtils.isEmpty(dsToReplace) && dialNumber.equals(dsToReplace)) { 241 // Needs to be converted 242 if (!TextUtils.isEmpty(dsReplacement) && dsReplacement.endsWith("MDN")) { 243 String mdn = phone.getLine1Number(); 244 if (!TextUtils.isEmpty(mdn)) { 245 if (mdn.startsWith("+")) { 246 outNumber = mdn; 247 } else { 248 outNumber = dsReplacement.substring(0, dsReplacement.length() -3) 249 + mdn; 250 } 251 } 252 } else { 253 outNumber = dsReplacement; 254 } 255 break; 256 } 257 } 258 } 259 260 if (!TextUtils.isEmpty(outNumber)) { 261 log("convertNumberIfNecessary: convert service number"); 262 mNumberConverted = true; 263 return outNumber; 264 } 265 266 return dialNumber; 267 268 } 269 compareGid1(Phone phone, String serviceGid1)270 private boolean compareGid1(Phone phone, String serviceGid1) { 271 String gid1 = phone.getGroupIdLevel1(); 272 int gid_length = serviceGid1.length(); 273 boolean ret = true; 274 275 if (serviceGid1 == null || serviceGid1.equals("")) { 276 log("compareGid1 serviceGid is empty, return " + ret); 277 return ret; 278 } 279 // Check if gid1 match service GID1 280 if (!((gid1 != null) && (gid1.length() >= gid_length) && 281 gid1.substring(0, gid_length).equalsIgnoreCase(serviceGid1))) { 282 log(" gid1 " + gid1 + " serviceGid1 " + serviceGid1); 283 ret = false; 284 } 285 log("compareGid1 is " + (ret?"Same":"Different")); 286 return ret; 287 } 288 289 //***** Overridden from Handler 290 @Override handleMessage(Message msg)291 public abstract void handleMessage (Message msg); registerForVoiceCallStarted(Handler h, int what, Object obj)292 public abstract void registerForVoiceCallStarted(Handler h, int what, Object obj); unregisterForVoiceCallStarted(Handler h)293 public abstract void unregisterForVoiceCallStarted(Handler h); registerForVoiceCallEnded(Handler h, int what, Object obj)294 public abstract void registerForVoiceCallEnded(Handler h, int what, Object obj); unregisterForVoiceCallEnded(Handler h)295 public abstract void unregisterForVoiceCallEnded(Handler h); getState()296 public abstract PhoneConstants.State getState(); log(String msg)297 protected abstract void log(String msg); 298 299 /** 300 * Called when the call tracker should attempt to reconcile its calls against its underlying 301 * phone implementation and cleanup any stale calls. 302 */ cleanupCalls()303 public void cleanupCalls() { 304 // no base implementation 305 } 306 dump(FileDescriptor fd, PrintWriter pw, String[] args)307 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 308 pw.println("CallTracker:"); 309 pw.println(" mPendingOperations=" + mPendingOperations); 310 pw.println(" mNeedsPoll=" + mNeedsPoll); 311 pw.println(" mLastRelevantPoll=" + mLastRelevantPoll); 312 } 313 } 314