summaryrefslogtreecommitdiffstats
path: root/telecomm
diff options
context:
space:
mode:
authorIhab Awad <ihab@google.com>2014-05-16 10:22:16 -0700
committerIhab Awad <ihab@google.com>2014-05-18 18:58:28 -0700
commit542e0ea8bfa60f09c33e4be366adf8681c25d0bf (patch)
treec14780ca73f0a2638138ae8a7a2ec1b2f26e80b9 /telecomm
parentc9ee304c91b5572f0c1cd95e9bd1e4a84e28587d (diff)
downloadframeworks_base-542e0ea8bfa60f09c33e4be366adf8681c25d0bf.zip
frameworks_base-542e0ea8bfa60f09c33e4be366adf8681c25d0bf.tar.gz
frameworks_base-542e0ea8bfa60f09c33e4be366adf8681c25d0bf.tar.bz2
Publish new Telecomm API for Connection Services
Creates a new API for Connection Services providing phone connections to the Android system. Supersedes CallService API, which will be gradually replaced by this one. Change-Id: Ie4b06c1b0843a777c5b8ee9c5dd12b9d9f990980
Diffstat (limited to 'telecomm')
-rw-r--r--telecomm/java/android/telecomm/Connection.java412
-rw-r--r--telecomm/java/android/telecomm/ConnectionRequest.java57
-rw-r--r--telecomm/java/android/telecomm/ConnectionService.java345
-rw-r--r--telecomm/java/android/telecomm/Response.java39
-rw-r--r--telecomm/java/android/telecomm/Subscription.java48
5 files changed, 901 insertions, 0 deletions
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
new file mode 100644
index 0000000..6b7463c
--- /dev/null
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -0,0 +1,412 @@
+/*
+ * 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 android.telecomm;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Represents a connection to a remote endpoint that carries voice traffic.
+ */
+public abstract class Connection {
+
+ private static String TAG = Connection.class.getSimpleName();
+
+ public interface Listener {
+ void onStateChanged(Connection c, int state);
+ 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() {}
+
+ public static final int NEW = 0;
+ public static final int RINGING = 1;
+ public static final int DIALING = 2;
+ public static final int ACTIVE = 3;
+ public static final int HOLDING = 4;
+ public static final int DISCONNECTED = 5;
+ }
+
+ private final Set<Listener> mListeners = new HashSet<>();
+ private int mState = State.NEW;
+ private CallAudioState mCallAudioState;
+ private Uri mHandle;
+
+ /**
+ * Create a new Connection.
+ */
+ protected Connection() {}
+
+ /**
+ * @return The handle (e.g., phone number) to which this Connection
+ * is currently communicating.
+ */
+ public final Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * @return The state of this Connection.
+ *
+ * @hide
+ */
+ public final int getState() {
+ return mState;
+ }
+
+ /**
+ * @return The audio state of the call, describing how its audio is currently
+ * being routed by the system. This is {@code null} if this Connection
+ * does not directly know about its audio state.
+ */
+ public final CallAudioState getCallAudioState() {
+ return mCallAudioState;
+ }
+
+ /**
+ * Assign a listener to be notified of state changes.
+ *
+ * @param l A listener.
+ * @return This Connection.
+ *
+ * @hide
+ */
+ 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 Connection removeConnectionListener(Listener l) {
+ mListeners.remove(l);
+ return this;
+ }
+
+ /**
+ * Play a DTMF tone in this Connection.
+ *
+ * @param c A DTMF character.
+ *
+ * @hide
+ */
+ public final void playDtmfTone(char c) {
+ Log.d(TAG, "playDtmfTone " + c);
+ onPlayDtmfTone(c);
+ }
+
+ /**
+ * Stop any DTMF tones which may be playing in this Connection.
+ *
+ * @hide
+ */
+ public final void stopDtmfTone() {
+ Log.d(TAG, "stopDtmfTone");
+ onStopDtmfTone();
+ }
+
+ /**
+ * Disconnect this Connection. If and when the Connection can comply with
+ * this request, it will transition to the {@link State#DISCONNECTED}
+ * state and notify its listeners.
+ *
+ * @hide
+ */
+ public final void disconnect() {
+ Log.d(TAG, "disconnect");
+ onDisconnect();
+ }
+
+ /**
+ * Abort this Connection. The Connection will immediately transition to
+ * the {@link State#DISCONNECTED} state, and send no notifications of this
+ * or any other future events.
+ *
+ * @hide
+ */
+ public final void abort() {
+ Log.d(TAG, "abort");
+ onAbort();
+ }
+
+ /**
+ * Place this Connection on hold. If and when the Connection can comply with
+ * this request, it will transition to the {@link State#HOLDING}
+ * state and notify its listeners.
+ *
+ * @hide
+ */
+ public final void hold() {
+ Log.d(TAG, "hold");
+ onHold();
+ }
+
+ /**
+ * Un-hold this Connection. If and when the Connection can comply with
+ * this request, it will transition to the {@link State#ACTIVE}
+ * state and notify its listeners.
+ *
+ * @hide
+ */
+ public final void unhold() {
+ Log.d(TAG, "unhold");
+ onUnhold();
+ }
+
+ /**
+ * Accept a {@link State#RINGING} Connection. If and when the Connection
+ * can comply with this request, it will transition to the {@link State#ACTIVE}
+ * state and notify its listeners.
+ *
+ * @hide
+ */
+ public final void answer() {
+ Log.d(TAG, "answer");
+ if (mState == State.RINGING) {
+ onAnswer();
+ }
+ }
+
+ /**
+ * Reject a {@link State#RINGING} Connection. If and when the Connection
+ * can comply with this request, it will transition to the {@link State#ACTIVE}
+ * state and notify its listeners.
+ *
+ * @hide
+ */
+ public final void reject() {
+ Log.d(TAG, "reject");
+ if (mState == State.RINGING) {
+ onReject();
+ }
+ }
+
+ /**
+ * Inform this Connection that the state of its audio output has been changed externally.
+ *
+ * @param state The new audio state.
+ */
+ public void setAudioState(CallAudioState state) {
+ Log.d(TAG, "setAudioState " + state);
+ onSetAudioState(state);
+ }
+
+ /**
+ * @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(TAG, "Unknown state " + state);
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Sets the value of the {@link #getHandle()} property and notifies listeners.
+ *
+ * @param handle The new handle.
+ */
+ protected void setHandle(Uri handle) {
+ Log.d(TAG, "setHandle " + handle);
+ // TODO: Enforce super called
+ mHandle = handle;
+ for (Listener l : mListeners) {
+ l.onHandleChanged(this, handle);
+ }
+ }
+
+ /**
+ * Sets state to active (e.g., an ongoing call where two or more parties can actively
+ * communicate).
+ */
+ protected void setActive() {
+ setState(State.ACTIVE);
+ }
+
+ /**
+ * Sets state to ringing (e.g., an inbound ringing call).
+ */
+ 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(TAG, "Disconnected with cause " + cause + " message " + message);
+ for (Listener l : mListeners) {
+ l.onDisconnected(this, cause, message);
+ }
+ }
+
+ /**
+ * Notifies this Connection and listeners that the {@link #getCallAudioState()} property
+ * has a new value.
+ *
+ * @param state The new call audio state.
+ */
+ protected void onSetAudioState(CallAudioState state) {
+ // TODO: Enforce super called
+ this.mCallAudioState = state;
+ for (Listener l : mListeners) {
+ l.onAudioStateChanged(this, state);
+ }
+ }
+
+ /**
+ * Notifies this Connection and listeners of a change in the current signal levels
+ * for the underlying data transport.
+ *
+ * @param details A {@link android.os.Bundle} containing details of the current level.
+ */
+ protected void onSetSignal(Bundle details) {
+ // TODO: Enforce super called
+ for (Listener l : mListeners) {
+ l.onSignalChanged(this, details);
+ }
+ }
+
+ /**
+ * Notifies this Connection of a request to play a DTMF tone.
+ *
+ * @param c A DTMF character.
+ */
+ protected void onPlayDtmfTone(char c) {}
+
+ /**
+ * Notifies this Connection of a request to stop any currently playing DTMF tones.
+ */
+ protected void onStopDtmfTone() {}
+
+ /**
+ * Notifies this Connection of a request to disconnect.
+ */
+ protected void onDisconnect() {}
+
+ /**
+ * Notifies this Connection of a request to abort.
+ */
+ protected void onAbort() {}
+
+ /**
+ * Notifies this Connection of a request to hold.
+ */
+ protected void onHold() {}
+
+ /**
+ * Notifies this Connection of a request to exit a hold state.
+ */
+ protected void onUnhold() {}
+
+ /**
+ * Notifies this Connection, which is in {@link State#RINGING}, of
+ * a request to accept.
+ */
+ protected void onAnswer() {}
+
+ /**
+ * Notifies this Connection, which is in {@link State#RINGING}, of
+ * a request to reject.
+ */
+ protected void onReject() {}
+
+ private void setState(int state) {
+ Log.d(TAG, "setState: " + stateToString(state));
+ this.mState = state;
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, state);
+ }
+ }
+}
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
new file mode 100644
index 0000000..c1f1871
--- /dev/null
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.telecomm;
+
+import android.os.Bundle;
+import android.net.Uri;
+
+/**
+ * Simple data container encapsulating a request to some entity to
+ * create a new {@link Connection}.
+ */
+public final class ConnectionRequest {
+
+ // TODO: Token to limit recursive invocations
+ // TODO: Consider upgrading "mHandle" to ordered list of handles, indicating a set of phone
+ // numbers that would satisfy the client's needs, in order of preference
+ private final Uri mHandle;
+ private final Bundle mExtras;
+
+ public ConnectionRequest(Uri handle, Bundle extras) {
+ mHandle = handle; mExtras = extras;
+ }
+
+ /**
+ * The handle (e.g., phone number) to which the {@link Connection} is to connect.
+ */
+ public Uri getHandle() { return mHandle; }
+
+ /**
+ * Application-specific extra data. Used for passing back information from an incoming
+ * call {@code Intent}, and for any proprietary extensions arranged between a client
+ * 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/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
new file mode 100644
index 0000000..aba4579
--- /dev/null
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -0,0 +1,345 @@
+/*
+ * 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 android.telecomm;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 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();
+
+ // STOPSHIP: Debug Logging should be conditional on a debug flag or use a set of
+ // logging functions that make it automaticaly so.
+
+ // 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() {};
+
+ // Mappings from Connections to IDs as understood by the current CallService implementation
+ private final Map<String, Connection> mConnectionById = new HashMap<>();
+ private final Map<Connection, String> mIdByConnection = new HashMap<>();
+
+ private final Connection.Listener mConnectionListener = new Connection.Listener() {
+ @Override
+ public void onStateChanged(Connection c, int state) {
+ String id = mIdByConnection.get(c);
+ Log.d(TAG, "Adapter set state " + id + " " + Connection.stateToString(state));
+ switch (state) {
+ case Connection.State.ACTIVE:
+ getAdapter().setActive(id);
+ break;
+ case Connection.State.DIALING:
+ getAdapter().setDialing(id);
+ break;
+ case Connection.State.DISCONNECTED:
+ // Handled in onDisconnected()
+ break;
+ case Connection.State.HOLDING:
+ getAdapter().setOnHold(id);
+ break;
+ case Connection.State.NEW:
+ // Nothing to tell Telecomm
+ break;
+ case Connection.State.RINGING:
+ getAdapter().setRinging(id);
+ break;
+ }
+ }
+
+ @Override
+ public void onDisconnected(Connection c, int cause, String message) {
+ String id = mIdByConnection.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
+ }
+
+ @Override
+ public void onAudioStateChanged(Connection c, CallAudioState state) {
+ // TODO: Unsupported yet
+ }
+
+ @Override
+ public void onSignalChanged(Connection c, Bundle details) {
+ // TODO: Unsupported yet
+ }
+
+ @Override
+ public void onDestroyed(Connection c) {
+ 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) {
+ 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);
+ }
+ }
+ );
+ }
+
+ @Override
+ public final void call(final CallInfo callInfo) {
+ Log.d(TAG, "call " + callInfo);
+ onCreateConnections(
+ new ConnectionRequest(
+ callInfo.getHandle(),
+ callInfo.getExtras()),
+ new Response<ConnectionRequest, Connection>() {
+ @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 (Connection c : result) {
+ c.abort();
+ }
+ } else {
+ addConnection(callInfo.getId(), result[0]);
+ Log.d(TAG, "adapter handleSuccessfulOutgoingCall "
+ + callInfo.getId());
+ getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
+ }
+ }
+
+ @Override
+ public void onError(ConnectionRequest request, String reason) {
+ getAdapter().handleFailedOutgoingCall(callInfo.getId(), reason);
+ }
+ }
+ );
+ }
+
+ @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: 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 (Connection c : result) {
+ c.abort();
+ }
+ } else {
+ 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,
+ connectionStateToCallState(result[0].getState()),
+ request.getHandle() /* result[0].getHandle() == null
+ ? Uri.EMPTY : result[0].getHandle() */));
+ }
+ }
+
+ @Override
+ public void onError(ConnectionRequest request, String reason) {
+ Log.d(TAG, "adapter failed setIncomingCallId " + request + " " + reason);
+ }
+ }
+ );
+ }
+
+ @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 (e.g. 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(
+ Uri handle,
+ Response<Uri, Subscription> callback) {}
+
+ /**
+ * Create a Connection given a request.
+ *
+ * @param request Data encapsulating details of the desired Connection.
+ * @param callback A callback for providing the result.
+ */
+ public void onCreateConnections(
+ ConnectionRequest request,
+ Response<ConnectionRequest, Connection> callback) {}
+
+ /**
+ * Create a Connection to match an incoming connection notification.
+ *
+ * @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) {}
+
+ 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 Connection.State " + connectionState);
+ return CallState.NEW;
+ }
+ }
+
+ private void addConnection(String callId, Connection connection) {
+ mConnectionById.put(callId, connection);
+ mIdByConnection.put(connection, callId);
+ connection.addConnectionListener(mConnectionListener);
+ }
+
+ private void removeConnection(Connection connection) {
+ connection.removeConnectionListener(mConnectionListener);
+ mConnectionById.remove(mIdByConnection.get(connection));
+ mIdByConnection.remove(connection);
+ }
+
+ private Connection findConnectionForAction(String callId, String action) {
+ if (mConnectionById.containsKey(callId)) {
+ return mConnectionById.get(callId);
+ }
+ Log.wtf(TAG, action + " - Cannot find Connection \"" + callId + "\"");
+ return NULL_CONNECTION;
+ }
+} \ No newline at end of file
diff --git a/telecomm/java/android/telecomm/Response.java b/telecomm/java/android/telecomm/Response.java
new file mode 100644
index 0000000..14f8340
--- /dev/null
+++ b/telecomm/java/android/telecomm/Response.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.telecomm;
+
+/**
+ * Used to inform a client of asynchronously returned results.
+ */
+public interface Response<IN, OUT> {
+
+ /**
+ * Provide a set of results.
+ *
+ * @param request The original request.
+ * @param result The results.
+ */
+ void onResult(IN request, OUT... result);
+
+ /**
+ * Indicates the inability to provide results.
+ *
+ * @param request The original request.
+ * @param reason The reason for the failure.
+ */
+ void onError(IN request, String reason);
+}
diff --git a/telecomm/java/android/telecomm/Subscription.java b/telecomm/java/android/telecomm/Subscription.java
new file mode 100644
index 0000000..f187f4d
--- /dev/null
+++ b/telecomm/java/android/telecomm/Subscription.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.telecomm;
+
+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;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {}
+
+ public static final Parcelable.Creator<Subscription> CREATOR
+ = new Parcelable.Creator<Subscription>() {
+ public Subscription createFromParcel(Parcel in) {
+ return new Subscription(in);
+ }
+
+ public Subscription[] newArray(int size) {
+ return new Subscription[size];
+ }
+ };
+
+ private Subscription(Parcel in) {}
+}