Refactor Telephony to new framework
Refactors Telephony to the new-style framework, where the framework classes still live
inside the Telephony package here, but we are getting ready to move them into the actual
frameworks/base directory.
Change-Id: Id93771d20f682e3ebf225e2f4225d24072dce28b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 59eb0a3..2b15c13 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -565,17 +565,17 @@
<action android:name="android.telecomm.CallServiceSelector" />
</intent-filter>
</service>
- <service android:name="com.android.services.telephony.GsmCallService">
+ <service android:name="com.android.services.telephony.GsmConnectionService">
<intent-filter>
<action android:name="android.telecomm.CallService" />
</intent-filter>
</service>
- <service android:name="com.android.services.telephony.CdmaCallService">
+ <service android:name="com.android.services.telephony.CdmaConnectionService">
<intent-filter>
<action android:name="android.telecomm.CallService" />
</intent-filter>
</service>
- <service android:name="com.android.services.telephony.SipCallService">
+ <service android:name="com.android.services.telephony.SipConnectionService">
<intent-filter>
<action android:name="android.telecomm.CallService" />
</intent-filter>
diff --git a/src/com/android/services/telecomm/Connection.java b/src/com/android/services/telecomm/Connection.java
index 1d8684a..072d97d 100644
--- a/src/com/android/services/telecomm/Connection.java
+++ b/src/com/android/services/telecomm/Connection.java
@@ -18,6 +18,8 @@
import com.google.android.collect.Sets;
+import com.android.services.telephony.Log;
+
import android.net.Uri;
import android.os.Bundle;
import android.telecomm.CallAudioState;
@@ -34,9 +36,36 @@
void onAudioStateChanged(Connection c, CallAudioState state);
void onHandleChanged(Connection c, Uri newHandle);
void onSignalChanged(Connection c, Bundle details);
+ void onDisconnected(Connection c, int cause, String message);
void onDestroyed(Connection c);
}
+ public static class ListenerBase implements Listener {
+ /** {@inheritDoc} */
+ @Override
+ public void onStateChanged(Connection c, int state) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onAudioStateChanged(Connection c, CallAudioState state) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onHandleChanged(Connection c, Uri newHandle) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onSignalChanged(Connection c, Bundle details) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDisconnected(Connection c, int cause, String message) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDestroyed(Connection c) {}
+ }
+
public final class State {
private State() {}
@@ -52,14 +81,11 @@
private int mState = State.NEW;
private CallAudioState mCallAudioState;
private Uri mHandle;
- private long mStartTime = -1L;
- private long mEndTime = -1L;
/**
* Create a new Connection.
*/
- protected Connection() {
- }
+ protected Connection() {}
/**
* @return The handle (e.g., phone number) to which this Connection
@@ -95,44 +121,26 @@
* Assign a listener to be notified of state changes.
*
* @param l A listener.
+ * @return This Connection.
*
* @hide
*/
- public final void addConnectionListener(Listener l) {
+ public final Connection addConnectionListener(Listener l) {
mListeners.add(l);
+ return this;
}
/**
* Remove a previously assigned listener that was being notified of state changes.
*
* @param l A Listener.
+ * @return This Connection.
*
* @hide
*/
- public final void removeConnectionListener(Listener l) {
+ public final Connection removeConnectionListener(Listener l) {
mListeners.remove(l);
- }
-
- /**
- * @return The system time at which this Connection transitioned into the
- * {@link State#ACTIVE} state. This value is {@code -1L}
- * if it has not been explicitly assigned.
- *
- * @hide
- */
- public final long getStartTime() {
- return mStartTime;
- }
-
- /**
- * @return The system time at which this Connection transitioned into the
- * {@link State#DISCONNECTED} state. This value is
- * {@code -1L} if it has not been explicitly assigned.
- *
- * @hide
- */
- public final long getEndTime() {
- return mEndTime;
+ return this;
}
/**
@@ -143,6 +151,7 @@
* @hide
*/
public final void playDtmfTone(char c) {
+ Log.d(this, "playDtmfTone %c", c);
onPlayDtmfTone(c);
}
@@ -152,6 +161,7 @@
* @hide
*/
public final void stopDtmfTone() {
+ Log.d(this, "stopDtmfTone");
onStopDtmfTone();
}
@@ -163,6 +173,7 @@
* @hide
*/
public final void disconnect() {
+ Log.d(this, "disconnect");
onDisconnect();
}
@@ -174,6 +185,7 @@
* @hide
*/
public final void abort() {
+ Log.d(this, "abort");
onAbort();
}
@@ -185,6 +197,7 @@
* @hide
*/
public final void hold() {
+ Log.d(this, "hold");
onHold();
}
@@ -196,6 +209,7 @@
* @hide
*/
public final void unhold() {
+ Log.d(this, "unhold");
onUnhold();
}
@@ -207,6 +221,7 @@
* @hide
*/
public final void answer() {
+ Log.d(this, "answer");
if (mState == State.RINGING) {
onAnswer();
}
@@ -220,7 +235,10 @@
* @hide
*/
public final void reject() {
- if (mState == State.RINGING) { onReject(); }
+ Log.d(this, "reject");
+ if (mState == State.RINGING) {
+ onReject();
+ }
}
/**
@@ -229,16 +247,41 @@
* @param state The new audio state.
*/
public void setAudioState(CallAudioState state) {
+ Log.d(this, "setAudioState %s", state);
onSetAudioState(state);
}
/**
- * Notifies this Connection and listeners that the {@link #getHandle()} property
- * has a new value.
+ * @param state An integer value from {@link State}.
+ * @return A string representation of the value.
+ */
+ public static String stateToString(int state) {
+ switch (state) {
+ case State.NEW:
+ return "NEW";
+ case State.RINGING:
+ return "RINGING";
+ case State.DIALING:
+ return "DIALING";
+ case State.ACTIVE:
+ return "ACTIVE";
+ case State.HOLDING:
+ return "HOLDING";
+ case State.DISCONNECTED:
+ return "DISCONNECTED";
+ default:
+ Log.wtf(Connection.class, "Unknown state %d", state);
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Sets the value of the {@link #getHandle()} property and notifies listeners.
*
* @param handle The new handle.
*/
- protected void onSetHandle(Uri handle) {
+ protected void setHandle(Uri handle) {
+ Log.d(this, "setHandle %s", handle);
// TODO: Enforce super called
mHandle = handle;
for (Listener l : mListeners) {
@@ -247,37 +290,50 @@
}
/**
- * Notifies this Connection and listeners that the {@link #getState()} property
- * has a new value.
- *
- * @param state The new state.
+ * Sets state to active (e.g., an ongoing call where two or more parties can actively
+ * communicate).
*/
- protected void onSetState(int state) {
- // TODO: Enforce super called
- this.mState = state;
- // TODO: This can also check for only VALID state transitions
- if (state == State.ACTIVE) {
- mStartTime = System.currentTimeMillis();
- }
- if (state == State.DISCONNECTED) {
- mEndTime = System.currentTimeMillis();
- }
- for (Listener l : mListeners) {
- l.onStateChanged(this, state);
- }
+ protected void setActive() {
+ setState(State.ACTIVE);
}
/**
- * Notifies this Connection and listeners that the {@link #getState()} property
- * has a new value, and specifies a reason.
- *
- * TODO: needed for disconnect cause -- consider how that will be supported
- *
- * @param state The new state.
- * @param reason The reason for the change.
+ * Sets state to ringing (e.g., an inbound ringing call).
*/
- protected void onSetState(int state, String reason) {
- // TODO: Enforce super called
+ protected void setRinging() {
+ setState(State.RINGING);
+ }
+
+ /**
+ * Sets state to dialing (e.g., dialing an outbound call).
+ */
+ protected void setDialing() {
+ setState(State.DIALING);
+ }
+
+ /**
+ * Sets state to be on hold.
+ */
+ protected void setOnHold() {
+ setState(State.HOLDING);
+ }
+
+ /**
+ * Sets state to disconnected. This will first notify listeners with an
+ * {@link Listener#onStateChanged(Connection, int)} event, then will fire an
+ * {@link Listener#onDisconnected(Connection, int, String)} event with additional
+ * details.
+ *
+ * @param cause The reason for the disconnection, any of
+ * {@link android.telephony.DisconnectCause}.
+ * @param message Optional call-service-provided message about the disconnect.
+ */
+ protected void setDisconnected(int cause, String message) {
+ setState(State.DISCONNECTED);
+ Log.d(this, "Disconnected with cause %d, message \"%s\"", cause, message);
+ for (Listener l : mListeners) {
+ l.onDisconnected(this, cause, message);
+ }
}
/**
@@ -350,4 +406,12 @@
* a request to reject.
*/
protected void onReject() {}
+
+ private void setState(int state) {
+ Log.d(this, "setState: %s", stateToString(state));
+ this.mState = state;
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, state);
+ }
+ }
}
diff --git a/src/com/android/services/telecomm/ConnectionRequest.java b/src/com/android/services/telecomm/ConnectionRequest.java
index 37db505..2027330 100644
--- a/src/com/android/services/telecomm/ConnectionRequest.java
+++ b/src/com/android/services/telecomm/ConnectionRequest.java
@@ -45,4 +45,12 @@
* and servant {@code ConnectionService} which agree on a vocabulary for such data.
*/
public Bundle getExtras() { return mExtras; }
+
+ public String toString() {
+ return String.format("PhoneConnectionRequest %s %s",
+ mHandle == null
+ ? Uri.EMPTY
+ : ConnectionService.toLogSafePhoneNumber(mHandle.toString()),
+ mExtras == null ? "" : mExtras);
+ }
}
diff --git a/src/com/android/services/telecomm/ConnectionService.java b/src/com/android/services/telecomm/ConnectionService.java
index 9a5ff22..5f2389e 100644
--- a/src/com/android/services/telecomm/ConnectionService.java
+++ b/src/com/android/services/telecomm/ConnectionService.java
@@ -28,12 +28,14 @@
import android.util.Log;
/**
- * Provides actual voice connections to the Android framework and to other processes
- * running on this device.
+ * A {@link android.app.Service} that provides telephone connections to
+ * processes running on an Android device.
*/
public abstract class ConnectionService extends CallService {
+ private static final String TAG = ConnectionService.class.getSimpleName();
- private static final String TAG = ConnectionService.class.getName();
+ // Flag controlling whether PII is emitted into the logs
+ private static final boolean PII_DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Connection NULL_CONNECTION = new Connection() {};
@@ -43,30 +45,38 @@
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@Override
public void onStateChanged(Connection c, int state) {
+ String id = mConnectionById.inverse().get(c);
+ Log.d(TAG, "Adapter set state " + id + " " + Connection.stateToString(state));
switch (state) {
case Connection.State.ACTIVE:
- getAdapter().setActive(mConnectionById.inverse().get(c));
+ getAdapter().setActive(id);
break;
case Connection.State.DIALING:
- getAdapter().setDialing(mConnectionById.inverse().get(c));
+ getAdapter().setDialing(id);
break;
case Connection.State.DISCONNECTED:
- // TODO: Disconnect cause and description
- getAdapter().setDisconnected(mConnectionById.inverse().get(c), 0, null);
+ // Handled in onDisconnected()
break;
case Connection.State.HOLDING:
- getAdapter().setOnHold(mConnectionById.inverse().get(c));
+ getAdapter().setOnHold(id);
break;
case Connection.State.NEW:
// Nothing to tell Telecomm
break;
case Connection.State.RINGING:
- getAdapter().setRinging(mConnectionById.inverse().get(c));
+ getAdapter().setRinging(id);
break;
}
}
@Override
+ public void onDisconnected(Connection c, int cause, String message) {
+ String id = mConnectionById.inverse().get(c);
+ Log.d(TAG, "Adapter set disconnected " + cause + " " + message);
+ getAdapter().setDisconnected(id, cause, message);
+ }
+
+ @Override
public void onHandleChanged(Connection c, Uri newHandle) {
// TODO: Unsupported yet
}
@@ -83,27 +93,29 @@
@Override
public void onDestroyed(Connection c) {
- c.removeConnectionListener(this);
- String id = mConnectionById.inverse().get(c);
- mConnectionById.get(id).removeConnectionListener(mConnectionListener);
- mConnectionById.remove(id);
+ removeConnection(c);
}
};
@Override
public final void isCompatibleWith(final CallInfo callInfo) {
+ Log.d(TAG, "isCompatibleWith " + callInfo);
onFindSubscriptions(
callInfo.getHandle(),
new Response<Uri, Subscription>() {
@Override
public void onResult(Uri handle, Subscription... result) {
- getAdapter().setIsCompatibleWith(callInfo.getId(), result.length > 0);
+ boolean isCompatible = result.length > 0;
+ Log.d(TAG, "adapter setIsCompatibleWith "
+ + callInfo.getId() + " " + isCompatible);
+ getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible);
}
@Override
public void onError(Uri handle, String reason) {
Log.wtf(TAG, "Error in onFindSubscriptions " + callInfo.getHandle()
+ " error: " + reason);
+ getAdapter().setIsCompatibleWith(callInfo.getId(), false);
}
}
);
@@ -111,6 +123,7 @@
@Override
public final void call(final CallInfo callInfo) {
+ Log.d(TAG, "call " + callInfo);
onCreateConnections(
new ConnectionRequest(
callInfo.getHandle(),
@@ -119,15 +132,17 @@
@Override
public void onResult(ConnectionRequest request, Connection... result) {
if (result.length != 1) {
+ Log.d(TAG, "adapter handleFailedOutgoingCall " + callInfo);
getAdapter().handleFailedOutgoingCall(
callInfo.getId(),
"Created " + result.length + " Connections, expected 1");
- for (int i = 0; i < result.length; i++) {
- result[i].abort();
+ for (Connection c : result) {
+ c.abort();
}
} else {
- mConnectionById.put(callInfo.getId(), result[0]);
- result[0].addConnectionListener(mConnectionListener);
+ addConnection(callInfo.getId(), result[0]);
+ Log.d(TAG, "adapter handleSuccessfulOutgoingCall "
+ + callInfo.getId());
getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
}
}
@@ -142,37 +157,44 @@
@Override
public final void abort(String callId) {
+ Log.d(TAG, "abort " + callId);
findConnectionForAction(callId, "abort").abort();
}
@Override
public final void setIncomingCallId(final String callId, Bundle extras) {
+ Log.d(TAG, "setIncomingCallId " + callId + " " + extras);
onCreateIncomingConnection(
new ConnectionRequest(
- null /* todo getHandle() */,
+ null, // TODO: Can we obtain this from "extras"?
extras),
new Response<ConnectionRequest, Connection>() {
@Override
public void onResult(ConnectionRequest request, Connection... result) {
if (result.length != 1) {
+ Log.d(TAG, "adapter handleFailedOutgoingCall " + callId);
getAdapter().handleFailedOutgoingCall(
callId,
"Created " + result.length + " Connections, expected 1");
- for (int i = 0; i < result.length; i++) {
- result[i].abort();
+ for (Connection c : result) {
+ c.abort();
}
} else {
- mConnectionById.put(callId, result[0]);
+ addConnection(callId, result[0]);
+ Log.d(TAG, "adapter notifyIncomingCall " + callId);
+ // TODO: Uri.EMPTY is because CallInfo crashes when Parceled with a
+ // null URI ... need to fix that at its cause!
getAdapter().notifyIncomingCall(new CallInfo(
callId,
- CallState.NEW /* TODO ? */,
- null /* TODO handle */));
+ connectionStateToCallState(result[0].getState()),
+ request.getHandle() /* result[0].getHandle() == null
+ ? Uri.EMPTY : result[0].getHandle() */));
}
}
@Override
public void onError(ConnectionRequest request, String reason) {
- getAdapter().handleFailedOutgoingCall(callId, reason);
+ Log.d(TAG, "adapter failed setIncomingCallId " + request + " " + reason);
}
}
);
@@ -180,48 +202,56 @@
@Override
public final void answer(String callId) {
+ Log.d(TAG, "answer " + callId);
findConnectionForAction(callId, "answer").answer();
}
@Override
public final void reject(String callId) {
+ Log.d(TAG, "reject " + callId);
findConnectionForAction(callId, "reject").reject();
}
@Override
public final void disconnect(String callId) {
+ Log.d(TAG, "disconnect " + callId);
findConnectionForAction(callId, "disconnect").disconnect();
}
@Override
public final void hold(String callId) {
+ Log.d(TAG, "hold " + callId);
findConnectionForAction(callId, "hold").hold();
}
@Override
public final void unhold(String callId) {
+ Log.d(TAG, "unhold " + callId);
findConnectionForAction(callId, "unhold").unhold();
}
@Override
public final void playDtmfTone(String callId, char digit) {
+ Log.d(TAG, "playDtmfTone " + callId + " " + Character.toString(digit));
findConnectionForAction(callId, "playDtmfTone").playDtmfTone(digit);
}
@Override
public final void stopDtmfTone(String callId) {
+ Log.d(TAG, "stopDtmfTone " + callId);
findConnectionForAction(callId, "stopDtmfTone").stopDtmfTone();
}
@Override
public final void onAudioStateChanged(String callId, CallAudioState audioState) {
+ Log.d(TAG, "onAudioStateChanged " + callId + " " + audioState);
findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
}
/**
- * Find a set of Subscriptions matching a given handle (phone number).
+ * Find a set of Subscriptions matching a given handle (e.g. phone number).
*
- * @param handle A phone number.
+ * @param handle A handle (e.g. phone number) with which to connect.
* @param callback A callback for providing the result.
*/
public void onFindSubscriptions(
@@ -231,7 +261,7 @@
/**
* Create a Connection given a request.
*
- * @param request Some data encapsulating details of the desired Connection.
+ * @param request Data encapsulating details of the desired Connection.
* @param callback A callback for providing the result.
*/
public void onCreateConnections(
@@ -241,14 +271,69 @@
/**
* Create a Connection to match an incoming connection notification.
*
- * @param request Some data encapsulating details of the desired Connection.
+ * @param request Data encapsulating details of the desired Connection.
* @param callback A callback for providing the result.
*/
public void onCreateIncomingConnection(
ConnectionRequest request,
Response<ConnectionRequest, Connection> callback) {}
- private final Connection findConnectionForAction(String callId, String action) {
+ static String toLogSafePhoneNumber(String number) {
+ // For unknown number, log empty string.
+ if (number == null) {
+ return "";
+ }
+
+ if (PII_DEBUG) {
+ // When PII_DEBUG is true we emit PII.
+ return number;
+ }
+
+ // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
+ // sanitized phone numbers.
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number.length(); i++) {
+ char c = number.charAt(i);
+ if (c == '-' || c == '@' || c == '.') {
+ builder.append(c);
+ } else {
+ builder.append('x');
+ }
+ }
+ return builder.toString();
+ }
+
+ private CallState connectionStateToCallState(int connectionState) {
+ switch (connectionState) {
+ case Connection.State.NEW:
+ return CallState.NEW;
+ case Connection.State.RINGING:
+ return CallState.RINGING;
+ case Connection.State.DIALING:
+ return CallState.DIALING;
+ case Connection.State.ACTIVE:
+ return CallState.ACTIVE;
+ case Connection.State.HOLDING:
+ return CallState.ON_HOLD;
+ case Connection.State.DISCONNECTED:
+ return CallState.DISCONNECTED;
+ default:
+ Log.wtf(TAG, "Unknown PhoneConnection.State " + connectionState);
+ return CallState.NEW;
+ }
+ }
+
+ private void addConnection(String callId, Connection connection) {
+ mConnectionById.put(callId, connection);
+ connection.addConnectionListener(mConnectionListener);
+ }
+
+ private void removeConnection(Connection connection) {
+ connection.removeConnectionListener(mConnectionListener);
+ mConnectionById.inverse().remove(connection);
+ }
+
+ private Connection findConnectionForAction(String callId, String action) {
if (mConnectionById.containsKey(callId)) {
return mConnectionById.get(callId);
}
diff --git a/src/com/android/services/telecomm/Subscription.java b/src/com/android/services/telecomm/Subscription.java
index 2456ce6..1cf02f1 100644
--- a/src/com/android/services/telecomm/Subscription.java
+++ b/src/com/android/services/telecomm/Subscription.java
@@ -19,8 +19,14 @@
import android.os.Parcel;
import android.os.Parcelable;
+/**
+ * Represents a distinct subscription, line of service or call placement method that
+ * a {@link ConnectionService} can use to place phone calls.
+ */
public class Subscription implements Parcelable {
+ public Subscription() {}
+
public int describeContents() {
return 0;
}
diff --git a/src/com/android/services/telephony/BaseTelephonyCallService.java b/src/com/android/services/telephony/BaseTelephonyCallService.java
deleted file mode 100644
index 9cb4661..0000000
--- a/src/com/android/services/telephony/BaseTelephonyCallService.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 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.services.telephony;
-
-import android.net.Uri;
-import android.telecomm.CallAudioState;
-import android.telecomm.CallInfo;
-import android.telecomm.CallService;
-import android.telecomm.CallServiceAdapter;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.Phone;
-
-/**
- * The parent class for telephony-based call services. Subclasses provide the specific phone (GSM,
- * CDMA, etc...) to use.
- */
-public abstract class BaseTelephonyCallService extends CallService {
- /** {@inheritDoc} */
- @Override
- public void abort(String callId) {
- TelephonyCallConnection callConnection = CallRegistrar.get(callId);
- if (callConnection != null) {
- callConnection.disconnect(true);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void disconnect(String callId) {
- TelephonyCallConnection callConnection = CallRegistrar.get(callId);
- if (callConnection != null) {
- callConnection.disconnect(false);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void hold(String callId) {
- Log.d(this, "Attempting to put call on hold: %s", callId);
- TelephonyCallConnection callConnection = CallRegistrar.get(callId);
- if (callConnection != null) {
- callConnection.hold();
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void unhold(String callId) {
- Log.d(this, "Attempting to release call from hold: %s", callId);
- TelephonyCallConnection callConnection = CallRegistrar.get(callId);
- if (callConnection != null) {
- callConnection.unhold();
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void onAudioStateChanged(String activeCallId, CallAudioState audioState) {
- TelephonyCallConnection callConnection = CallRegistrar.get(activeCallId);
- if (callConnection != null) {
- callConnection.onAudioStateChanged(audioState);
- }
- }
-
- /**
- * Initiates the call, should be called by the subclass.
- */
- protected void startCallWithPhone(Phone phone, CallInfo callInfo) {
- String callId = callInfo.getId();
- if (phone == null) {
- getAdapter().handleFailedOutgoingCall(callId, "Phone is null");
- return;
- }
-
- if (callInfo.getHandle() == null) {
- getAdapter().handleFailedOutgoingCall(callInfo.getId(), "Handle is null");
- return;
- }
-
- String number = callInfo.getHandle().getSchemeSpecificPart();
- if (TextUtils.isEmpty(number)) {
- getAdapter().handleFailedOutgoingCall(callId, "Unable to parse number");
- return;
- }
-
- Connection connection;
- try {
- connection = phone.dial(number);
- } catch (CallStateException e) {
- Log.e(this, e, "Call to Phone.dial failed with exception");
- getAdapter().handleFailedOutgoingCall(callId, e.getMessage());
- return;
- }
-
- if (connection == null) {
- getAdapter().handleFailedOutgoingCall(callId, "Call to phone.dial failed");
- return;
- }
-
- TelephonyCallConnection callConnection =
- new TelephonyCallConnection(getAdapter(), callId, connection);
- CallRegistrar.register(callId, callConnection);
-
- getAdapter().handleSuccessfulOutgoingCall(callId);
- }
-}
diff --git a/src/com/android/services/telephony/CallRegistrar.java b/src/com/android/services/telephony/CallRegistrar.java
deleted file mode 100644
index a1a41c2..0000000
--- a/src/com/android/services/telephony/CallRegistrar.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 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.services.telephony;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.android.internal.telephony.Connection;
-
-import java.util.Collection;
-import java.util.HashMap;
-
-/**
- * Maintains global associations between a Telecomm-supplied call ID and a
- * {@link TelephonyCallConnection} object.
- */
-final class CallRegistrar {
- private static final String TAG = CallRegistrar.class.getSimpleName();
-
- /** Map of all call connections keyed by the call ID. */
- private static HashMap<String, TelephonyCallConnection> sCallConnections = Maps.newHashMap();
-
- /**
- * Registers the specified call ID with the specified call connection.
- *
- * @param callId The call ID from Telecomm.
- * @param callConnection The call connection.
- */
- static void register(String callId, TelephonyCallConnection callConnection) {
- Preconditions.checkNotNull(callId);
- Preconditions.checkNotNull(callConnection);
-
- if (sCallConnections.containsKey(callId)) {
- Log.wtf(TAG, "Reregistering the call: %s", callId);
- } else {
- sCallConnections.put(callId, callConnection);
- }
- }
-
- /**
- * Unregisters the specified call ID.
- *
- * @param callId The call ID from Telecomm.
- */
- static void unregister(String callId) {
- Preconditions.checkNotNull(callId);
- sCallConnections.remove(callId);
- }
-
- /**
- * Returns true if the specified connection has already been registered with a call ID.
- *
- * @param connection The connection to test.
- */
- static boolean isConnectionRegistered(Connection connection) {
- for (TelephonyCallConnection callConnection : CallRegistrar.getCallConnections()) {
- if (callConnection.getOriginalConnection() == connection) {
- return true;
- }
- }
- return false;
- }
-
- static TelephonyCallConnection get(String callId) {
- return sCallConnections.get(callId);
- }
-
- static boolean isEmpty() {
- return sCallConnections.isEmpty();
- }
-
- /**
- * Returns all the registered call connections as a collection.
- */
- static Collection<TelephonyCallConnection> getCallConnections() {
- return sCallConnections.values();
- }
-}
diff --git a/src/com/android/services/telephony/CdmaCallService.java b/src/com/android/services/telephony/CdmaCallService.java
deleted file mode 100644
index 6ddfeee..0000000
--- a/src/com/android/services/telephony/CdmaCallService.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 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.services.telephony;
-
-import android.content.Context;
-import android.telecomm.CallInfo;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
-
-/**
- * Call service that uses the CDMA phone.
- */
-public class CdmaCallService extends PstnCallService {
- static boolean shouldSelect(Context context, CallInfo callInfo) {
- TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- return telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
- }
-
- /** {@inheritDoc} */
- @Override
- public void isCompatibleWith(CallInfo callInfo) {
- getAdapter().setIsCompatibleWith(callInfo.getId(), shouldSelect(this, callInfo));
- }
-
- /** {@inheritDoc} */
- @Override
- protected Phone getPhone() {
- return CachedPhoneFactory.getCdmaPhone();
- }
-
- /** {@inheritDoc} */
- @Override
- public void playDtmfTone(String callId, char digit) {
- // TODO(santoscordon): There are conditions where we should play dtmf tones with different
- // timeouts.
- // TODO(santoscordon): We get explicit response from the phone via a Message when the burst
- // tone has completed. During this time we can get subsequent requests. We need to stop
- // passing in null as the message and start handling it to implement a queue.
- getPhone().sendBurstDtmf(Character.toString(digit), 0, 0, null);
- }
-
- /** {@inheritDoc} */
- @Override
- public void stopDtmfTone(String callId) {
- // no-op, we only play timed dtmf tones for cdma.
- }
-}
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
new file mode 100644
index 0000000..f46e64f
--- /dev/null
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+
+/**
+ * Manages a single phone call handled by CDMA.
+ */
+public class CdmaConnection extends PstnConnection {
+
+ public CdmaConnection(Phone phone, Connection connection) {
+ super(phone, connection);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onPlayDtmfTone(char digit) {
+ // TODO(santoscordon): There are conditions where we should play dtmf tones with different
+ // timeouts.
+ // TODO(santoscordon): We get explicit response from the phone via a Message when the burst
+ // tone has completed. During this time we can get subsequent requests. We need to stop
+ // passing in null as the message and start handling it to implement a queue.
+ getPhone().sendBurstDtmf(Character.toString(digit), 0, 0, null);
+ super.onPlayDtmfTone(digit);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onStopDtmfTone() {
+ // no-op, we only play timed dtmf tones for cdma.
+ super.onStopDtmfTone();
+ }
+}
diff --git a/src/com/android/services/telephony/CdmaConnectionService.java b/src/com/android/services/telephony/CdmaConnectionService.java
new file mode 100644
index 0000000..06add1b
--- /dev/null
+++ b/src/com/android/services/telephony/CdmaConnectionService.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+import com.android.phone.Constants;
+import com.android.services.telecomm.ConnectionRequest;
+
+/**
+ * Connection service that uses CDMA.
+ */
+public class CdmaConnectionService extends PstnConnectionService {
+
+ /** {@inheritDoc} */
+ @Override
+ protected Phone getPhone() {
+ return CachedPhoneFactory.getCdmaPhone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean canCall(Uri handle) {
+ return canCall(this, handle);
+ }
+
+ // TODO: Refactor this out when CallServiceSelector is deprecated
+ /* package */ static boolean canCall(Context context, Uri handle) {
+ TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ return telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA
+ && Constants.SCHEME_TEL.equals(handle.getScheme());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected TelephonyConnection onCreateTelephonyConnection(
+ ConnectionRequest request, Connection connection) {
+ return new CdmaConnection(getPhone(), connection);
+ }
+}
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
new file mode 100644
index 0000000..50ef154
--- /dev/null
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+
+/**
+ * Manages a single phone call handled by GSM.
+ */
+public class GsmConnection extends PstnConnection {
+
+ public GsmConnection(Phone phone, Connection connection) {
+ super(phone, connection);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onPlayDtmfTone(char digit) {
+ getPhone().startDtmf(digit);
+ super.onPlayDtmfTone(digit);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onStopDtmfTone() {
+ getPhone().stopDtmf();
+ super.onStopDtmfTone();
+ }
+}
diff --git a/src/com/android/services/telephony/GsmCallService.java b/src/com/android/services/telephony/GsmConnectionService.java
similarity index 61%
rename from src/com/android/services/telephony/GsmCallService.java
rename to src/com/android/services/telephony/GsmConnectionService.java
index 7470405..bf4fe7d 100644
--- a/src/com/android/services/telephony/GsmCallService.java
+++ b/src/com/android/services/telephony/GsmConnectionService.java
@@ -17,28 +17,18 @@
package com.android.services.telephony;
import android.content.Context;
-import android.telecomm.CallInfo;
+import android.net.Uri;
import android.telephony.TelephonyManager;
+import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.Constants;
+import com.android.services.telecomm.ConnectionRequest;
/**
- * Call service that uses the GSM phone.
+ * Connnection service that uses GSM.
*/
-public class GsmCallService extends PstnCallService {
- static boolean shouldSelect(Context context, CallInfo callInfo) {
- TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- return telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM;
- }
-
- /** {@inheritDoc} */
- @Override
- public void isCompatibleWith(CallInfo callInfo) {
- getAdapter().setIsCompatibleWith(callInfo.getId(), shouldSelect(this, callInfo));
- }
-
+public class GsmConnectionService extends PstnConnectionService {
/** {@inheritDoc} */
@Override
protected Phone getPhone() {
@@ -47,13 +37,22 @@
/** {@inheritDoc} */
@Override
- public void playDtmfTone(String callId, char digit) {
- getPhone().startDtmf(digit);
+ protected boolean canCall(Uri handle) {
+ return canCall(this, handle);
+ }
+
+ // TODO: Refactor this out when CallServiceSelector is deprecated
+ /* package */ static boolean canCall(Context context, Uri handle) {
+ TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ return telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM
+ && Constants.SCHEME_TEL.equals(handle.getScheme());
}
/** {@inheritDoc} */
@Override
- public void stopDtmfTone(String callId) {
- getPhone().stopDtmf();
+ protected TelephonyConnection onCreateTelephonyConnection(
+ ConnectionRequest request, Connection connection) {
+ return new GsmConnection(getPhone(), connection);
}
}
diff --git a/src/com/android/services/telephony/PstnCallService.java b/src/com/android/services/telephony/PstnCallService.java
deleted file mode 100644
index 8a458f7..0000000
--- a/src/com/android/services/telephony/PstnCallService.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 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.services.telephony;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.telecomm.CallInfo;
-import android.telecomm.CallState;
-
-import android.telephony.PhoneNumberUtils;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.Phone;
-import com.android.phone.Constants;
-import com.google.android.collect.Sets;
-
-import java.util.Set;
-
-/**
- * The parent class for PSTN-based call services. Handles shared functionality between all PSTN
- * call services.
- */
-public abstract class PstnCallService extends BaseTelephonyCallService {
- private EmergencyCallHelper mEmergencyCallHelper;
- private Set<String> mPendingOutgoingEmergencyCalls = Sets.newHashSet();
-
- @Override
- public void onCreate() {
- super.onCreate();
- mEmergencyCallHelper = new EmergencyCallHelper(this);
- }
-
- /** {@inheritDoc} */
- @Override
- public final void call(final CallInfo callInfo) {
- // TODO: Consider passing call emergency information as part of CallInfo so that we dont
- // have to make the check here once again.
- String handle = callInfo.getHandle().getSchemeSpecificPart();
- final Phone phone = getPhone();
- if (PhoneNumberUtils.isPotentialEmergencyNumber(handle)) {
- final String callId = callInfo.getId();
-
- EmergencyCallHelper.Callback callback = new EmergencyCallHelper.Callback() {
- @Override
- public void onComplete(boolean isRadioReady) {
- if (mPendingOutgoingEmergencyCalls.remove(callId)) {
- // The emergency call was still pending (not aborted) so continue with the
- // rest of the logic.
-
- if (isRadioReady) {
- startCallWithPhone(phone, callInfo);
- } else {
- getAdapter().handleFailedOutgoingCall(
- callInfo.getId(), "Failed to turn on radio.");
- }
- }
- }
- };
-
- mPendingOutgoingEmergencyCalls.add(callId);
-
- // If the radio is already on, this will call us back fairly quickly.
- mEmergencyCallHelper.startTurnOnRadioSequence(phone, callback);
- } else {
- startCallWithPhone(phone, callInfo);
- }
- }
-
- /**
- * Looks for a new incoming call and if one is found, tells Telecomm to associate the incoming
- * call with the specified call ID.
- *
- * {@inheritDoc}
- */
- @Override
- public final void setIncomingCallId(String callId, Bundle extras) {
- Log.d(this, "setIncomingCallId: %s", callId);
- Phone phone = getPhone();
- Call call = getPhone().getRingingCall();
-
- // The ringing call is always not-null, check if it is truly ringing by checking its state.
- if (call.getState().isRinging()) {
- Connection connection = call.getEarliestConnection();
-
- if (CallRegistrar.isConnectionRegistered(connection)) {
- Log.w(this, "Cannot set incoming call ID, ringing connection already registered.");
- } else {
- // Create and register a new call connection.
- TelephonyCallConnection callConnection =
- new TelephonyCallConnection(getAdapter(), callId, connection);
- CallRegistrar.register(callId, callConnection);
-
- // Address can be null for blocked calls.
- String address = connection.getAddress();
- if (address == null) {
- address = "";
- }
-
- // Notify Telecomm of the incoming call.
- Uri handle = Uri.fromParts(Constants.SCHEME_TEL, address, null);
- CallInfo callInfo = new CallInfo(callId, CallState.RINGING, handle);
- getAdapter().notifyIncomingCall(callInfo);
- }
- } else {
- Log.w(this, "Found no ringing call, call state: %s", call.getState());
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void answer(String callId) {
- // TODO(santoscordon): Tons of hairy logic is missing here around multiple active calls on
- // CDMA devices. See {@link CallManager.acceptCall}.
-
- Log.i(this, "answer: %s", callId);
- if (isValidRingingCall(callId)) {
- try {
- getPhone().acceptCall();
- } catch (CallStateException e) {
- Log.e(this, e, "Failed to accept call: %s", callId);
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void reject(String callId) {
- Log.i(this, "reject: %s", callId);
- if (isValidRingingCall(callId)) {
- try {
- getPhone().rejectCall();
- } catch (CallStateException e) {
- Log.e(this, e, "Failed to reject call: %s", callId);
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void abort(String callId) {
- mPendingOutgoingEmergencyCalls.remove(callId);
- super.abort(callId);
- }
-
- /**
- * @return The current phone object behind this call service.
- */
- protected abstract Phone getPhone();
-
- /**
- * Checks to see if the specified call ID corresponds to an active incoming call. Returns false
- * if there is no association between the specified call ID and an actual call, or if the
- * associated call is not incoming (See {@link Call.State#isRinging}).
- *
- * @param callId The ID of the call.
- */
- private boolean isValidRingingCall(String callId) {
- TelephonyCallConnection callConnection = CallRegistrar.get(callId);
-
- if (callConnection == null) {
- Log.d(this, "Unknown call ID while testing for a ringing call.");
- } else {
- Phone phone = getPhone();
- Call ringingCall = phone.getRingingCall();
-
- // The ringingCall object is always not-null so we have to check its current state.
- if (ringingCall.getState().isRinging()) {
- Connection connection = callConnection.getOriginalConnection();
- if (ringingCall.getEarliestConnection() == connection) {
- // The ringing connection is the same one for this call. We have a match!
- return true;
- } else {
- Log.w(this, "A ringing connection exists, but it is not the same connection.");
- }
- } else {
- Log.i(this, "There is no longer a ringing call.");
- }
- }
-
- return false;
- }
-}
diff --git a/src/com/android/services/telephony/PstnConnection.java b/src/com/android/services/telephony/PstnConnection.java
new file mode 100644
index 0000000..8157fca
--- /dev/null
+++ b/src/com/android/services/telephony/PstnConnection.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+
+/**
+ * Manages a single phone call handled by the PSTN infrastructure.
+ */
+public abstract class PstnConnection extends TelephonyConnection {
+
+ private final Phone mPhone;
+
+ public PstnConnection(Phone phone, Connection connection) {
+ super(connection);
+ mPhone = phone;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onAnswer() {
+ // TODO(santoscordon): Tons of hairy logic is missing here around multiple active calls on
+ // CDMA devices. See {@link CallManager.acceptCall}.
+
+ Log.i(this, "Answer call.");
+ if (isValidRingingCall(getOriginalConnection())) {
+ try {
+ mPhone.acceptCall();
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to accept call.");
+ }
+ }
+ super.onAnswer();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onReject() {
+ Log.i(this, "Reject call.");
+ if (isValidRingingCall(getOriginalConnection())) {
+ try {
+ mPhone.rejectCall();
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to reject call.");
+ }
+ }
+ super.onReject();
+ }
+
+ protected Phone getPhone() {
+ return mPhone;
+ }
+
+ /**
+ * Checks to see if the specified low-level Telephony {@link Connection} corresponds to an
+ * active incoming call. Returns false if there is no such actual call, or if the
+ * associated call is not incoming (See {@link Call.State#isRinging}).
+ *
+ * @param connection The connection to ask about.
+ */
+ private boolean isValidRingingCall(Connection connection) {
+ Call ringingCall = mPhone.getRingingCall();
+
+ if (ringingCall.getState().isRinging()) {
+ // The ringingCall object is always not-null so we have to check its current state.
+ if (ringingCall.getEarliestConnection() == connection) {
+ // The ringing connection is the same one for this call. We have a match!
+ return true;
+ } else {
+ Log.w(this, "A ringing connection exists, but it is not the same connection.");
+ }
+ } else {
+ Log.i(this, "There is no longer a ringing call.");
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/services/telephony/PstnConnectionService.java b/src/com/android/services/telephony/PstnConnectionService.java
new file mode 100644
index 0000000..fc59bda
--- /dev/null
+++ b/src/com/android/services/telephony/PstnConnectionService.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import android.net.Uri;
+
+import android.telephony.PhoneNumberUtils;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Phone;
+import com.android.phone.Constants;
+import com.android.services.telecomm.Connection;
+import com.android.services.telecomm.ConnectionRequest;
+import com.android.services.telecomm.Response;
+
+import com.google.android.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * The parent class for PSTN-based call services. Handles shared functionality between all PSTN
+ * call services.
+ */
+public abstract class PstnConnectionService extends TelephonyConnectionService {
+ private EmergencyCallHelper mEmergencyCallHelper;
+ private final Set<ConnectionRequest> mPendingOutgoingEmergencyCalls = Sets.newHashSet();
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mEmergencyCallHelper = new EmergencyCallHelper(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onCreateConnections(
+ final ConnectionRequest request,
+ final Response<ConnectionRequest, Connection> response) {
+ // TODO: Consider passing call emergency information as part of ConnectionRequest so
+ // that we do not have to make the check here once again.
+ String handle = request.getHandle().getSchemeSpecificPart();
+ final Phone phone = getPhone();
+ if (PhoneNumberUtils.isPotentialEmergencyNumber(handle)) {
+ EmergencyCallHelper.Callback callback = new EmergencyCallHelper.Callback() {
+ @Override
+ public void onComplete(boolean isRadioReady) {
+ if (mPendingOutgoingEmergencyCalls.remove(request)) {
+ // The emergency call was still pending (not aborted) so continue with the
+ // rest of the logic.
+
+ if (isRadioReady) {
+ startCallWithPhone(phone, request, response);
+ } else {
+ responseError(request, response, "Failed to turn on radio.");
+ }
+ }
+ }
+ };
+
+ mPendingOutgoingEmergencyCalls.add(request);
+
+ // If the radio is already on, this will call us back fairly quickly.
+ mEmergencyCallHelper.startTurnOnRadioSequence(phone, callback);
+ } else {
+ startCallWithPhone(phone, request, response);
+ }
+ super.onCreateConnections(request, response);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onCreateIncomingConnection(
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> response) {
+ Log.d(this, "onCreateIncomingConnection");
+ Call call = getPhone().getRingingCall();
+
+ // The ringing call is always not-null, check if it is truly ringing by checking its state.
+ if (call.getState().isRinging()) {
+ com.android.internal.telephony.Connection connection = call.getEarliestConnection();
+
+ if (isConnectionKnown(connection)) {
+ responseError(
+ request,
+ response,
+ "Cannot set incoming call ID, ringing connection already registered.");
+ } else {
+ // Address can be null for blocked calls.
+ String address = connection.getAddress();
+ if (address == null) {
+ address = "";
+ }
+
+ Uri handle = Uri.fromParts(Constants.SCHEME_TEL, address, null);
+
+ TelephonyConnection telephonyConnection;
+ try {
+ telephonyConnection = createTelephonyConnection(request, connection);
+ } catch (Exception e) {
+ responseError(request, response, e.getMessage());
+ return;
+ }
+
+ responseResult(
+ new ConnectionRequest(handle, request.getExtras()),
+ response,
+ telephonyConnection);
+ }
+ } else {
+ responseError(
+ request,
+ response,
+ String.format("Found no ringing call, call state: %s", call.getState()));
+ }
+ super.onCreateIncomingConnection(request, response);
+ }
+
+ /**
+ * @return The current phone object behind this call service.
+ */
+ protected abstract Phone getPhone();
+}
diff --git a/src/com/android/services/telephony/SipConnection.java b/src/com/android/services/telephony/SipConnection.java
new file mode 100644
index 0000000..0545613
--- /dev/null
+++ b/src/com/android/services/telephony/SipConnection.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import com.android.services.telecomm.Connection;
+
+/**
+ * A {@link Connection} object for SIP calls.
+ */
+public class SipConnection extends TelephonyConnection {
+
+ public SipConnection(com.android.internal.telephony.Connection connection) {
+ super(connection);
+ }
+
+ // TODO: Fill in the below methods
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPlayDtmfTone(char c) {
+ super.onPlayDtmfTone(c);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onStopDtmfTone() {
+ super.onStopDtmfTone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onDisconnect() {
+ super.onDisconnect();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onAbort() {
+ super.onAbort();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onHold() {
+ super.onHold();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onUnhold() {
+ super.onUnhold();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onAnswer() {
+ super.onAnswer();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onReject() {
+ super.onReject();
+ }
+}
diff --git a/src/com/android/services/telephony/SipCallService.java b/src/com/android/services/telephony/SipConnectionService.java
similarity index 69%
rename from src/com/android/services/telephony/SipCallService.java
rename to src/com/android/services/telephony/SipConnectionService.java
index 9d87655..8a61acf 100644
--- a/src/com/android/services/telephony/SipCallService.java
+++ b/src/com/android/services/telephony/SipConnectionService.java
@@ -22,30 +22,62 @@
import android.net.sip.SipProfile;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Bundle;
import android.provider.Settings;
-import android.telecomm.CallInfo;
import android.telephony.PhoneNumberUtils;
-import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.sip.SipPhone;
import com.android.phone.Constants;
import com.android.phone.PhoneUtils;
import com.android.phone.sip.SipProfileDb;
import com.android.phone.sip.SipSharedPreferences;
+import com.android.services.telecomm.Connection;
+import com.android.services.telecomm.ConnectionRequest;
+import com.android.services.telecomm.Response;
import java.util.HashMap;
/**
* Call service that uses the SIP phone.
*/
-public class SipCallService extends BaseTelephonyCallService {
+public class SipConnectionService extends TelephonyConnectionService {
private static HashMap<String, SipPhone> sSipPhones = new HashMap<String, SipPhone>();
- static boolean shouldSelect(Context context, CallInfo callInfo) {
- Uri uri = callInfo.getHandle();
- return shouldUseSipPhone(context, uri.getScheme(), uri.getSchemeSpecificPart());
+ /** {@inheritDoc} */
+ @Override
+ public void onCreateConnections(
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> callback) {
+ new GetSipProfileTask(this, request, callback).execute();
+ super.onCreateConnections(request, callback);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onCreateIncomingConnection(
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> callback) {
+ super.onCreateIncomingConnection(request, callback);
+ // TODO: fill in
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean canCall(Uri handle) {
+ return canCall(this, handle);
+ }
+
+ // TODO: Refactor this out when CallServiceSelector is deprecated
+ /* package */ static boolean canCall(Context context, Uri handle) {
+ return shouldUseSipPhone(context, handle.getScheme(), handle.getSchemeSpecificPart());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected TelephonyConnection onCreateTelephonyConnection(
+ ConnectionRequest request,
+ com.android.internal.telephony.Connection connection) {
+ return new SipConnection(connection);
}
private static boolean shouldUseSipPhone(Context context, String scheme, String number) {
@@ -80,56 +112,21 @@
return true;
}
- /** {@inheritDoc} */
- @Override
- public void isCompatibleWith(CallInfo callInfo) {
- getAdapter().setIsCompatibleWith(callInfo.getId(), shouldSelect(this, callInfo));
- }
-
- /** {@inheritDoc} */
- @Override
- public void call(CallInfo callInfo) {
- new GetSipProfileTask(this, callInfo).execute();
- }
-
- /** {@inheritDoc} */
- @Override
- public void setIncomingCallId(String callId, Bundle extras) {
- // TODO(santoscordon): fill in.
- }
-
- /** {@inheritDoc} */
- public void answer(String callId) {
- // TODO(santoscordon): fill in.
- }
-
- /** {@inheritDoc} */
- public void reject(String callId) {
- // TODO(santoscordon): fill in.
- }
-
- /** {@inheritDoc} */
- @Override
- public void playDtmfTone(String callId, char digit) {
- // TODO(ihab): fill in
- }
-
- /** {@inheritDoc} */
- @Override
- public void stopDtmfTone(String callId) {
- // TODO(ihab): fill in
- }
-
/**
* Asynchronously looks up the SIP profile to use for the given call.
*/
private class GetSipProfileTask extends AsyncTask<Void, Void, SipProfile> {
- private final CallInfo mCallInfo;
+ private final ConnectionRequest mRequest;
+ private final Response<ConnectionRequest, Connection> mResponse;
private final SipProfileDb mSipProfileDb;
private final SipSharedPreferences mSipSharedPreferences;
- GetSipProfileTask(Context context, CallInfo callInfo) {
- mCallInfo = callInfo;
+ GetSipProfileTask(
+ Context context,
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> response) {
+ mRequest = request;
+ mResponse = response;
mSipProfileDb = new SipProfileDb(context);
mSipSharedPreferences = new SipSharedPreferences(context);
}
@@ -148,11 +145,14 @@
@Override
protected void onPostExecute(SipProfile profile) {
- onSipProfileChosen(profile, mCallInfo);
+ onSipProfileChosen(profile, mRequest, mResponse);
}
}
- private void onSipProfileChosen(SipProfile profile, CallInfo callInfo) {
+ private void onSipProfileChosen(
+ SipProfile profile,
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> response) {
SipPhone phone = null;
if (profile != null) {
String sipUri = profile.getUriString();
@@ -167,6 +167,6 @@
}
}
}
- startCallWithPhone(phone, callInfo);
+ startCallWithPhone(phone, request, response);
}
}
diff --git a/src/com/android/services/telephony/TelephonyCallServiceProvider.java b/src/com/android/services/telephony/TelephonyCallServiceProvider.java
index 12a537a..a299913 100644
--- a/src/com/android/services/telephony/TelephonyCallServiceProvider.java
+++ b/src/com/android/services/telephony/TelephonyCallServiceProvider.java
@@ -16,7 +16,6 @@
package com.android.services.telephony;
-import android.os.IBinder;
import android.telecomm.CallServiceDescriptor;
import android.telecomm.CallServiceLookupResponse;
import android.telecomm.CallServiceProvider;
@@ -32,18 +31,18 @@
public void lookupCallServices(CallServiceLookupResponse response) {
ArrayList<CallServiceDescriptor> descriptors = new ArrayList<CallServiceDescriptor>();
descriptors.add(CallServiceDescriptor.newBuilder(this)
- .setCallService(GsmCallService.class)
+ .setCallService(GsmConnectionService.class)
.setNetworkType(CallServiceDescriptor.FLAG_PSTN)
.build());
descriptors.add(CallServiceDescriptor.newBuilder(this)
- .setCallService(CdmaCallService.class)
+ .setCallService(CdmaConnectionService.class)
.setNetworkType(CallServiceDescriptor.FLAG_PSTN)
.build());
descriptors.add(CallServiceDescriptor.newBuilder(this)
- .setCallService(SipCallService.class)
- .setNetworkType(CallServiceDescriptor.FLAG_WIFI |
- CallServiceDescriptor.FLAG_MOBILE)
- .build());
+ .setCallService(SipConnectionService.class)
+ .setNetworkType(CallServiceDescriptor.FLAG_WIFI |
+ CallServiceDescriptor.FLAG_MOBILE)
+ .build());
response.setCallServiceDescriptors(descriptors);
}
}
diff --git a/src/com/android/services/telephony/TelephonyCallServiceSelector.java b/src/com/android/services/telephony/TelephonyCallServiceSelector.java
index 5673694..8bfe5ef 100644
--- a/src/com/android/services/telephony/TelephonyCallServiceSelector.java
+++ b/src/com/android/services/telephony/TelephonyCallServiceSelector.java
@@ -19,7 +19,6 @@
import android.telecomm.CallInfo;
import android.telecomm.CallServiceDescriptor;
import android.telecomm.CallServiceSelector;
-import android.telecomm.CallServiceSelectorAdapter;
import java.util.ArrayList;
import java.util.List;
@@ -40,16 +39,16 @@
}
String name = descriptor.getServiceComponent().getClassName();
- if (name.equals(CdmaCallService.class.getName())) {
- if (CdmaCallService.shouldSelect(this, callInfo)) {
+ if (name.equals(CdmaConnectionService.class.getName())) {
+ if (CdmaConnectionService.canCall(this, callInfo.getHandle())) {
selectedDescriptors.add(descriptor);
}
- } else if (name.equals(GsmCallService.class.getName())) {
- if (GsmCallService.shouldSelect(this, callInfo)) {
+ } else if (name.equals(GsmConnectionService.class.getName())) {
+ if (GsmConnectionService.canCall(this, callInfo.getHandle())) {
selectedDescriptors.add(descriptor);
}
- } else if (name.equals(SipCallService.class.getName())) {
- if (SipCallService.shouldSelect(this, callInfo)) {
+ } else if (name.equals(SipConnectionService.class.getName())) {
+ if (SipConnectionService.canCall(this, callInfo.getHandle())) {
selectedDescriptors.add(descriptor);
}
}
diff --git a/src/com/android/services/telephony/TelephonyCallConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
similarity index 76%
rename from src/com/android/services/telephony/TelephonyCallConnection.java
rename to src/com/android/services/telephony/TelephonyConnection.java
index de4a3f7..e11e3ef 100644
--- a/src/com/android/services/telephony/TelephonyCallConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -19,65 +19,54 @@
import android.os.Handler;
import android.os.Message;
import android.telecomm.CallAudioState;
-import android.telecomm.CallServiceAdapter;
+import android.telephony.DisconnectCause;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
+import com.android.services.telecomm.Connection;
/**
- * Manages a single phone call. Listens to the call's state changes and updates the
- * CallServiceAdapter.
+ * Manages a single phone call in Telephony.
*/
-class TelephonyCallConnection {
+class TelephonyConnection extends Connection {
private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 1;
- private final String mCallId;
private final StateHandler mHandler = new StateHandler();
- private CallServiceAdapter mCallServiceAdapter;
-
- private Connection mOriginalConnection;
+ private com.android.internal.telephony.Connection mOriginalConnection;
private Call.State mState = Call.State.IDLE;
- TelephonyCallConnection(CallServiceAdapter callServiceAdapter, String callId,
- Connection connection) {
- mCallServiceAdapter = callServiceAdapter;
- mCallId = callId;
- mOriginalConnection = connection;
+ protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection) {
+ mOriginalConnection = originalConnection;
mOriginalConnection.getCall().getPhone().registerForPreciseCallStateChanged(mHandler,
EVENT_PRECISE_CALL_STATE_CHANGED, null);
updateState();
}
- String getCallId() {
- return mCallId;
- }
-
- Connection getOriginalConnection() {
+ com.android.internal.telephony.Connection getOriginalConnection() {
return mOriginalConnection;
}
- void disconnect(boolean shouldAbort) {
- if (shouldAbort) {
- mCallServiceAdapter = null;
- close();
- }
- if (mOriginalConnection != null) {
- try {
- mOriginalConnection.hangup();
- } catch (CallStateException e) {
- Log.e(this, e, "Call to Connection.hangup failed with exception");
- }
- }
+ @Override
+ protected void onAbort() {
+ hangup();
+ super.onAbort();
}
- void hold() {
+ @Override
+ protected void onDisconnect() {
+ hangup();
+ super.onDisconnect();
+ }
+
+ @Override
+ protected void onHold() {
+ Log.d(this, "Attempting to put call on hold");
// TODO(santoscordon): Can dialing calls be put on hold as well since they take up the
// foreground call slot?
if (Call.State.ACTIVE == mState) {
- Log.v(this, "Holding active call %s.", mCallId);
+ Log.v(this, "Holding active call");
try {
Phone phone = mOriginalConnection.getCall().getPhone();
Call ringingCall = phone.getRingingCall();
@@ -103,9 +92,12 @@
} else {
Log.w(this, "Cannot put a call that is not currently active on hold.");
}
+ super.onHold();
}
- void unhold() {
+ @Override
+ protected void onUnhold() {
+ Log.d(this, "Attempting to release call from hold");
if (Call.State.HOLDING == mState) {
try {
// TODO: This doesn't handle multiple calls across call services yet
@@ -116,9 +108,11 @@
} else {
Log.w(this, "Cannot release a call that is not already on hold from hold.");
}
+ super.onUnhold();
}
- void onAudioStateChanged(CallAudioState audioState) {
+ @Override
+ protected void onSetAudioState(CallAudioState audioState) {
// TODO: update TTY mode.
if (mOriginalConnection != null) {
Call call = mOriginalConnection.getCall();
@@ -126,10 +120,25 @@
call.getPhone().setEchoSuppressionEnabled();
}
}
+ super.onSetAudioState(audioState);
+ }
+
+ private void hangup() {
+ if (mOriginalConnection != null) {
+ try {
+ mOriginalConnection.hangup();
+ // Set state deliberately since we are going to close() and will no longer be
+ // listening to state updates from mOriginalConnection
+ setDisconnected(DisconnectCause.NORMAL, null);
+ } catch (CallStateException e) {
+ Log.e(this, e, "Call to Connection.hangup failed with exception");
+ }
+ }
+ close();
}
private void updateState() {
- if (mOriginalConnection == null || mCallServiceAdapter == null) {
+ if (mOriginalConnection == null) {
return;
}
@@ -138,28 +147,28 @@
return;
}
+ Log.d(this, "mOriginalConnection new state = %s", newState);
+
mState = newState;
switch (newState) {
case IDLE:
break;
case ACTIVE:
- mCallServiceAdapter.setActive(mCallId);
+ setActive();
break;
case HOLDING:
- mCallServiceAdapter.setOnHold(mCallId);
+ setOnHold();
break;
case DIALING:
case ALERTING:
- mCallServiceAdapter.setDialing(mCallId);
+ setDialing();
break;
case INCOMING:
case WAITING:
- mCallServiceAdapter.setRinging(mCallId);
+ setRinging();
break;
case DISCONNECTED:
- mCallServiceAdapter.setDisconnected(
- mCallId, mOriginalConnection.getDisconnectCause(), null);
- close();
+ setDisconnected(mOriginalConnection.getDisconnectCause(), null);
break;
case DISCONNECTING:
break;
@@ -174,7 +183,6 @@
}
mOriginalConnection = null;
}
- CallRegistrar.unregister(mCallId);
}
private class StateHandler extends Handler {
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
new file mode 100644
index 0000000..d575f05
--- /dev/null
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 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.services.telephony;
+
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Phone;
+import com.android.services.telecomm.Connection;
+import com.android.services.telecomm.ConnectionRequest;
+import com.android.services.telecomm.ConnectionService;
+import com.android.services.telecomm.Response;
+import com.android.services.telecomm.Subscription;
+
+import com.google.android.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * The parent class for telephony-based call services. Subclasses provide the specific phone (GSM,
+ * CDMA, etc...) to use.
+ */
+public abstract class TelephonyConnectionService extends ConnectionService {
+ private static final Set<com.android.internal.telephony.Connection> sKnownConnections
+ = Sets.newHashSet();
+
+ /** {@inheritDoc} */
+ @Override
+ public void onFindSubscriptions(
+ Uri handle,
+ Response<Uri, Subscription> response) {
+ try {
+ responseResult(handle, response, canCall(handle) ? new Subscription() : null);
+ } catch (Exception e) {
+ responseError(handle, response, "onFindSubscriptions error: " + e.toString());
+ }
+ }
+
+ /**
+ * Initiates the underlying Telephony call, then creates a {@link TelephonyConnection}
+ * by calling
+ * {@link #createTelephonyConnection(ConnectionRequest,
+ * com.android.internal.telephony.Connection)}
+ * at the appropriate time. Should be called by the subclass.
+ */
+ protected void startCallWithPhone(
+ Phone phone,
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> response) {
+ Log.d(this, "startCallWithPhone: %s.", request);
+
+ if (phone == null) {
+ responseError(request, response, "Phone is null");
+ return;
+ }
+
+ if (request.getHandle() == null) {
+ responseError(request, response, "Handle is null");
+ return;
+ }
+
+ String number = request.getHandle().getSchemeSpecificPart();
+ if (TextUtils.isEmpty(number)) {
+ responseError(request, response, "Unable to parse number");
+ return;
+ }
+
+ com.android.internal.telephony.Connection connection;
+ try {
+ connection = phone.dial(number);
+ } catch (CallStateException e) {
+ Log.e(this, e, "Call to Phone.dial failed with exception");
+ responseError(request, response, e.getMessage());
+ return;
+ }
+
+ if (connection == null) {
+ responseError(request, response, "Call to phone.dial failed");
+ return;
+ }
+
+ try {
+ responseResult(request, response, createTelephonyConnection(request, connection));
+ } catch (Exception e) {
+ Log.e(this, e, "Call to createConnection failed with exception");
+ responseError(request, response, e.getMessage());
+ }
+ }
+
+ protected <REQUEST, RESULT> void responseError(
+ REQUEST request,
+ Response<REQUEST, RESULT> response,
+ String reason) {
+ Log.d(this, "responseError %s: %s", request, reason);
+ response.onError(request, reason);
+ }
+
+ protected void responseResult(
+ Uri request,
+ Response<Uri, Subscription> response,
+ Subscription result) {
+ Log.d(this, "responseResult %s -> %s", request, result);
+ response.onResult(request, result);
+ }
+
+ protected void responseResult(
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> response,
+ Connection result) {
+ Log.d(this, "responseResult %s -> %s", request, result);
+ response.onResult(request, result);
+ }
+
+ protected final TelephonyConnection createTelephonyConnection(
+ ConnectionRequest request,
+ final com.android.internal.telephony.Connection connection) {
+ final TelephonyConnection telephonyConnection =
+ onCreateTelephonyConnection(request, connection);
+ sKnownConnections.add(connection);
+ telephonyConnection.addConnectionListener(new Connection.ListenerBase() {
+ @Override
+ public void onDestroyed(Connection c) {
+ telephonyConnection.removeConnectionListener(this);
+ sKnownConnections.remove(connection);
+ }
+ });
+ return telephonyConnection;
+ }
+
+ protected static boolean isConnectionKnown(
+ com.android.internal.telephony.Connection connection) {
+ return sKnownConnections.contains(connection);
+ }
+
+ /**
+ * Determine whether this {@link TelephonyConnectionService} can place a call
+ * to the supplied handle (phone number).
+ *
+ * @param handle The proposed handle.
+ * @return {@code true} if the handle can be called.
+ */
+ protected abstract boolean canCall(Uri handle);
+
+ /**
+ * Create a Telephony-specific {@link Connection} object.
+ *
+ * @param request A request for creating a {@link Connection}.
+ * @param connection An underlying Telephony {@link com.android.internal.telephony.Connection}
+ * to use.
+ * @return A new {@link TelephonyConnection}.
+ */
+ protected abstract TelephonyConnection onCreateTelephonyConnection(
+ ConnectionRequest request,
+ com.android.internal.telephony.Connection connection);
+}
diff --git a/src/com/android/services/telephony/TelephonyGlobals.java b/src/com/android/services/telephony/TelephonyGlobals.java
index 042d1c6..8ca7e03 100644
--- a/src/com/android/services/telephony/TelephonyGlobals.java
+++ b/src/com/android/services/telephony/TelephonyGlobals.java
@@ -66,12 +66,12 @@
if (TelephonyManager.PHONE_TYPE_GSM == phoneType) {
Log.d(this, "Phone type GSM found");
mGsmIncomingCallNotifier = new IncomingCallNotifier(
- GsmCallService.class, CachedPhoneFactory.getGsmPhone());
+ GsmConnectionService.class, CachedPhoneFactory.getGsmPhone());
} else if (TelephonyManager.PHONE_TYPE_CDMA == phoneType) {
Log.d(this, "Phone type CDMA found");
mCdmaIncomingCallNotifier = new IncomingCallNotifier(
- CdmaCallService.class, CachedPhoneFactory.getCdmaPhone());
+ CdmaConnectionService.class, CachedPhoneFactory.getCdmaPhone());
}
// TODO(santoscordon): Do SIP. SIP will require a slightly different solution since it