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