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