Add emergency call service selector.
Change-Id: I85b8c0a6c6d2db668caaf27abe6b022e8e06cc8c
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
index 2db6255..195ac17 100644
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ b/src/com/android/telecomm/CallServiceAdapter.java
@@ -174,7 +174,7 @@
/** {@inheritDoc} */
@Override
public void setIsCompatibleWith(String callId, boolean isCompatible) {
- Log.v(this, "setIsCompatibleWith id: %d, isCompatible: %b", callId, isCompatible);
+ Log.v(this, "setIsCompatibleWith id: %s, isCompatible: %b", callId, isCompatible);
mCallIdMapper.checkValidCallId(callId);
mHandler.obtainMessage(MSG_SET_IS_COMPATIBLE_WITH, isCompatible ? 1 : 0, 0, callId).
sendToTarget();
diff --git a/src/com/android/telecomm/CallServiceSelectorRepository.java b/src/com/android/telecomm/CallServiceSelectorRepository.java
index 50d26e8..709d440 100644
--- a/src/com/android/telecomm/CallServiceSelectorRepository.java
+++ b/src/com/android/telecomm/CallServiceSelectorRepository.java
@@ -93,8 +93,9 @@
ServiceInfo serviceInfo = entry.serviceInfo;
if (serviceInfo != null) {
// The entry resolves to a proper service, add it to the list of selector names.
- selectorNames.add(
- new ComponentName(serviceInfo.packageName, serviceInfo.name));
+ ComponentName componentName =
+ new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ selectorNames.add(componentName);
}
}
diff --git a/src/com/android/telecomm/CallServiceSelectorWrapper.java b/src/com/android/telecomm/CallServiceSelectorWrapper.java
index f4ddf19..75aab41 100644
--- a/src/com/android/telecomm/CallServiceSelectorWrapper.java
+++ b/src/com/android/telecomm/CallServiceSelectorWrapper.java
@@ -25,9 +25,10 @@
import android.telecomm.CallServiceSelector;
import android.telecomm.TelecommConstants;
-import com.google.common.base.Preconditions;
+import com.android.internal.telecomm.ICallServiceSelectionResponse;
import com.android.internal.telecomm.ICallServiceSelector;
import com.android.internal.telecomm.ICallServiceSelectorAdapter;
+import com.google.common.base.Preconditions;
import java.util.List;
@@ -43,15 +44,42 @@
private final CallServiceSelectorAdapter mAdapter;
/**
- * Creates a call-service selector for the specified component.
+ * Creates a call-service selector for the specified component using the specified action to
+ * bind to it.
+ *
+ * @param action The action used to bind to the selector.
+ * @param componentName The component name of the service.
+ * @param callsManager The calls manager.
+ * @param outgoingCallsManager The outgoing calls manager.
+ */
+ CallServiceSelectorWrapper(
+ String action,
+ ComponentName componentName,
+ CallsManager callsManager,
+ OutgoingCallsManager outgoingCallsManager) {
+
+ super(action, componentName);
+ mAdapter =
+ new CallServiceSelectorAdapter(callsManager, outgoingCallsManager, mCallIdMapper);
+ }
+
+ /**
+ * Creates a call-service selector for specified component and uses
+ * {@link TelecommConstants#ACTION_CALL_SERVICE_SELECTOR} as the action to bind.
*
* @param componentName The component name of the service.
+ * @param callsManager The calls manager.
+ * @param outgoingCallsManager The outgoing calls manager.
*/
- CallServiceSelectorWrapper(ComponentName componentName, CallsManager callsManager,
+ CallServiceSelectorWrapper(
+ ComponentName componentName,
+ CallsManager callsManager,
OutgoingCallsManager outgoingCallsManager) {
- super(TelecommConstants.ACTION_CALL_SERVICE_SELECTOR, componentName);
- mAdapter = new CallServiceSelectorAdapter(callsManager, outgoingCallsManager,
- mCallIdMapper);
+
+ this(TelecommConstants.ACTION_CALL_SERVICE_SELECTOR,
+ componentName,
+ callsManager,
+ outgoingCallsManager);
}
/** See {@link CallServiceSelector#setCallServiceSelectorAdapter}. */
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 3e8a42e..c82b04d 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -188,7 +188,7 @@
* @param handle Handle to connect the call with.
* @param contactInfo Information about the entity being called.
* @param gatewayInfo Optional gateway information that can be used to route the call to the
- * actual dialed handle via a gateway provider. May be null.
+ * actual dialed handle via a gateway provider. May be null.
*/
void placeOutgoingCall(Uri handle, ContactInfo contactInfo, GatewayInfo gatewayInfo) {
for (OutgoingCallValidator validator : mOutgoingCallValidators) {
@@ -207,7 +207,7 @@
Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
Log.pii(uriHandle), Log.pii(handle));
}
- Call call = new Call(uriHandle, contactInfo, gatewayInfo, false /*isIncoming*/);
+ Call call = new Call(uriHandle, contactInfo, gatewayInfo, false /* isIncoming */);
setCallState(call, CallState.DIALING);
addCall(call);
mSwitchboard.placeOutgoingCall(call);
diff --git a/src/com/android/telecomm/EmergencyCallServiceSelector.java b/src/com/android/telecomm/EmergencyCallServiceSelector.java
new file mode 100644
index 0000000..d5f2774
--- /dev/null
+++ b/src/com/android/telecomm/EmergencyCallServiceSelector.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm;
+
+import android.net.Uri;
+import android.telecomm.CallInfo;
+import android.telecomm.CallServiceDescriptor;
+import android.telecomm.CallServiceSelector;
+import android.telecomm.CallServiceSelectorAdapter;
+import android.telephony.PhoneNumberUtils;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Selects call-services which can place emergency calls. For emergency handles, this selector is
+ * always the first selector attempted when placing a call, see
+ * {@link Switchboard#processNewOutgoingCall}. If this is ever invoked for a non-emergency handle,
+ * this selector returns zero call services so that call-service selection continues to the next
+ * selector. For emergency calls, it selects telephony's PSTN call services so that they are the
+ * first ones to try to place the call.
+ */
+public class EmergencyCallServiceSelector extends CallServiceSelector {
+ CallServiceSelectorAdapter mAdapter = null;
+
+ /**
+ * Returns true if the handle passed in is to a potential emergency number.
+ */
+ static boolean shouldUseSelector(Uri handle) {
+ return PhoneNumberUtils.isPotentialLocalEmergencyNumber(
+ handle.getSchemeSpecificPart(), TelecommApp.getInstance());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void setCallServiceSelectorAdapter(CallServiceSelectorAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void select(CallInfo callInfo, List<CallServiceDescriptor> descriptors) {
+ List<CallServiceDescriptor> selectedDescriptors = Lists.newArrayList();
+
+ // We check to see if the handle is potentially for an emergency call. *Potentially* means
+ // that we match both the full number and prefix (e.g., "911444" matches 911). After the
+ // call is made, the telephony call services will inform us of the actual number that was
+ // connected, which would be 911. This is why we check *potential* APIs before, but use the
+ // exact {@link PhoneNumberUtils#isLocalEmergencyNumber} once the call is connected.
+ if (shouldUseSelector(callInfo.getHandle())) {
+ // Search for and return the pstn call services if found.
+ for (CallServiceDescriptor descriptor : descriptors) {
+ // TODO(santoscordon): Consider adding some type of CAN_MAKE_EMERGENCY_CALLS
+ // capability for call services and relying on that. Also consider combining this
+ // with a permission so that we can check that instead of relying on hardcoded
+ // paths.
+ if (TelephonyUtil.isPstnCallService(descriptor)) {
+ selectedDescriptors.add(descriptor);
+ }
+ }
+ }
+
+ if (mAdapter != null) {
+ mAdapter.setSelectedCallServices(callInfo.getId(), selectedDescriptors);
+ }
+ }
+}
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index 77c3c16..ccb6b80 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -16,12 +16,7 @@
package com.android.telecomm;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
+import android.content.ComponentName;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -29,18 +24,21 @@
import android.telecomm.CallServiceDescriptor;
import android.telecomm.TelecommConstants;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
/**
* Switchboard is responsible for:
- * <ul>
- * <li> gathering the {@link CallServiceWrapper}s and {@link CallServiceSelectorWrapper}s through which to place
- * outgoing calls
- * <li> starting outgoing calls (via {@link OutgoingCallsManager}
- * <li> switching active calls between call services.
- * </ul>
+ * - gathering the {@link CallServiceWrapper}s and {@link CallServiceSelectorWrapper}s through
+ * which to place outgoing calls
+ * - starting outgoing calls (via {@link OutgoingCallsManager}
+ * - switching active calls between call services.
*/
final class Switchboard {
@@ -149,7 +147,7 @@
* @param call The call object.
* @param descriptor The relevant call-service descriptor.
* @param extras The optional extras passed via
- * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}
+ * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}
*/
void retrieveIncomingCall(Call call, CallServiceDescriptor descriptor, Bundle extras) {
Log.d(this, "retrieveIncomingCall");
@@ -295,7 +293,33 @@
* @param call The call to place.
*/
private void processNewOutgoingCall(Call call) {
- mOutgoingCallsManager.placeCall(call, mCallServices, mSelectors);
+ Collection<CallServiceSelectorWrapper> selectors = mSelectors;
+
+ boolean useEmergencySelector =
+ EmergencyCallServiceSelector.shouldUseSelector(call.getHandle());
+ Log.d(this, "processNewOutgoingCall, isEmergency=%b", useEmergencySelector);
+
+ if (useEmergencySelector) {
+ // This is potentially an emergency call so add the emergency selector before the
+ // other selectors.
+ ImmutableList.Builder<CallServiceSelectorWrapper> selectorsBuilder =
+ ImmutableList.builder();
+
+ ComponentName componentName = new ComponentName(
+ TelecommApp.getInstance(), EmergencyCallServiceSelector.class);
+ CallServiceSelectorWrapper emergencySelector =
+ new CallServiceSelectorWrapper(
+ componentName.flattenToShortString(),
+ componentName,
+ mCallsManager,
+ mOutgoingCallsManager);
+
+ selectorsBuilder.add(emergencySelector);
+ selectorsBuilder.addAll(mSelectors);
+ selectors = selectorsBuilder.build();
+ }
+
+ mOutgoingCallsManager.placeCall(call, mCallServices, selectors);
}
/**
diff --git a/src/com/android/telecomm/TelephonyUtil.java b/src/com/android/telecomm/TelephonyUtil.java
index 7a9ac22..be501f0 100644
--- a/src/com/android/telecomm/TelephonyUtil.java
+++ b/src/com/android/telecomm/TelephonyUtil.java
@@ -43,6 +43,17 @@
return selector.getComponentName().getPackageName().equals(TELEPHONY_PACKAGE_NAME);
}
+ static boolean isPstnCallService(CallServiceDescriptor descriptor) {
+ ComponentName componentName = descriptor.getServiceComponent();
+ if (TELEPHONY_PACKAGE_NAME.equals(componentName.getPackageName())) {
+ String className = componentName.getClassName();
+ return GSM_CALL_SERVICE_CLASS_NAME.equals(className) ||
+ CDMA_CALL_SERVICE_CLASS_NAME.equals(className);
+ }
+
+ return false;
+ }
+
/**
* Returns whether or not the call is currently connected as a cellular call (through the
* device's cellular radio).
@@ -57,10 +68,7 @@
if (callService == null) {
return false;
}
- CallServiceDescriptor descriptor = callService.getDescriptor();
- String className = descriptor.getServiceComponent().getClassName();
- return className.equals(GSM_CALL_SERVICE_CLASS_NAME) ||
- className.equals(CDMA_CALL_SERVICE_CLASS_NAME);
+ return isPstnCallService(callService.getDescriptor());
}
private static void verifyCallServiceExists(String serviceName) {