/** * Add call number info to call history database for international dialing feature !!!! need to * check okToLogThisCall is suitable for international dialing feature * * @param number The number for the call being logged. * @param start The start time of the call being logged. * @param duration The duration of the call being logged. * @param isSipCall whether sip call * @param slotId The slot id for the call being logged. */ private void addCallHistoryAsync( String number, long start, long duration, boolean isSipCall, int slotId) { final boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(number, mApplication); if (!isEmergencyNumber && !mIsComing && mVtCall != CALL_TYPE_VIDEO && !isSipCall && duration >= CallNotifier.CALL_DURATION_THRESHOLD_FOR_CALL_HISTORY && slotId > -1) { String countryISO = CallOptionUtils.getCurrentCountryISO(PhoneGlobals.getInstance()); try { new CallHistoryAsync() .addCall( new CallHistoryAsync.AddCallArgs( PhoneGlobals.getInstance(), number, countryISO, start, slotId, GeminiUtils.isGeminiSupport())); } catch (SQLiteDiskIOException e) { // TODO Auto-generated catch block Log.e(LOG_TAG, "Error!! - onDisconnect() Disk Full!"); e.printStackTrace(); } } }
/** * getCallerInfo given a phone number and subscription, look up in the call-log database for the * matching caller id info. * * @param context the context used to get the ContentResolver * @param number the phone number used to lookup caller id * @param subId the subscription for checking for if voice mail number or not * @return the CallerInfo which contains the caller id for the given number. The returned * CallerInfo is null if no number is supplied. If a matching number is not found, then a * generic caller info is returned, with all relevant fields empty or null. */ public static CallerInfo getCallerInfo(Context context, String number, int subId) { if (TextUtils.isEmpty(number)) { return null; } // Change the callerInfo number ONLY if it is an emergency number // or if it is the voicemail number. If it is either, take a // shortcut and skip the query. if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) { return new CallerInfo().markAsEmergency(context); } else if (PhoneNumberUtils.isVoiceMailNumber(subId, number)) { return new CallerInfo().markAsVoiceMail(); } Uri contactUri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, Uri.encode(number)); CallerInfo info = getCallerInfo(context, contactUri); info = doSecondaryLookupIfNecessary(context, number, info); // if no query results were returned with a viable number, // fill in the original number value we used to query with. if (TextUtils.isEmpty(info.phoneNumber)) { info.phoneNumber = number; } return info; }
/** Logs a call to the call from the parameters passed in. */ public void logCall( CallerInfo ci, String number, int presentation, int callType, long start, long duration, int slotId) { final boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(number, mApplication); // On some devices, to avoid accidental redialing of // emergency numbers, we *never* log emergency calls to // the Call Log. (This behavior is set on a per-product // basis, based on carrier requirements.) final boolean okToLogEmergencyNumber = mApplication.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log); // Don't log emergency numbers if the device doesn't allow it, boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber; if (isOkToLogThisCall) { if (DBG) { log( "sending Calllog entry: " + ci + ", " + PhoneUtils.toLogSafePhoneNumber(number) + "," + presentation + ", " + callType + ", " + start + ", " + duration); } /// M: For GEMINI or Other type call(Ex: sip call/video call) @{ CallLogAsync.AddCallArgs args; if (mPhoneType == PhoneConstants.PHONE_TYPE_CDMA && mIsCdmaCallWaitingReject) { args = getCallArgsForCdmaCallWaitingReject( ci, number, presentation, callType, start, duration, slotId); } else { args = getCallArgs(ci, number, presentation, callType, start, duration, slotId); } /// @} try { mCallLog.addCall(args); } catch (SQLiteDiskIOException e) { // TODO Auto-generated catch block Log.e(LOG_TAG, "Error!! - logCall() Disk Full!"); e.printStackTrace(); } } reset(); }
private void processIntent(Intent intent) { String action = intent.getAction(); intent.putExtra(SUBSCRIPTION_KEY, mSubscription); Log.d(TAG, "outGoingcallBroadCaster action is" + action); String number = PhoneNumberUtils.getNumberFromIntent(intent, this); Log.d(TAG, " number from Intent : " + number); // Check the number, don't convert for sip uri // TODO put uriNumber under PhoneNumberUtils if (number != null) { if (!PhoneNumberUtils.isUriNumber(number)) { number = PhoneNumberUtils.convertKeypadLettersToDigits(number); number = PhoneNumberUtils.stripSeparators(number); } } // If true, this flag will indicate that the current call is a special kind // of call (most likely an emergency number) that 3rd parties aren't allowed // to intercept or affect in any way. (In that case, we start the call // immediately rather than going through the NEW_OUTGOING_CALL sequence.) boolean callNow; if (getClass().getName().equals(intent.getComponent().getClassName())) { // If we were launched directly from the OutgoingCallBroadcaster, // not one of its more privileged aliases, then make sure that // only the non-privileged actions are allowed. if (!Intent.ACTION_CALL.equals(intent.getAction())) { Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL"); intent.setAction(Intent.ACTION_CALL); } } // Check whether or not this is an emergency number, in order to // enforce the restriction that only the CALL_PRIVILEGED and // CALL_EMERGENCY intents are allowed to make emergency calls. // // (Note that the ACTION_CALL check below depends on the result of // isPotentialLocalEmergencyNumber() rather than just plain // isLocalEmergencyNumber(), to be 100% certain that we *don't* // allow 3rd party apps to make emergency calls by passing in an // "invalid" number like "9111234" that isn't technically an // emergency number but might still result in an emergency call // with some networks.) final boolean isExactEmergencyNumber = (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(number, this); final boolean isPotentialEmergencyNumber = (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, this); if (VDBG) { Log.v(TAG, "- Checking restrictions for number '" + number + "':"); Log.v(TAG, " isExactEmergencyNumber = " + isExactEmergencyNumber); Log.v(TAG, " isPotentialEmergencyNumber = " + isPotentialEmergencyNumber); } /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ // TODO: This code is redundant with some code in InCallScreen: refactor. if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) { // We're handling a CALL_PRIVILEGED intent, so we know this request came // from a trusted source (like the built-in dialer.) So even a number // that's *potentially* an emergency number can safely be promoted to // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from // the dialer if you really want to.) action = isPotentialEmergencyNumber ? Intent.ACTION_CALL_EMERGENCY : Intent.ACTION_CALL; if (DBG) Log.v(TAG, "- updating action from CALL_PRIVILEGED to " + action); intent.setAction(action); } if (Intent.ACTION_CALL.equals(action)) { if (isPotentialEmergencyNumber) { Log.w( TAG, "Cannot call potential emergency number '" + number + "' with CALL Intent " + intent + "."); Log.i(TAG, "Launching default dialer instead..."); Intent invokeFrameworkDialer = new Intent(); // TwelveKeyDialer is in a tab so we really want // DialtactsActivity. Build the intent 'manually' to // use the java resolver to find the dialer class (as // opposed to a Context which look up known android // packages only) invokeFrameworkDialer.setClassName( "com.android.contacts", "com.android.contacts.DialtactsActivity"); invokeFrameworkDialer.setAction(Intent.ACTION_DIAL); invokeFrameworkDialer.setData(intent.getData()); invokeFrameworkDialer.putExtra(SUBSCRIPTION_KEY, mSubscription); if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: " + invokeFrameworkDialer); startActivity(invokeFrameworkDialer); finish(); return; } intent.putExtra(SUBSCRIPTION_KEY, mSubscription); Log.d(TAG, "for non emergency call,sub is :" + mSubscription); callNow = false; } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED // intent that we just turned into a CALL_EMERGENCY intent (see // above), or else it really is an CALL_EMERGENCY intent that // came directly from some other app (e.g. the EmergencyDialer // activity built in to the Phone app.) // Make sure it's at least *possible* that this is really an // emergency number. if (!isPotentialEmergencyNumber) { Log.w( TAG, "Cannot call non-potential-emergency number " + number + " with EMERGENCY_CALL Intent " + intent + "."); finish(); return; } int sub = PhoneApp.getInstance().getVoiceSubscriptionInService(); intent.putExtra(SUBSCRIPTION_KEY, sub); Log.d(TAG, "Attempting emergency call on sub :" + sub); callNow = true; } else { Log.e(TAG, "Unhandled Intent " + intent + "."); finish(); return; } // Make sure the screen is turned on. This is probably the right // thing to do, and more importantly it works around an issue in the // activity manager where we will not launch activities consistently // when the screen is off (since it is trying to keep them paused // and has... issues). // // Also, this ensures the device stays awake while doing the following // broadcast; technically we should be holding a wake lock here // as well. PhoneApp.getInstance().wakeUpScreen(); /* If number is null, we're probably trying to call a non-existent voicemail number, * send an empty flash or something else is fishy. Whatever the problem, there's no * number, so there's no point in allowing apps to modify the number. */ if (number == null || TextUtils.isEmpty(number)) { if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) { Log.i(TAG, "onCreate: SEND_EMPTY_FLASH..."); PhoneUtils.sendEmptyFlash(PhoneApp.getInstance().getPhone()); finish(); return; } else { Log.i(TAG, "onCreate: null or empty number, setting callNow=true..."); callNow = true; intent.putExtra(SUBSCRIPTION_KEY, mSubscription); } } if (callNow) { // This is a special kind of call (most likely an emergency number) // that 3rd parties aren't allowed to intercept or affect in any way. // So initiate the outgoing call immediately. if (DBG) Log.v(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent); // Initiate the outgoing call, and simultaneously launch the // InCallScreen to display the in-call UI: PhoneApp.getInstance().callController.placeCall(intent); // Note we do *not* "return" here, but instead continue and // send the ACTION_NEW_OUTGOING_CALL broadcast like for any // other outgoing call. (But when the broadcast finally // reaches the OutgoingCallReceiver, we'll know not to // initiate the call again because of the presence of the // EXTRA_ALREADY_CALLED extra.) } // For now, SIP calls will be processed directly without a // NEW_OUTGOING_CALL broadcast. // // TODO: In the future, though, 3rd party apps *should* be allowed to // intercept outgoing calls to SIP addresses as well. To do this, we should // (1) update the NEW_OUTGOING_CALL intent documentation to explain this // case, and (2) pass the outgoing SIP address by *not* overloading the // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold // the outgoing SIP address. (Be sure to document whether it's a URI or just // a plain address, whether it could be a tel: URI, etc.) Uri uri = intent.getData(); String scheme = uri.getScheme(); if (Constants.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) { startSipCallOptionHandler(this, intent, uri, number); finish(); return; // TODO: if there's ever a way for SIP calls to trigger a // "callNow=true" case (see above), we'll need to handle that // case here too (most likely by just doing nothing at all.) } final String callOrigin = intent.getStringExtra(PhoneApp.EXTRA_CALL_ORIGIN); if (callOrigin != null) { if (DBG) Log.v(TAG, "Call origin is passed (" + callOrigin + ")"); PhoneApp.getInstance().setLatestActiveCallOrigin(callOrigin); } else { if (DBG) Log.v(TAG, "Call origin is not passed. Reset current one."); PhoneApp.getInstance().setLatestActiveCallOrigin(null); } Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); if (number != null) { broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); } PhoneUtils.checkAndCopyPhoneProviderExtras(intent, broadcastIntent); broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow); broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString()); broadcastIntent.putExtra(SUBSCRIPTION_KEY, mSubscription); if (DBG) Log.v(TAG, "Broadcasting intent: " + broadcastIntent + "."); sendOrderedBroadcast( broadcastIntent, PERMISSION, new OutgoingCallReceiver(), null, // scheduler Activity.RESULT_OK, // initialCode number, // initialData: initial value for the result data null); // initialExtras }
/** Returns a mask of capabilities for the connection such as merge, hold, etc. */ private int getCapabilitiesFor(Connection connection, Call call, boolean isForConference) { final boolean callIsActive = (call.getState() == Call.State.ACTIVE); final boolean callIsBackground = (call.getState() == Call.State.ONHOLD); final Phone phone = connection.getCall().getPhone(); boolean canAddCall = false; boolean canMergeCall = false; boolean canSwapCall = false; boolean canRespondViaText = false; boolean canMute = false; boolean canAddParticipant = false; boolean canModifyCall = false; boolean voicePrivacy = false; final boolean supportHold; final boolean canHold; final boolean genericConf = isForConference && (connection.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA); if (!MSimTelephonyManager.getDefault().isMultiSimEnabled()) { supportHold = PhoneUtils.okToSupportHold(mCallManager); canHold = (supportHold ? PhoneUtils.okToHoldCall(mCallManager) : false); // only applies to active calls if (callIsActive) { canMergeCall = PhoneUtils.okToMergeCalls(mCallManager); canSwapCall = PhoneUtils.okToSwapCalls(mCallManager); } canAddCall = PhoneUtils.okToAddCall(mCallManager); } else { final int subscription = call.getSubscription(); supportHold = PhoneUtils.okToSupportHold(mCallManager, subscription); canHold = (supportHold ? PhoneUtils.okToHoldCall(mCallManager, subscription) : false); // only applies to active calls if (callIsActive) { canMergeCall = PhoneUtils.okToMergeCalls(mCallManager, subscription); canSwapCall = PhoneUtils.okToSwapCalls(mCallManager, subscription); } canAddCall = PhoneUtils.okToAddCall(mCallManager, subscription); } if (callIsActive || callIsBackground) { canModifyCall = PhoneUtils.isVTModifyAllowed(connection); } canAddParticipant = PhoneUtils.canAddParticipant(mCallManager) && canAddCall; // "Mute": only enabled when the foreground call is ACTIVE. // (It's meaningless while on hold, or while DIALING/ALERTING.) // It's also explicitly disabled during emergency calls or if // emergency callback mode (ECM) is active. boolean isEmergencyCall = false; if (connection != null) { isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(connection.getAddress(), phone.getContext()); } boolean isECM = PhoneUtils.isPhoneInEcm(phone); if (isEmergencyCall || isECM) { // disable "Mute" item canMute = false; } else { canMute = callIsActive; } canRespondViaText = RejectWithTextMessageManager.allowRespondViaSmsForCall(call, connection); // special rules section! // CDMA always has Add if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { canAddCall = true; } // Voice Privacy for CDMA if ((phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) && mVoicePrivacyState) { voicePrivacy = true; } int retval = 0x0; if (canHold) { retval |= Capabilities.HOLD; } if (supportHold) { retval |= Capabilities.SUPPORT_HOLD; } if (canAddCall) { retval |= Capabilities.ADD_CALL; } if (canMergeCall) { retval |= Capabilities.MERGE_CALLS; } if (canSwapCall) { retval |= Capabilities.SWAP_CALLS; } if (canRespondViaText) { retval |= Capabilities.RESPOND_VIA_TEXT; } if (canMute) { retval |= Capabilities.MUTE; } if (canAddParticipant) { retval |= Capabilities.ADD_PARTICIPANT; } if (genericConf) { retval |= Capabilities.GENERIC_CONFERENCE; } if (canModifyCall) { retval |= Capabilities.MODIFY_CALL; } if (voicePrivacy) { retval |= Capabilities.VOICE_PRIVACY; } return retval; }