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 }
public void doReceive(Context context, Intent intent) { if (DBG) Log.v(TAG, "doReceive: " + intent); boolean alreadyCalled; String number; String originalUri; alreadyCalled = intent.getBooleanExtra(OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false); if (alreadyCalled) { if (DBG) Log.v(TAG, "CALL already placed -- returning."); return; } // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData // is used as the actual number to call. (If null, no call will be // placed.) number = getResultData(); if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'"); final PhoneApp app = PhoneApp.getInstance(); if (isOtaActive()) { // OTASP call is active. Don't allow new outgoing calls at all Log.w(TAG, "OTASP call is active: disallowing a new outgoing call."); return; } if (number == null) { if (DBG) Log.v(TAG, "CALL cancelled (null number), returning..."); return; } else if (TelephonyCapabilities.supportsOtasp(app.phone) && (app.phone.getState() != Phone.State.IDLE) && (app.phone.isOtaSpNumber(number))) { if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning."); return; } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, context)) { // Just like 3rd-party apps aren't allowed to place emergency // calls via the ACTION_CALL intent, we also don't allow 3rd // party apps to use the NEW_OUTGOING_CALL broadcast to rewrite // an outgoing call into an emergency number. Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + "."); return; } originalUri = intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI); if (originalUri == null) { Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning."); return; } Uri uri = Uri.parse(originalUri); // We already called convertKeypadLettersToDigits() and // stripSeparators() way back in onCreate(), before we sent out the // NEW_OUTGOING_CALL broadcast. But we need to do it again here // too, since the number might have been modified/rewritten during // the broadcast (and may now contain letters or separators again.) number = PhoneNumberUtils.convertKeypadLettersToDigits(number); number = PhoneNumberUtils.stripSeparators(number); if (DBG) Log.v(TAG, "doReceive: proceeding with call..."); if (VDBG) Log.v(TAG, "- uri: " + uri); if (VDBG) Log.v(TAG, "- actual number to dial: '" + number + "'"); startSipCallOptionHandler(context, intent, uri, number); }