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