1 /* 2 * Copyright (C) 2013 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.phone; 18 19 import com.android.internal.telephony.CallerInfo; 20 import com.android.internal.telephony.Connection; 21 import com.android.internal.telephony.Phone; 22 import com.android.internal.telephony.PhoneConstants; 23 import com.android.internal.telephony.TelephonyCapabilities; 24 import com.android.phone.common.CallLogAsync; 25 26 import android.os.SystemProperties; 27 import android.provider.CallLog.Calls; 28 import android.telephony.PhoneNumberUtils; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 /** 33 * Helper class for interacting with the call log. 34 */ 35 class CallLogger { 36 private static final String LOG_TAG = CallLogger.class.getSimpleName(); 37 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 1) && 38 (SystemProperties.getInt("ro.debuggable", 0) == 1); 39 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2); 40 41 private PhoneGlobals mApplication; 42 private CallLogAsync mCallLog; 43 44 public CallLogger(PhoneGlobals application, CallLogAsync callLogAsync) { 45 mApplication = application; 46 mCallLog = callLogAsync; 47 } 48 49 /** 50 * Logs a call to the call log based on the connection object passed in. 51 * 52 * @param c The connection object for the call being logged. 53 * @param callLogType The type of call log entry. 54 */ 55 public void logCall(Connection c, int callLogType) { 56 final String number = c.getAddress(); 57 final long date = c.getCreateTime(); 58 final long duration = c.getDurationMillis(); 59 final Phone phone = c.getCall().getPhone(); 60 61 final CallerInfo ci = getCallerInfoFromConnection(c); // May be null. 62 final String logNumber = getLogNumber(c, ci); 63 64 if (DBG) { 65 log("- onDisconnect(): logNumber set to:" + PhoneUtils.toLogSafePhoneNumber(logNumber) + 66 ", number set to: " + PhoneUtils.toLogSafePhoneNumber(number)); 67 } 68 69 // TODO: In getLogNumber we use the presentation from 70 // the connection for the CNAP. Should we use the one 71 // below instead? (comes from caller info) 72 73 // For international calls, 011 needs to be logged as + 74 final int presentation = getPresentation(c, ci); 75 76 final boolean isOtaspNumber = TelephonyCapabilities.supportsOtasp(phone) 77 && phone.isOtaSpNumber(number); 78 79 // Don't log OTASP calls. 80 if (!isOtaspNumber) { 81 logCall(ci, logNumber, presentation, callLogType, date, duration); 82 } 83 } 84 85 /** 86 * Came as logCall(Connection,int) but calculates the call type from the connection object. 87 */ 88 public void logCall(Connection c) { 89 final Connection.DisconnectCause cause = c.getDisconnectCause(); 90 91 // Set the "type" to be displayed in the call log (see constants in CallLog.Calls) 92 final int callLogType; 93 94 if (c.isIncoming()) { 95 callLogType = (cause == Connection.DisconnectCause.INCOMING_MISSED ? 96 Calls.MISSED_TYPE : Calls.INCOMING_TYPE); 97 } else { 98 callLogType = Calls.OUTGOING_TYPE; 99 } 100 if (VDBG) log("- callLogType: " + callLogType + ", UserData: " + c.getUserData()); 101 102 logCall(c, callLogType); 103 } 104 105 /** 106 * Logs a call to the call from the parameters passed in. 107 */ 108 public void logCall(CallerInfo ci, String number, int presentation, int callType, long start, 109 long duration) { 110 final boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(number, 111 mApplication); 112 113 // On some devices, to avoid accidental redialing of 114 // emergency numbers, we *never* log emergency calls to 115 // the Call Log. (This behavior is set on a per-product 116 // basis, based on carrier requirements.) 117 final boolean okToLogEmergencyNumber = 118 mApplication.getResources().getBoolean( 119 R.bool.allow_emergency_numbers_in_call_log); 120 121 // Don't log emergency numbers if the device doesn't allow it, 122 boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber; 123 124 if (isOkToLogThisCall) { 125 if (DBG) { 126 log("sending Calllog entry: " + ci + ", " + PhoneUtils.toLogSafePhoneNumber(number) 127 + "," + presentation + ", " + callType + ", " + start + ", " + duration); 128 } 129 130 CallLogAsync.AddCallArgs args = new CallLogAsync.AddCallArgs(mApplication, ci, number, 131 presentation, callType, start, duration); 132 mCallLog.addCall(args); 133 } 134 } 135 136 /** 137 * Get the caller info. 138 * 139 * @param conn The phone connection. 140 * @return The CallerInfo associated with the connection. Maybe null. 141 */ 142 private CallerInfo getCallerInfoFromConnection(Connection conn) { 143 CallerInfo ci = null; 144 Object o = conn.getUserData(); 145 146 if ((o == null) || (o instanceof CallerInfo)) { 147 ci = (CallerInfo) o; 148 } else { 149 ci = ((PhoneUtils.CallerInfoToken) o).currentInfo; 150 } 151 return ci; 152 } 153 154 /** 155 * Retrieve the phone number from the caller info or the connection. 156 * 157 * For incoming call the number is in the Connection object. For 158 * outgoing call we use the CallerInfo phoneNumber field if 159 * present. All the processing should have been done already (CDMA vs GSM numbers). 160 * 161 * If CallerInfo is missing the phone number, get it from the connection. 162 * Apply the Call Name Presentation (CNAP) transform in the connection on the number. 163 * 164 * @param conn The phone connection. 165 * @param callerInfo The CallerInfo. Maybe null. 166 * @return the phone number. 167 */ 168 private String getLogNumber(Connection conn, CallerInfo callerInfo) { 169 String number = null; 170 171 if (conn.isIncoming()) { 172 number = conn.getAddress(); 173 } else { 174 // For emergency and voicemail calls, 175 // CallerInfo.phoneNumber does *not* contain a valid phone 176 // number. Instead it contains an I18N'd string such as 177 // "Emergency Number" or "Voice Mail" so we get the number 178 // from the connection. 179 if (null == callerInfo || TextUtils.isEmpty(callerInfo.phoneNumber) || 180 callerInfo.isEmergencyNumber() || callerInfo.isVoiceMailNumber()) { 181 if (conn.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 182 // In cdma getAddress() is not always equals to getOrigDialString(). 183 number = conn.getOrigDialString(); 184 } else { 185 number = conn.getAddress(); 186 } 187 } else { 188 number = callerInfo.phoneNumber; 189 } 190 } 191 192 if (null == number) { 193 return null; 194 } else { 195 int presentation = conn.getNumberPresentation(); 196 197 // Do final CNAP modifications. 198 String newNumber = PhoneUtils.modifyForSpecialCnapCases(mApplication, callerInfo, 199 number, presentation); 200 201 if (!PhoneNumberUtils.isUriNumber(number)) { 202 number = PhoneNumberUtils.stripSeparators(number); 203 } 204 if (VDBG) log("getLogNumber: " + number); 205 return number; 206 } 207 } 208 209 /** 210 * Get the presentation from the callerinfo if not null otherwise, 211 * get it from the connection. 212 * 213 * @param conn The phone connection. 214 * @param callerInfo The CallerInfo. Maybe null. 215 * @return The presentation to use in the logs. 216 */ 217 private int getPresentation(Connection conn, CallerInfo callerInfo) { 218 int presentation; 219 220 if (null == callerInfo) { 221 presentation = conn.getNumberPresentation(); 222 } else { 223 presentation = callerInfo.numberPresentation; 224 if (DBG) log("- getPresentation(): ignoring connection's presentation: " + 225 conn.getNumberPresentation()); 226 } 227 if (DBG) log("- getPresentation: presentation: " + presentation); 228 return presentation; 229 } 230 231 private void log(String msg) { 232 Log.d(LOG_TAG, msg); 233 } 234 } 235