diff options
Diffstat (limited to 'telecomm/java/android')
22 files changed, 2539 insertions, 0 deletions
diff --git a/telecomm/java/android/telecomm/CallAudioState.aidl b/telecomm/java/android/telecomm/CallAudioState.aidl new file mode 100644 index 0000000..ae64567 --- /dev/null +++ b/telecomm/java/android/telecomm/CallAudioState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +parcelable CallAudioState; diff --git a/telecomm/java/android/telecomm/CallAudioState.java b/telecomm/java/android/telecomm/CallAudioState.java new file mode 100644 index 0000000..d9a0090 --- /dev/null +++ b/telecomm/java/android/telecomm/CallAudioState.java @@ -0,0 +1,160 @@ +/* + * 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; + +import java.util.Locale; + +/** + * Encapsulates all audio states during a call. + */ +public final class CallAudioState implements Parcelable { + /** Direct the audio stream through the device's earpiece. */ + public static int ROUTE_EARPIECE = 0x00000001; + + /** Direct the audio stream through Bluetooth. */ + public static int ROUTE_BLUETOOTH = 0x00000002; + + /** Direct the audio stream through a wired headset. */ + public static int ROUTE_WIRED_HEADSET = 0x00000004; + + /** Direct the audio stream through the device's spakerphone. */ + public static int ROUTE_SPEAKER = 0x00000008; + + /** + * Direct the audio stream through the device's earpiece or wired headset if one is + * connected. + */ + public static int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET; + + /** Bit mask of all possible audio routes. */ + public static int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET | + ROUTE_SPEAKER; + + /** True if the call is muted, false otherwise. */ + public final boolean isMuted; + + /** The route to use for the audio stream. */ + public final int route; + + /** Bit vector of all routes supported by this call. */ + public final int supportedRouteMask; + + /** @hide */ + public CallAudioState(boolean isMuted, int route, int supportedRouteMask) { + this.isMuted = isMuted; + this.route = route; + this.supportedRouteMask = supportedRouteMask; + } + + /** @hide */ + public CallAudioState(CallAudioState state) { + isMuted = state.isMuted; + route = state.route; + supportedRouteMask = state.supportedRouteMask; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof CallAudioState)) { + return false; + } + CallAudioState state = (CallAudioState) obj; + return isMuted == state.isMuted && route == state.route && + supportedRouteMask == state.supportedRouteMask; + } + + @Override + public String toString() { + return String.format(Locale.US, + "[CallAudioState isMuted: %b, route; %s, supportedRouteMask: %s]", + isMuted, audioRouteToString(route), audioRouteToString(supportedRouteMask)); + } + + /** @hide */ + public static String audioRouteToString(int route) { + if (route == 0 || (route & ~ROUTE_ALL) != 0x0) { + return "UNKNOWN"; + } + + StringBuffer buffer = new StringBuffer(); + if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) { + listAppend(buffer, "EARPIECE"); + } + if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) { + listAppend(buffer, "BLUETOOTH"); + } + if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) { + listAppend(buffer, "WIRED_HEADSET"); + } + if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) { + listAppend(buffer, "SPEAKER"); + } + + return buffer.toString(); + } + + private static void listAppend(StringBuffer buffer, String str) { + if (buffer.length() > 0) { + buffer.append(", "); + } + buffer.append(str); + } + + /** + * Responsible for creating CallAudioState objects for deserialized Parcels. + */ + public static final Parcelable.Creator<CallAudioState> CREATOR = + new Parcelable.Creator<CallAudioState> () { + + @Override + public CallAudioState createFromParcel(Parcel source) { + boolean isMuted = source.readByte() == 0 ? false : true; + int route = source.readInt(); + int supportedRouteMask = source.readInt(); + return new CallAudioState(isMuted, route, supportedRouteMask); + } + + @Override + public CallAudioState[] newArray(int size) { + return new CallAudioState[size]; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Writes CallAudioState object into a serializeable Parcel. + */ + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeByte((byte) (isMuted ? 1 : 0)); + destination.writeInt(route); + destination.writeInt(supportedRouteMask); + } +} diff --git a/telecomm/java/android/telecomm/CallCapabilities.java b/telecomm/java/android/telecomm/CallCapabilities.java new file mode 100644 index 0000000..b2b33a3 --- /dev/null +++ b/telecomm/java/android/telecomm/CallCapabilities.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +/** Defines actions a call currently supports. */ +public class CallCapabilities { + /** Call can currently be put on hold or unheld. */ + public static final int HOLD = 0x00000001; + + /** Call supports the hold feature. */ + public static final int SUPPORT_HOLD = 0x00000002; + + /** Call can currently be merged. */ + public static final int MERGE_CALLS = 0x00000004; + + /* Call can currently be swapped with another call. */ + public static final int SWAP_CALLS = 0x00000008; + + /* Call currently supports adding another call to this one. */ + public static final int ADD_CALL = 0x00000010; + + /* Call supports responding via text option. */ + public static final int RESPOND_VIA_TEXT = 0x00000020; + + /* Call can be muted. */ + public static final int MUTE = 0x00000040; + + /* Call supports generic conference mode. */ + public static final int GENERIC_CONFERENCE = 0x00000080; + + /* Call currently supports switch between connections. */ + public static final int CONNECTION_HANDOFF = 0x00000100; + + public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL + | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF; +} diff --git a/telecomm/java/android/telecomm/CallInfo.aidl b/telecomm/java/android/telecomm/CallInfo.aidl new file mode 100644 index 0000000..bc5ef96 --- /dev/null +++ b/telecomm/java/android/telecomm/CallInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2013, 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; + +parcelable CallInfo; diff --git a/telecomm/java/android/telecomm/CallInfo.java b/telecomm/java/android/telecomm/CallInfo.java new file mode 100644 index 0000000..cb7f2dc --- /dev/null +++ b/telecomm/java/android/telecomm/CallInfo.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013, 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Date; + +/** + * A parcelable holder class of Call information data. This class is intended for transfering call + * information from Telecomm to call services and thus is read-only. + * TODO(santoscordon): Need final public-facing comments in this file. + */ +public final class CallInfo implements Parcelable { + + /** + * Unique identifier for the call. + */ + private final String mId; + + /** + * The state of the call. + */ + private final CallState mState; + + /** + * Endpoint to which the call is connected. + * This could be the dialed value for outgoing calls or the caller id of incoming calls. + */ + private final Uri mHandle; + + /** + * Gateway information for the call. + */ + private final GatewayInfo mGatewayInfo; + + /** + * Additional information that can be persisted. For example, extra handoff information can + * attached to a call using {@link CallServiceSelectorAdapter#setHandoffInfo(String,Uri,Bundle). + */ + private final Bundle mExtras; + + /** The descriptor for the call service currently routing this call. */ + private final CallServiceDescriptor mCurrentCallServiceDescriptor; + + public CallInfo(String id, CallState state, Uri handle) { + this(id, state, handle, null, Bundle.EMPTY, null); + } + + /** + * Persists handle of the other party of this call. + * + * @param id The unique ID of the call. + * @param state The state of the call. + * @param handle The handle to the other party in this call. + * @param gatewayInfo Gateway information pertaining to this call. + * @param extras Additional information that can be persisted. + * @param currentCallServiceDescriptor The descriptor for the call service currently routing + * this call. + * + * @hide + */ + public CallInfo( + String id, + CallState state, + Uri handle, + GatewayInfo gatewayInfo, + Bundle extras, + CallServiceDescriptor currentCallServiceDescriptor) { + mId = id; + mState = state; + mHandle = handle; + mGatewayInfo = gatewayInfo; + mExtras = extras; + mCurrentCallServiceDescriptor = currentCallServiceDescriptor; + } + + public String getId() { + return mId; + } + + public CallState getState() { + return mState; + } + + public Uri getHandle() { + return mHandle; + } + + /** + * @return The actual handle this call is associated with. This is used by call services to + * correctly indicate in their UI what handle the user is actually calling, and by other + * telecomm components that require the user-dialed handle to function. + */ + public Uri getOriginalHandle() { + if (mGatewayInfo != null) { + return mGatewayInfo.getOriginalHandle(); + } + return getHandle(); + } + + public GatewayInfo getGatewayInfo() { + return mGatewayInfo; + } + + public Bundle getExtras() { + return mExtras; + } + + public CallServiceDescriptor getCurrentCallServiceDescriptor() { + return mCurrentCallServiceDescriptor; + } + + /** + * Responsible for creating CallInfo objects for deserialized Parcels. + */ + public static final Parcelable.Creator<CallInfo> CREATOR = new Parcelable.Creator<CallInfo> () { + @Override + public CallInfo createFromParcel(Parcel source) { + String id = source.readString(); + CallState state = CallState.valueOf(source.readString()); + Uri handle = Uri.CREATOR.createFromParcel(source); + + boolean gatewayInfoPresent = source.readByte() != 0; + GatewayInfo gatewayInfo = null; + if (gatewayInfoPresent) { + gatewayInfo = GatewayInfo.CREATOR.createFromParcel(source); + } + + ClassLoader classLoader = CallInfo.class.getClassLoader(); + Bundle extras = source.readParcelable(classLoader); + CallServiceDescriptor descriptor = source.readParcelable(classLoader); + return new CallInfo(id, state, handle, gatewayInfo, extras, descriptor); + } + + @Override + public CallInfo[] newArray(int size) { + return new CallInfo[size]; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Writes CallInfo object into a serializeable Parcel. + */ + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeString(mId); + destination.writeString(mState.name()); + mHandle.writeToParcel(destination, 0); + + if (mGatewayInfo != null) { + destination.writeByte((byte) 1); + mGatewayInfo.writeToParcel(destination, 0); + } else { + destination.writeByte((byte) 0); + } + + destination.writeParcelable(mExtras, 0); + destination.writeParcelable(mCurrentCallServiceDescriptor, 0); + } +} diff --git a/telecomm/java/android/telecomm/CallNumberPresentation.java b/telecomm/java/android/telecomm/CallNumberPresentation.java new file mode 100644 index 0000000..6cd22f8 --- /dev/null +++ b/telecomm/java/android/telecomm/CallNumberPresentation.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +/** Defines how numbers are displayed in caller id. */ +public enum CallNumberPresentation { + /** Number is displayed normally. */ + ALLOWED, + + /** Number was blocked. */ + RESTRICTED, + + /** Presentation was not specified or is unknown. */ + UNKNOWN, + + /** Number should be displayed as a pay phone. */ + PAYPHONE +} diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java new file mode 100644 index 0000000..51f10c1 --- /dev/null +++ b/telecomm/java/android/telecomm/CallService.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2013 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.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; + +import com.android.internal.os.SomeArgs; +import com.android.internal.telecomm.ICallService; +import com.android.internal.telecomm.ICallServiceAdapter; + +/** + * Base implementation of CallService which can be used to provide calls for the system + * in-call UI. CallService is a one-way service from the framework's CallsManager to any app + * that would like to provide calls managed by the default system in-call user interface. + * When the service is bound by the framework, CallsManager will call setCallServiceAdapter + * which will provide CallService with an instance of {@link CallServiceAdapter} to be used + * for communicating back to CallsManager. Subsequently, more specific methods of the service + * will be called to perform various call actions including making an outgoing call and + * disconnected existing calls. + * TODO(santoscordon): Needs more about AndroidManifest.xml service registrations before + * we can unhide this API. + * + * Most public methods of this function are backed by a one-way AIDL interface which precludes + * synchronous responses. As a result, most responses are handled by (or have TODOs to handle) + * response objects instead of return values. + * TODO(santoscordon): Improve paragraph above once the final design is in place. + */ +public abstract class CallService extends Service { + + private static final int MSG_SET_CALL_SERVICE_ADAPTER = 1; + private static final int MSG_IS_COMPATIBLE_WITH = 2; + private static final int MSG_CALL = 3; + private static final int MSG_ABORT = 4; + private static final int MSG_SET_INCOMING_CALL_ID = 5; + private static final int MSG_ANSWER = 6; + private static final int MSG_REJECT = 7; + private static final int MSG_DISCONNECT = 8; + private static final int MSG_HOLD = 9; + private static final int MSG_UNHOLD = 10; + private static final int MSG_ON_AUDIO_STATE_CHANGED = 11; + private static final int MSG_PLAY_DTMF_TONE = 12; + private static final int MSG_STOP_DTMF_TONE = 13; + + /** + * Default Handler used to consolidate binder method calls onto a single thread. + */ + private final class CallServiceMessageHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_CALL_SERVICE_ADAPTER: + mAdapter = new CallServiceAdapter((ICallServiceAdapter) msg.obj); + onAdapterAttached(mAdapter); + break; + case MSG_IS_COMPATIBLE_WITH: + isCompatibleWith((CallInfo) msg.obj); + break; + case MSG_CALL: + call((CallInfo) msg.obj); + break; + case MSG_ABORT: + abort((String) msg.obj); + break; + case MSG_SET_INCOMING_CALL_ID: { + SomeArgs args = (SomeArgs) msg.obj; + try { + String callId = (String) args.arg1; + Bundle extras = (Bundle) args.arg2; + setIncomingCallId(callId, extras); + } finally { + args.recycle(); + } + break; + } + case MSG_ANSWER: + answer((String) msg.obj); + break; + case MSG_REJECT: + reject((String) msg.obj); + break; + case MSG_DISCONNECT: + disconnect((String) msg.obj); + break; + case MSG_HOLD: + hold((String) msg.obj); + break; + case MSG_UNHOLD: + unhold((String) msg.obj); + break; + case MSG_ON_AUDIO_STATE_CHANGED: { + SomeArgs args = (SomeArgs) msg.obj; + try { + String callId = (String) args.arg1; + CallAudioState audioState = (CallAudioState) args.arg2; + onAudioStateChanged(callId, audioState); + } finally { + args.recycle(); + } + break; + } + case MSG_PLAY_DTMF_TONE: + playDtmfTone((String) msg.obj, (char) msg.arg1); + break; + case MSG_STOP_DTMF_TONE: + stopDtmfTone((String) msg.obj); + break; + default: + break; + } + } + } + + /** + * Default ICallService implementation provided to CallsManager via {@link #onBind}. + */ + private final class CallServiceBinder extends ICallService.Stub { + @Override + public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) { + mMessageHandler.obtainMessage(MSG_SET_CALL_SERVICE_ADAPTER, callServiceAdapter) + .sendToTarget(); + } + + @Override + public void isCompatibleWith(CallInfo callInfo) { + mMessageHandler.obtainMessage(MSG_IS_COMPATIBLE_WITH, callInfo).sendToTarget(); + } + + @Override + public void call(CallInfo callInfo) { + mMessageHandler.obtainMessage(MSG_CALL, callInfo).sendToTarget(); + } + + @Override + public void abort(String callId) { + mMessageHandler.obtainMessage(MSG_ABORT, callId).sendToTarget(); + } + + @Override + public void setIncomingCallId(String callId, Bundle extras) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = extras; + mMessageHandler.obtainMessage(MSG_SET_INCOMING_CALL_ID, args).sendToTarget(); + } + + @Override + public void answer(String callId) { + mMessageHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget(); + } + + @Override + public void reject(String callId) { + mMessageHandler.obtainMessage(MSG_REJECT, callId).sendToTarget(); + } + + @Override + public void disconnect(String callId) { + mMessageHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget(); + } + + @Override + public void hold(String callId) { + mMessageHandler.obtainMessage(MSG_HOLD, callId).sendToTarget(); + } + + @Override + public void unhold(String callId) { + mMessageHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget(); + } + + @Override + public void playDtmfTone(String callId, char digit) { + mMessageHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget(); + } + + @Override + public void stopDtmfTone(String callId) { + mMessageHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget(); + } + + @Override + public void onAudioStateChanged(String callId, CallAudioState audioState) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = audioState; + mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget(); + } + } + + /** + * Message handler for consolidating binder callbacks onto a single thread. + * See {@link CallServiceMessageHandler}. + */ + private final CallServiceMessageHandler mMessageHandler = new CallServiceMessageHandler(); + + /** + * Default binder implementation of {@link ICallService} interface. + */ + private final CallServiceBinder mBinder = new CallServiceBinder(); + + private CallServiceAdapter mAdapter = null; + + /** {@inheritDoc} */ + @Override + public final IBinder onBind(Intent intent) { + return getBinder(); + } + + /** + * Returns binder object which can be used across IPC methods. + */ + public final IBinder getBinder() { + return mBinder; + } + + /** + * @return The attached {@link CallServiceAdapter} if the service is bound, null otherwise. + */ + protected final CallServiceAdapter getAdapter() { + return mAdapter; + } + + /** + * Lifecycle callback which is called when this {@link CallService} has been attached to a + * {@link CallServiceAdapter}, indicating {@link #getAdapter()} is now safe to use. + * + * @param adapter The adapter now attached to this call service. + */ + protected void onAdapterAttached(CallServiceAdapter adapter) { + } + + /** + * Determines if the CallService can place the specified call. Response is sent via + * {@link CallServiceAdapter#setIsCompatibleWith}. When responding, the correct call ID must be + * specified. Only used in the context of outgoing calls and call switching (handoff). + * + * @param callInfo The details of the relevant call. + */ + public abstract void isCompatibleWith(CallInfo callInfo); + + /** + * Attempts to call the relevant party using the specified call's handle, be it a phone number, + * SIP address, or some other kind of user ID. Note that the set of handle types is + * dynamically extensible since call providers should be able to implement arbitrary + * handle-calling systems. See {@link #isCompatibleWith}. It is expected that the + * call service respond via {@link CallServiceAdapter#handleSuccessfulOutgoingCall(String)} + * if it can successfully make the call. Only used in the context of outgoing calls. + * + * @param callInfo The details of the relevant call. + */ + public abstract void call(CallInfo callInfo); + + /** + * Aborts the outgoing call attempt. Invoked in the unlikely event that Telecomm decides to + * abort an attempt to place a call. Only ever be invoked after {@link #call} invocations. + * After this is invoked, Telecomm does not expect any more updates about the call and will + * actively ignore any such update. This is different from {@link #disconnect} where Telecomm + * expects confirmation via CallServiceAdapter.markCallAsDisconnected. + * + * @param callId The identifier of the call to abort. + */ + public abstract void abort(String callId); + + /** + * Receives a new call ID to use with an incoming call. Invoked by Telecomm after it is notified + * that this call service has a pending incoming call, see + * {@link TelecommConstants#ACTION_INCOMING_CALL}. The call service must first give Telecomm + * additional information about the call through {@link CallServiceAdapter#notifyIncomingCall}. + * Following that, the call service can update the call at will using the specified call ID. + * + * If a {@link Bundle} was passed (via {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}) in + * with the {@link TelecommConstants#ACTION_INCOMING_CALL} intent, <code>extras</code> will be + * populated with this {@link Bundle}. Otherwise, an empty Bundle will be returned. + * + * @param callId The ID of the call. + * @param extras The optional extras which were passed in with the intent, or an empty Bundle. + */ + public abstract void setIncomingCallId(String callId, Bundle extras); + + /** + * Answers a ringing call identified by callId. Telecomm invokes this method as a result of the + * user hitting the "answer" button in the incoming call screen. + * + * @param callId The ID of the call. + */ + public abstract void answer(String callId); + + /** + * Rejects a ringing call identified by callId. Telecomm invokes this method as a result of the + * user hitting the "reject" button in the incoming call screen. + * + * @param callId The ID of the call. + */ + public abstract void reject(String callId); + + /** + * Disconnects the specified call. + * + * @param callId The ID of the call to disconnect. + */ + public abstract void disconnect(String callId); + + /** + * Puts the specified call on hold. + * + * @param callId The ID of the call to put on hold. + */ + public abstract void hold(String callId); + + /** + * Removes the specified call from hold. + * + * @param callId The ID of the call to unhold. + */ + public abstract void unhold(String callId); + + /** + * Plays a dual-tone multi-frequency signaling (DTMF) tone in a call. + * + * @param callId The unique ID of the call in which the tone will be played. + * @param digit A character representing the DTMF digit for which to play the tone. This + * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. + */ + public abstract void playDtmfTone(String callId, char digit); + + /** + * Stops any dual-tone multi-frequency sinaling (DTMF) tone currently playing. + * + * DTMF tones are played by calling {@link #playDtmfTone(String,char)}. If no DTMF tone is + * currently playing, this method will do nothing. + * + * @param callId The unique ID of the call in which any currently playing tone will be stopped. + */ + public abstract void stopDtmfTone(String callId); + + /** + * Called when the audio state changes. + * + * @param activeCallId The identifier of the call that was active during the state change. + * @param audioState The new {@link CallAudioState}. + */ + public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); +} diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java new file mode 100644 index 0000000..d5bb989 --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceAdapter.java @@ -0,0 +1,160 @@ +/* + * 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.RemoteException; + +import com.android.internal.telecomm.ICallServiceAdapter; + +/** + * Provides methods for ICallService implementations to interact with the system phone app. + * TODO(santoscordon): Need final public-facing comments in this file. + */ +public final class CallServiceAdapter { + private final ICallServiceAdapter mAdapter; + + /** + * {@hide} + */ + public CallServiceAdapter(ICallServiceAdapter adapter) { + mAdapter = adapter; + } + + /** + * Receives confirmation of a call service's ability to place a call. This method is used in + * response to {@link CallService#isCompatibleWith}. + * + * @param callId The identifier of the call for which compatibility is being received. This ID + * should correspond to the ID given as part of the call information in + * {@link CallService#isCompatibleWith}. + * @param isCompatible True if the call service can place the call. + */ + public void setIsCompatibleWith(String callId, boolean isCompatible) { + try { + mAdapter.setIsCompatibleWith(callId, isCompatible); + } catch (RemoteException e) { + } + } + + /** + * Provides Telecomm with the details of an incoming call. An invocation of this method must + * follow {@link CallService#setIncomingCallId} and use the call ID specified therein. Upon + * the invocation of this method, Telecomm will bring up the incoming-call interface where the + * user can elect to answer or reject a call. + * + * @param callInfo The details of the relevant call. + */ + public void notifyIncomingCall(CallInfo callInfo) { + try { + mAdapter.notifyIncomingCall(callInfo); + } catch (RemoteException e) { + } + } + + /** + * Tells Telecomm that an attempt to place the specified outgoing call succeeded. + * TODO(santoscordon): Consider adding a CallState parameter in case this outgoing call is + * somehow no longer in the DIALING state. + * + * @param callId The ID of the outgoing call. + */ + public void handleSuccessfulOutgoingCall(String callId) { + try { + mAdapter.handleSuccessfulOutgoingCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Tells Telecomm that an attempt to place the specified outgoing call failed. + * + * @param callId The ID of the outgoing call. + * @param errorMessage The error associated with the failed call attempt. + */ + public void handleFailedOutgoingCall(String callId, String errorMessage) { + try { + mAdapter.handleFailedOutgoingCall(callId, errorMessage); + } catch (RemoteException e) { + } + } + + /** + * Sets a call's state to active (e.g., an ongoing call where two parties can actively + * communicate). + * + * @param callId The unique ID of the call whose state is changing to active. + */ + public void setActive(String callId) { + try { + mAdapter.setActive(callId); + } catch (RemoteException e) { + } + } + + /** + * Sets a call's state to ringing (e.g., an inbound ringing call). + * + * @param callId The unique ID of the call whose state is changing to ringing. + */ + public void setRinging(String callId) { + try { + mAdapter.setRinging(callId); + } catch (RemoteException e) { + } + } + + /** + * Sets a call's state to dialing (e.g., dialing an outbound call). + * + * @param callId The unique ID of the call whose state is changing to dialing. + */ + public void setDialing(String callId) { + try { + mAdapter.setDialing(callId); + } catch (RemoteException e) { + } + } + + /** + * Sets a call's state to disconnected. + * + * @param callId The unique ID of the call whose state is changing to disconnected. + * @param disconnectCause The reason for the disconnection, any of + * {@link android.telephony.DisconnectCause}. + * @param disconnectMessage Optional call-service-provided message about the disconnect. + */ + public void setDisconnected(String callId, int disconnectCause, String disconnectMessage) { + try { + mAdapter.setDisconnected(callId, disconnectCause, disconnectMessage); + } catch (RemoteException e) { + } + } + + /** + * Sets a call's state to be on hold. + * + * @param callId - The unique ID of the call whose state is changing to be on hold. + */ + public void setOnHold(String callId) { + try { + mAdapter.setOnHold(callId); + } catch (RemoteException e) { + } + } + + +} diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/telecomm/java/android/telecomm/CallServiceDescriptor.aidl new file mode 100644 index 0000000..f517c73 --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceDescriptor.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +parcelable CallServiceDescriptor; diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.java b/telecomm/java/android/telecomm/CallServiceDescriptor.java new file mode 100644 index 0000000..dec3791 --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceDescriptor.java @@ -0,0 +1,232 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.content.ComponentName; +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.Locale; +import java.util.UUID; + +/** + * An immutable object containing information about a given {@link CallService}. Instances are + * created using the enclosed {@link Builder}. + */ +public final class CallServiceDescriptor implements Parcelable { + private static final String TAG = CallServiceDescriptor.class.getSimpleName(); + + /** + * A placeholder value indicating an invalid network type. + * @hide + */ + private static final int FLAG_INVALID = 0; + + /** + * Indicates that the device must be connected to a Wi-Fi network in order for the backing + * {@link CallService} to be used. + */ + public static final int FLAG_WIFI = 0x01; + + /** + * Indicates that the device must be connected to a cellular PSTN network in order for the + * backing {@link CallService} to be used. + */ + public static final int FLAG_PSTN = 0x02; + + /** + * Indicates that the device must be connected to a cellular data network in order for the + * backing {@link CallService} to be used. + */ + public static final int FLAG_MOBILE = 0x04; + + /** + * Represents all of the defined FLAG_ constants so validity can be easily checked. + * @hide + */ + public static final int FLAG_ALL = FLAG_WIFI | FLAG_PSTN | FLAG_MOBILE; + + /** + * A unique ID used to identify a given instance. + */ + private final String mCallServiceId; + + /** + * The {@link ComponentName} of the {@link CallService} implementation which this is describing. + */ + private final ComponentName mComponentName; + + /** + * The type of connection that the {@link CallService} requires; will be one of the FLAG_* + * constants defined in this class. + */ + private final int mNetworkType; + + private CallServiceDescriptor( + String callServiceId, + ComponentName componentName, + int networkType) { + + mCallServiceId = callServiceId; + mComponentName = componentName; + mNetworkType = networkType; + } + + /** + * @return The ID used to identify this {@link CallService}. + */ + public String getCallServiceId() { + return mCallServiceId; + } + + /** + * @return The {@link ComponentName} of the {@link CallService}. + */ + public ComponentName getServiceComponent() { + return mComponentName; + } + + /** + * @return The network type required by the {@link CallService} to place a call. + */ + public int getNetworkType() { + return mNetworkType; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof CallServiceDescriptor)) { + return false; + } + CallServiceDescriptor descriptor = (CallServiceDescriptor) obj; + return mCallServiceId.equals(descriptor.mCallServiceId) && + mComponentName.equals(descriptor.mComponentName) && + mNetworkType == descriptor.mNetworkType; + } + + @Override + public String toString() { + return String.format(Locale.US, "[%s, component: %s]", + CallServiceDescriptor.class.getSimpleName(), + mComponentName == null ? "null" : mComponentName.flattenToShortString()); + } + + /** + * @param context {@link Context} to use for the construction of the {@link Builder}. + * @return A new {@link Builder} instance. + */ + public static Builder newBuilder(Context context) { + return new Builder(context); + } + + /** + * Creates {@link CallServiceDescriptor} instances. Builders should be created with the + * {@link CallServiceDescriptor#newBuilder(Context)} method. + */ + public static class Builder { + /** The {@link Context} to use to verify {@link ComponentName} ownership. */ + private Context mContext; + + /** The {@link ComponentName} pointing to the backing {@link CallService}. */ + private ComponentName mComponentName; + + /** The required network type that the {@link CallService} needs. */ + private int mNetworkType = FLAG_INVALID; + + private Builder(Context context) { + mContext = context; + } + + /** + * Set which {@link CallService} this {@link CallServiceDescriptor} is describing. + * + * @param callServiceClass The {@link CallService} class + * @return This {@link Builder} for method chaining. + */ + public Builder setCallService(Class<? extends CallService> callServiceClass) { + mComponentName = new ComponentName(mContext, callServiceClass); + return this; + } + + /** + * Which network type the backing {@link CallService} requires. This must be one of the + * {@link CallServiceDescriptor}.TYPE_* fields. + * + * @param networkType Which network type the backing {@link CallService} requires. + * @return This {@link Builder} for method chaining. + */ + public Builder setNetworkType(int networkType) { + mNetworkType = networkType; + return this; + } + + /** + * @return A constructed {@link CallServiceDescriptor} object. + */ + public CallServiceDescriptor build() { + // STOPSHIP: Verify validity of ComponentName (permissions, intents, etc) + + // Make sure that they passed in a valid network flag combination + if (mNetworkType == FLAG_INVALID || ((mNetworkType & FLAG_ALL) == 0)) { + + Log.wtf(TAG, "Invalid network type for " + mComponentName); + // Revert them back to TYPE_INVALID so it won't be considered. + mNetworkType = FLAG_INVALID; + } + + // TODO: Should we use a sha1 of the ComponentName? Would prevent duplicates. + return new CallServiceDescriptor( + UUID.randomUUID().toString(), mComponentName, mNetworkType); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mCallServiceId); + dest.writeParcelable(mComponentName, 0); + dest.writeInt(mNetworkType); + } + + public static final Creator<CallServiceDescriptor> CREATOR = + new Creator<CallServiceDescriptor>() { + @Override + public CallServiceDescriptor createFromParcel(Parcel source) { + String id = source.readString(); + ComponentName componentName = source.readParcelable( + CallServiceDescriptor.class.getClassLoader()); + int networkType = source.readInt(); + + return new CallServiceDescriptor(id, componentName, networkType); + } + + @Override + public CallServiceDescriptor[] newArray(int size) { + return new CallServiceDescriptor[size]; + } + }; +} diff --git a/telecomm/java/android/telecomm/CallServiceLookupResponse.java b/telecomm/java/android/telecomm/CallServiceLookupResponse.java new file mode 100644 index 0000000..dd35a24 --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceLookupResponse.java @@ -0,0 +1,51 @@ +/* + * 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.RemoteException; + +import com.android.internal.telecomm.ICallServiceLookupResponse; + +import java.util.List; + +/** + * Used by {@link CallServiceProvider} to return a list of {@link CallServiceDescriptor}s. + */ +public final class CallServiceLookupResponse { + private final ICallServiceLookupResponse mResponse; + + /** + * {@hide} + */ + public CallServiceLookupResponse(ICallServiceLookupResponse response) { + mResponse = response; + } + + /** + * Passes the sorted list of preferred {@link CallServiceDescriptor}s back to Telecomm. Used + * in the context of attempting to place a pending outgoing call. + * + * @param callServiceDescriptors The set of call-service descriptors from + * {@link CallServiceProvider}. + */ + public void setCallServiceDescriptors(List<CallServiceDescriptor> callServiceDescriptors) { + try { + mResponse.setCallServiceDescriptors(callServiceDescriptors); + } catch (RemoteException e) { + } + } +} diff --git a/telecomm/java/android/telecomm/CallServiceProvider.java b/telecomm/java/android/telecomm/CallServiceProvider.java new file mode 100644 index 0000000..c50334a --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceProvider.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 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.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; + +import com.android.internal.telecomm.ICallServiceLookupResponse; +import com.android.internal.telecomm.ICallServiceProvider; + +/** + * Base implementation of a call service provider which extends {@link Service}. This class + * should be extended by an app that wants to supply phone calls to be handled and managed by + * the device's in-call interface. All method-calls from the framework to the call service provider + * are passed through to the main thread for before executing the overriden methods of + * CallServiceProvider. + * + * TODO(santoscordon): Improve paragraph above once the final design is in place. Needs more + * about how this can be used. + */ +public abstract class CallServiceProvider extends Service { + + /** + * Default Handler used to consolidate binder method calls onto a single thread. + */ + private final class CallServiceProviderMessageHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_LOOKUP_CALL_SERVICES: + CallServiceLookupResponse response = + new CallServiceLookupResponse((ICallServiceLookupResponse) msg.obj); + lookupCallServices(response); + break; + } + } + } + + /** + * Default ICallServiceProvider implementation provided to CallsManager via {@link #onBind}. + */ + private final class CallServiceProviderWrapper extends ICallServiceProvider.Stub { + /** {@inheritDoc} */ + @Override + public void lookupCallServices(ICallServiceLookupResponse callServiceLookupResponse) { + Message message = mMessageHandler.obtainMessage( + MSG_LOOKUP_CALL_SERVICES, callServiceLookupResponse); + message.sendToTarget(); + } + } + + // Only used internally by this class. + // Binder method calls on this service can occur on multiple threads. These messages are used + // in conjunction with {@link #mMessageHandler} to ensure that all callbacks are handled on a + // single thread. Keeping it on a single thread allows CallService implementations to avoid + // needing multi-threaded code in their own callback routines. + private static final int MSG_LOOKUP_CALL_SERVICES = 1; + + /** + * Message handler for consolidating binder callbacks onto a single thread. + * See {@link CallServiceProviderMessageHandler}. + */ + private final CallServiceProviderMessageHandler mMessageHandler; + + /** + * Default binder implementation of {@link ICallServiceProvider} interface. + */ + private final CallServiceProviderWrapper mBinder; + + /** + * Protected constructor called only by subclasses creates the binder interface and + * single-threaded message handler. + */ + protected CallServiceProvider() { + mMessageHandler = new CallServiceProviderMessageHandler(); + mBinder = new CallServiceProviderWrapper(); + } + + /** {@inheritDoc} */ + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** + * Initiates the process to retrieve the list of {@link CallServiceDescriptor}s implemented by + * this provider. + * + * @param response The response object through which the list of call services is sent. + */ + public abstract void lookupCallServices(CallServiceLookupResponse response); +} diff --git a/telecomm/java/android/telecomm/CallServiceSelector.java b/telecomm/java/android/telecomm/CallServiceSelector.java new file mode 100644 index 0000000..c9c6ff6 --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceSelector.java @@ -0,0 +1,152 @@ +/* + * 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.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; + +import com.android.internal.os.SomeArgs; +import com.android.internal.telecomm.ICallServiceSelector; +import com.android.internal.telecomm.ICallServiceSelectorAdapter; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * Allows for the organization of {@link CallService}s for outbound calls. Given a call and list of + * {@link CallService} IDs, order the list in terms of priority and return it using + * {@link #select(CallInfo, List)}. + */ +public abstract class CallServiceSelector extends Service { + private static final int MSG_SET_CALL_SERVICE_SELECTOR_ADAPTER = 0; + private static final int MSG_SELECT = 1; + + private final HashMap<String, CallInfo> mCalls = new HashMap<String, CallInfo>(); + + /** Handler to move client-bound method calls to the main thread. */ + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_CALL_SERVICE_SELECTOR_ADAPTER: + mAdapter = new CallServiceSelectorAdapter( + (ICallServiceSelectorAdapter) msg.obj); + onAdapterAttached(mAdapter); + break; + case MSG_SELECT: + SomeArgs args = (SomeArgs) msg.obj; + try { + select((CallInfo) args.arg1, (List<CallServiceDescriptor>) args.arg2); + } finally { + args.recycle(); + } + break; + } + } + }; + + /** Manages the binder calls so that the implementor does not need to deal with it. */ + private final class CallServiceSelectorBinder extends ICallServiceSelector.Stub { + @Override + public void setCallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) { + mHandler.obtainMessage(MSG_SET_CALL_SERVICE_SELECTOR_ADAPTER, adapter) + .sendToTarget(); + } + + @Override + public void select(CallInfo callInfo, List<CallServiceDescriptor> descriptors) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callInfo; + args.arg2 = descriptors; + mHandler.obtainMessage(MSG_SELECT, args).sendToTarget(); + } + + @Override + public void onCallUpdated(CallInfo callInfo) { + mCalls.put(callInfo.getId(), callInfo); + } + + @Override + public void onCallRemoved(String callId) { + mCalls.remove(callId); + } + } + + private final CallServiceSelectorBinder mBinder; + + private CallServiceSelectorAdapter mAdapter = null; + + protected CallServiceSelector() { + mBinder = new CallServiceSelectorBinder(); + } + + @Override + public final IBinder onBind(Intent intent) { + return mBinder; + } + + /** + * Returns a list of all calls managed by this selector. + */ + protected final Collection<CallInfo> getCalls() { + return Collections.unmodifiableCollection(mCalls.values()); + } + + /** + * @return The attached {@link CallServiceSelectorAdapter} if attached, or null otherwise. + */ + protected final CallServiceSelectorAdapter getAdapter() { + return mAdapter; + } + + /** + * Cancel the outgoing call. Any subsequent calls to {@link #select(CallInfo, List)} will be + * ignored. + * + * @param callInfo The call to canceled. + */ + protected final void cancelOutgoingCall(CallInfo callInfo) { + getAdapter().cancelOutgoingCall(callInfo.getId()); + } + + /** + * Lifecycle callback which is called when this {@link CallServiceSelector} has been attached + * to a {@link CallServiceSelectorAdapter}, indicating {@link #getAdapter()} is now safe to use. + * + * @param adapter The adapter now attached to this call service selector. + */ + protected void onAdapterAttached(CallServiceSelectorAdapter adapter) { + } + + /** + * Given a list of {@link CallServiceDescriptor}s, order them into a prioritized list and return + * them through + * {@link CallServiceSelectorAdapter#setSelectedCallServices(String,List)}. + * + * @param callInfo The call being placed using the {@link CallService}s. + * @param descriptors The descriptors of the available {@link CallService}s with which to place + * the call. + */ + protected abstract void select(CallInfo callInfo, List<CallServiceDescriptor> descriptors); +} diff --git a/telecomm/java/android/telecomm/CallServiceSelectorAdapter.java b/telecomm/java/android/telecomm/CallServiceSelectorAdapter.java new file mode 100644 index 0000000..4d2e8aa --- /dev/null +++ b/telecomm/java/android/telecomm/CallServiceSelectorAdapter.java @@ -0,0 +1,84 @@ +/* + * 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.os.RemoteException; +import android.telecomm.CallServiceDescriptor; + +import com.android.internal.telecomm.ICallServiceSelectorAdapter; + +import java.util.List; + +/** + * Provides methods for ICallServiceSelector implementations to interact with Telecomm. + */ +public final class CallServiceSelectorAdapter { + private final ICallServiceSelectorAdapter mAdapter; + + /** + * {@hide} + */ + public CallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) { + mAdapter = adapter; + } + + /** + * Records the sorted set of call services that are preferred by the corresponding + * call-service selector. + * + * @param callId The ID of the call to complete. + * @param selectedCallServiceDescriptors The prioritized list of preferred call-service + * descriptors to use for completing the call. + */ + public void setSelectedCallServices( + String callId, + List<CallServiceDescriptor> selectedCallServiceDescriptors) { + try { + mAdapter.setSelectedCallServices(callId, selectedCallServiceDescriptors); + } catch (RemoteException e) { + } + } + + /** + * Cancels the specified outgoing call. + * + * @param callId The ID of the call to cancel. + */ + public void cancelOutgoingCall(String callId) { + try { + mAdapter.cancelOutgoingCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Associates handoff information with an ongoing call. Calls can switch from one call service + * to another. Setting handle to a non-null value marks the call as switchable. + * + * @param callId The ID of the call to set handoff information for. + * @param handle The handle used to place the call when switching. + * @param extras Optional extra that's attached to the call. + */ + public void setHandoffInfo(String callId, Uri handle, Bundle extras) { + try { + mAdapter.setHandoffInfo(callId, handle, extras); + } catch (RemoteException e) { + } + } +} diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java new file mode 100644 index 0000000..152c202 --- /dev/null +++ b/telecomm/java/android/telecomm/CallState.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +/** + * Defines call-state constants of the different states in which a call can exist. Although states + * have the notion of normal transitions, due to the volatile nature of telephony systems, code + * that uses these states should be resilient to unexpected state changes outside of what is + * considered traditional. + */ +public enum CallState { + /** + * Indicates that a call is new and not connected. This is used as the default state internally + * within Telecomm and should not be used between Telecomm and call services. Call services are + * not expected to ever interact with NEW calls, but {@link InCallService}s will see calls in + * this state. + */ + NEW, + + /** + * Indicates that a call is outgoing and in the dialing state. A call transitions to this state + * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this + * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED} + * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user). + */ + DIALING, + + /** + * Indicates that a call is incoming and the user still has the option of answering, rejecting, + * or doing nothing with the call. This state is usually associated with some type of audible + * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED} + * otherwise. + */ + RINGING, + + /** + * Indicates that the call is active but in a "post-dial" state where Telecomm is now sending + * some dual-tone multi-frequency signaling (DTMF) tones appended to the dialed number. Normal + * transitions are to {@link #POST_DIAL_WAIT} when the post-dial string requires user + * confirmation to proceed, {@link #ACTIVE} when the post-dial tones are completed, or + * {@link #DISCONNECTED}. + */ + POST_DIAL, + + /** + * Indicates that the call was in the {@link #POST_DIAL} state but is now waiting for user + * confirmation before the remaining digits can be sent. Normal transitions are to + * {@link #POST_DIAL} when the user asks Telecomm to proceed with the post-dial sequence. + */ + POST_DIAL_WAIT, + + /** + * Indicates that a call is currently connected to another party and a communication channel is + * open between them. The normal transition to this state is by the user answering a + * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party. + */ + ACTIVE, + + /** + * Indicates that the call is currently on hold. In this state, the call is not terminated + * but no communication is allowed until the call is no longer on hold. The typical transition + * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing + * an action, such as clicking the hold button. + */ + ON_HOLD, + + /** + * Indicates that a call is currently disconnected. All states can transition to this state + * by the call service giving notice that the connection has been severed. When the user + * explicitly ends a call, it will not transition to this state until the call service confirms + * the disconnection or communication was lost to the call service currently responsible for + * this call (e.g., call service crashes). + */ + DISCONNECTED, + + /** + * Indicates that the call was attempted (mostly in the context of outgoing, at least at the + * time of writing) but cancelled before it was successfully connected. + * @hide + */ + ABORTED; +} diff --git a/telecomm/java/android/telecomm/GatewayInfo.aidl b/telecomm/java/android/telecomm/GatewayInfo.aidl new file mode 100644 index 0000000..d59e9b4 --- /dev/null +++ b/telecomm/java/android/telecomm/GatewayInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +parcelable GatewayInfo; diff --git a/telecomm/java/android/telecomm/GatewayInfo.java b/telecomm/java/android/telecomm/GatewayInfo.java new file mode 100644 index 0000000..b95e6b6 --- /dev/null +++ b/telecomm/java/android/telecomm/GatewayInfo.java @@ -0,0 +1,106 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +/** + * When calls are made, they may contain gateway information for services which route phone calls + * through their own service/numbers. The data consists of a number to call and the package name of + * the service. This data is used in two ways: + * <ol> + * <li> Call the appropriate routing number + * <li> Display information about how the call is being routed to the user + * </ol> + */ +public class GatewayInfo implements Parcelable { + + private final String mGatewayProviderPackageName; + private final Uri mGatewayHandle; + private final Uri mOriginalHandle; + + /** @hide */ + public GatewayInfo(String packageName, Uri gatewayUri, Uri originalHandle) { + mGatewayProviderPackageName = packageName; + mGatewayHandle = gatewayUri; + mOriginalHandle = originalHandle; + } + + /** + * Package name of the gateway provider service. used to place the call with. + */ + public String getGatewayProviderPackageName() { + return mGatewayProviderPackageName; + } + + /** + * Gateway provider handle to use when actually placing the call. + */ + public Uri getGatewayHandle() { + return mGatewayHandle; + } + + /** + * The actual call handle that the user is trying to connect to via the gateway. + */ + public Uri getOriginalHandle() { + return mOriginalHandle; + } + + public boolean isEmpty() { + return TextUtils.isEmpty(mGatewayProviderPackageName) || mGatewayHandle == null; + } + + /** Implement the Parcelable interface */ + public static final Parcelable.Creator<GatewayInfo> CREATOR = + new Parcelable.Creator<GatewayInfo> () { + + @Override + public GatewayInfo createFromParcel(Parcel source) { + String gatewayPackageName = source.readString(); + Uri gatewayUri = Uri.CREATOR.createFromParcel(source); + Uri originalHandle = Uri.CREATOR.createFromParcel(source); + return new GatewayInfo(gatewayPackageName, gatewayUri, originalHandle); + } + + @Override + public GatewayInfo[] newArray(int size) { + return new GatewayInfo[size]; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeString(mGatewayProviderPackageName); + mGatewayHandle.writeToParcel(destination, 0); + mOriginalHandle.writeToParcel(destination, 0); + } +} diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java new file mode 100644 index 0000000..e41d3f6 --- /dev/null +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -0,0 +1,199 @@ +/* + * 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.RemoteException; + +import com.android.internal.telecomm.IInCallAdapter; + +/** + * Receives commands from {@link InCallService} implementations which should be executed by + * Telecomm. When Telecomm binds to a {@link InCallService}, an instance of this class is given to + * the in-call service through which it can manipulate live (active, dialing, ringing) calls. When + * the in-call service is notified of new calls ({@link InCallService#addCall}), it can use the + * given call IDs to execute commands such as {@link #answerCall} for incoming calls or + * {@link #disconnectCall} for active calls the user would like to end. Some commands are only + * appropriate for calls in certain states; please consult each method for such limitations. + * TODO(santoscordon): Needs more/better comments once the API is finalized. + * TODO(santoscordon): Specify the adapter will stop functioning when there are no more calls. + */ +public final class InCallAdapter { + private final IInCallAdapter mAdapter; + + /** + * {@hide} + */ + public InCallAdapter(IInCallAdapter adapter) { + mAdapter = adapter; + } + + /** + * Instructs Telecomm to answer the specified call. + * + * @param callId The identifier of the call to answer. + */ + public void answerCall(String callId) { + try { + mAdapter.answerCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to reject the specified call. + * TODO(santoscordon): Add reject-with-text-message parameter when that feature + * is ported over. + * + * @param callId The identifier of the call to reject. + */ + public void rejectCall(String callId) { + try { + mAdapter.rejectCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to disconnect the specified call. + * + * @param callId The identifier of the call to disconnect. + */ + public void disconnectCall(String callId) { + try { + mAdapter.disconnectCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to put the specified call on hold. + * + * @param callId The identifier of the call to put on hold. + */ + public void holdCall(String callId) { + try { + mAdapter.holdCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to release the specified call from hold. + * + * @param callId The identifier of the call to release from hold. + */ + public void unholdCall(String callId) { + try { + mAdapter.unholdCall(callId); + } catch (RemoteException e) { + } + } + + /** + * Mute the microphone. + * + * @param shouldMute True if the microphone should be muted. + */ + public void mute(boolean shouldMute) { + try { + mAdapter.mute(shouldMute); + } catch (RemoteException e) { + } + } + + /** + * Sets the audio route (speaker, bluetooth, etc...). See {@link CallAudioState}. + * + * @param route The audio route to use. + */ + public void setAudioRoute(int route) { + try { + mAdapter.setAudioRoute(route); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to play a dual-tone multi-frequency signaling (DTMF) tone in a call. + * + * Any other currently playing DTMF tone in the specified call is immediately stopped. + * + * @param callId The unique ID of the call in which the tone will be played. + * @param digit A character representing the DTMF digit for which to play the tone. This + * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. + */ + public void playDtmfTone(String callId, char digit) { + try { + mAdapter.playDtmfTone(callId, digit); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to stop any dual-tone multi-frequency signaling (DTMF) tone currently + * playing. + * + * DTMF tones are played by calling {@link #playDtmfTone(String,char)}. If no DTMF tone is + * currently playing, this method will do nothing. + * + * @param callId The unique ID of the call in which any currently playing tone will be stopped. + */ + public void stopDtmfTone(String callId) { + try { + mAdapter.stopDtmfTone(callId); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to continue playing a post-dial DTMF string. + * + * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, + * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. + * While these tones are playing, Telecomm will notify the {@link InCallService} that the call + * is in the {@link InCallService#setPostDial(String,String)} state. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, Telecomm + * will temporarily pause playing the tones for a pre-defined period of time. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, Telecomm + * will pause playing the tones and notify the {@link InCallService} that the call is in the + * {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue + * the postdial sequence, the {@link InCallService} should invoke the + * {@link #postDialContinue(String)} method. + * + * @param callId The unique ID of the call for which postdial string playing should continue. + */ + public void postDialContinue(String callId) { + try { + mAdapter.postDialContinue(callId); + } catch (RemoteException e) { + } + } + + /** + * Instructs Telecomm to handoff the call to another call service. + * + * @param callId The identifier of the call to handoff. + */ + public void handoffCall(String callId) { + try { + mAdapter.handoffCall(callId); + } catch (RemoteException e) { + } + } +} diff --git a/telecomm/java/android/telecomm/InCallCall.aidl b/telecomm/java/android/telecomm/InCallCall.aidl new file mode 100644 index 0000000..be2cdf8 --- /dev/null +++ b/telecomm/java/android/telecomm/InCallCall.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +parcelable InCallCall; diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java new file mode 100644 index 0000000..c3b2ae7 --- /dev/null +++ b/telecomm/java/android/telecomm/InCallCall.java @@ -0,0 +1,159 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.DisconnectCause; + +import java.util.Date; +import java.util.UUID; + +/** + * Information about a call that is used between InCallService and Telecomm. + */ +public final class InCallCall implements Parcelable { + private final String mId; + private final CallState mState; + private final int mDisconnectCause; + private final int mCapabilities; + private final long mConnectTimeMillis; + private final Uri mHandle; + private final GatewayInfo mGatewayInfo; + private final CallServiceDescriptor mCurrentCallServiceDescriptor; + private final CallServiceDescriptor mHandoffCallServiceDescriptor; + + /** @hide */ + public InCallCall( + String id, + CallState state, + int disconnectCause, + int capabilities, + long connectTimeMillis, + Uri handle, + GatewayInfo gatewayInfo, + CallServiceDescriptor descriptor, + CallServiceDescriptor handoffDescriptor) { + mId = id; + mState = state; + mDisconnectCause = disconnectCause; + mCapabilities = capabilities; + mConnectTimeMillis = connectTimeMillis; + mHandle = handle; + mGatewayInfo = gatewayInfo; + mCurrentCallServiceDescriptor = descriptor; + mHandoffCallServiceDescriptor = handoffDescriptor; + } + + /** The unique ID of the call. */ + public String getId() { + return mId; + } + + /** The current state of the call. */ + public CallState getState() { + return mState; + } + + /** + * Reason for disconnection, values are defined in {@link DisconnectCause}. Valid when call + * state is {@link CallState#DISCONNECTED}. + */ + public int getDisconnectCause() { + return mDisconnectCause; + } + + // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}. + public int getCapabilities() { + return mCapabilities; + } + + /** The time that the call switched to the active state. */ + public long getConnectTimeMillis() { + return mConnectTimeMillis; + } + + /** The endpoint to which the call is connected. */ + public Uri getHandle() { + return mHandle; + } + + /** Gateway information for the call. */ + public GatewayInfo getGatewayInfo() { + return mGatewayInfo; + } + + /** The descriptor for the call service currently routing this call. */ + public CallServiceDescriptor getCurrentCallServiceDescriptor() { + return mCurrentCallServiceDescriptor; + } + + /** + * The descriptor for the call service that this call is being switched to, null if handoff is + * not in progress. + */ + public CallServiceDescriptor getHandoffCallServiceDescriptor() { + return mHandoffCallServiceDescriptor; + } + + /** Responsible for creating InCallCall objects for deserialized Parcels. */ + public static final Parcelable.Creator<InCallCall> CREATOR = + new Parcelable.Creator<InCallCall> () { + @Override + public InCallCall createFromParcel(Parcel source) { + String id = source.readString(); + CallState state = CallState.valueOf(source.readString()); + int disconnectCause = source.readInt(); + int capabilities = source.readInt(); + long connectTimeMillis = source.readLong(); + ClassLoader classLoader = InCallCall.class.getClassLoader(); + Uri handle = source.readParcelable(classLoader); + GatewayInfo gatewayInfo = source.readParcelable(classLoader); + CallServiceDescriptor descriptor = source.readParcelable(classLoader); + CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader); + return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis, + handle, gatewayInfo, descriptor, handoffDescriptor); + } + + @Override + public InCallCall[] newArray(int size) { + return new InCallCall[size]; + } + }; + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** Writes InCallCall object into a Parcel. */ + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeString(mId); + destination.writeString(mState.name()); + destination.writeInt(mDisconnectCause); + destination.writeInt(mCapabilities); + destination.writeLong(mConnectTimeMillis); + destination.writeParcelable(mHandle, 0); + destination.writeParcelable(mGatewayInfo, 0); + destination.writeParcelable(mCurrentCallServiceDescriptor, 0); + destination.writeParcelable(mHandoffCallServiceDescriptor, 0); + } +} diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java new file mode 100644 index 0000000..63b2020 --- /dev/null +++ b/telecomm/java/android/telecomm/InCallService.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2013 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.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; + +import com.android.internal.os.SomeArgs; +import com.android.internal.telecomm.IInCallAdapter; +import com.android.internal.telecomm.IInCallService; + +/** + * This service is implemented by any app that wishes to provide the user-interface for managing + * phone calls. Telecomm binds to this service while there exists a live (active or incoming) + * call, and uses it to notify the in-call app of any live and and recently disconnected calls. + * TODO(santoscordon): Needs more/better description of lifecycle once the interface is better + * defined. + * TODO(santoscordon): What happens if two or more apps on a given device implement this interface? + */ +public abstract class InCallService extends Service { + private static final int MSG_SET_IN_CALL_ADAPTER = 1; + private static final int MSG_ADD_CALL = 2; + private static final int MSG_UPDATE_CALL = 3; + private static final int MSG_SET_POST_DIAL = 4; + private static final int MSG_SET_POST_DIAL_WAIT = 5; + private static final int MSG_ON_AUDIO_STATE_CHANGED = 6; + + /** Default Handler used to consolidate binder method calls onto a single thread. */ + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_IN_CALL_ADAPTER: + mAdapter = new InCallAdapter((IInCallAdapter) msg.obj); + onAdapterAttached(mAdapter); + break; + case MSG_ADD_CALL: + addCall((InCallCall) msg.obj); + break; + case MSG_UPDATE_CALL: + updateCall((InCallCall) msg.obj); + break; + case MSG_SET_POST_DIAL: { + SomeArgs args = (SomeArgs) msg.obj; + try { + String callId = (String) args.arg1; + String remaining = (String) args.arg2; + setPostDial(callId, remaining); + } finally { + args.recycle(); + } + break; + } + case MSG_SET_POST_DIAL_WAIT: { + SomeArgs args = (SomeArgs) msg.obj; + try { + String callId = (String) args.arg1; + String remaining = (String) args.arg2; + setPostDialWait(callId, remaining); + } finally { + args.recycle(); + } + break; + } + case MSG_ON_AUDIO_STATE_CHANGED: + onAudioStateChanged((CallAudioState) msg.obj); + break; + default: + break; + } + } + }; + + /** Manages the binder calls so that the implementor does not need to deal with it. */ + private final class InCallServiceBinder extends IInCallService.Stub { + /** {@inheritDoc} */ + @Override + public void setInCallAdapter(IInCallAdapter inCallAdapter) { + mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget(); + } + + /** {@inheritDoc} */ + @Override + public void addCall(InCallCall call) { + mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget(); + } + + /** {@inheritDoc} */ + @Override + public void updateCall(InCallCall call) { + mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget(); + } + + @Override + public void setPostDial(String callId, String remaining) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = remaining; + mHandler.obtainMessage(MSG_SET_POST_DIAL, args).sendToTarget(); + } + + @Override + public void setPostDialWait(String callId, String remaining) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = remaining; + mHandler.obtainMessage(MSG_SET_POST_DIAL_WAIT, args).sendToTarget(); + } + + /** {@inheritDoc} */ + @Override + public void onAudioStateChanged(CallAudioState audioState) { + mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, audioState).sendToTarget(); + } + } + + private final InCallServiceBinder mBinder; + + private InCallAdapter mAdapter; + + protected InCallService() { + mBinder = new InCallServiceBinder(); + } + + @Override + public final IBinder onBind(Intent intent) { + return mBinder; + } + + /** + * @return The attached {@link CallServiceSelectorAdapter} if attached, or null otherwise. + */ + protected final InCallAdapter getAdapter() { + return mAdapter; + } + + /** + * Lifecycle callback which is called when this {@link InCallService} has been attached + * to a {@link InCallAdapter}, indicating {@link #getAdapter()} is now safe to use. + * + * @param adapter The adapter now attached to this in-call service. + */ + protected void onAdapterAttached(InCallAdapter adapter) { + } + + /** + * Indicates to the in-call app that a new call has been created and an appropriate + * user-interface should be built and shown to notify the user. + * + * @param call Information about the new call. + */ + protected abstract void addCall(InCallCall call); + + /** + * Call when information about a call has changed. + * + * @param call Information about the new call. + */ + protected abstract void updateCall(InCallCall call); + + /** + * Indicates to the in-call app that the specified call is active but in a "post-dial" state + * where Telecomm is now sending some dual-tone multi-frequency signaling (DTMF) tones appended + * to the dialed number. Normal transitions are to {@link #setPostDialWait(String,String)} when + * the post-dial string requires user confirmation to proceed, and {@link CallState#ACTIVE} when + * the post-dial tones are completed. + * + * @param callId The identifier of the call changing state. + * @param remaining The remaining postdial string to be dialed. + */ + protected abstract void setPostDial(String callId, String remaining); + + /** + * Indicates to the in-call app that the specified call was in the + * {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the + * remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)} + * when the user asks Telecomm to proceed with the post-dial sequence and the in-call app + * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String)}. + * + * @param callId The identifier of the call changing state. + * @param remaining The remaining postdial string to be dialed. + */ + protected abstract void setPostDialWait(String callId, String remaining); + + /** + * Called when the audio state changes. + * + * @param audioState The new {@link CallAudioState}. + */ + protected abstract void onAudioStateChanged(CallAudioState audioState); +} diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java new file mode 100644 index 0000000..8300c92 --- /dev/null +++ b/telecomm/java/android/telecomm/TelecommConstants.java @@ -0,0 +1,97 @@ +/* + * 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.telephony.TelephonyManager; + +/** + * Defines constants for use with the Telecomm system. + */ +public final class TelecommConstants { + /** + * <p>Activity action: Starts the UI for handing an incoming call. This intent starts the + * in-call UI by notifying the Telecomm system that an incoming call exists for a specific call + * service (see {@link android.telecomm.CallService}). Telecomm reads the Intent extras to find + * and bind to the appropriate {@link android.telecomm.CallService} which Telecomm will + * ultimately use to control and get information about the call.</p> + * + * <p>Input: get*Extra field {@link #EXTRA_CALL_SERVICE_DESCRIPTOR} contains the component name + * of the {@link android.telecomm.CallService} that Telecomm should bind to. Telecomm will then + * ask the call service for more information about the call prior to showing any UI. + * + * TODO(santoscordon): Needs permissions. + * TODO(santoscordon): Consider moving this into a simple method call on a system service. + */ + public static final String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL"; + + /** + * The service action used to bind to {@link CallServiceProvider} implementations. + */ + public static final String ACTION_CALL_SERVICE_PROVIDER = CallServiceProvider.class.getName(); + + /** + * The service action used to bind to {@link CallService} implementations. + */ + public static final String ACTION_CALL_SERVICE = CallService.class.getName(); + + /** + * The service action used to bind to {@link CallServiceSelector} implementations. + */ + public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName(); + + /** + * Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that + * describes the call service to use for the incoming call. + */ + public static final String EXTRA_CALL_SERVICE_DESCRIPTOR = + "android.intent.extra.CALL_SERVICE_DESCRIPTOR"; + + /** + * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains + * metadata about the call. This {@link Bundle} will be returned to the {@link CallService} as + * part of {@link CallService#setIncomingCallId(String,Bundle)}. + */ + public static final String EXTRA_INCOMING_CALL_EXTRAS = + "android.intent.extra.INCOMING_CALL_EXTRAS"; + + /** + * Optional extra for {@link TelephonyManager#ACTION_PHONE_STATE_CHANGED} containing the + * disconnect code. + */ + public static final String EXTRA_CALL_DISCONNECT_CAUSE = + "android.telecomm.extra.CALL_DISCONNECT_CAUSE"; + + /** + * Optional extra for {@link TelephonyManager#ACTION_PHONE_STATE_CHANGED} containing the + * disconnect message. + */ + public static final String EXTRA_CALL_DISCONNECT_MESSAGE = + "android.telecomm.extra.CALL_DISCONNECT_MESSAGE"; + + /** + * The dual tone multi-frequency signaling character sent to indicate the dialing system should + * pause for a predefined period. + */ + public static final char DTMF_CHARACTER_PAUSE = ','; + + /** + * The dual-tone multi-frequency signaling character sent to indicate the dialing system should + * wait for user confirmation before proceeding. + */ + public static final char DTMF_CHARACTER_WAIT = ';'; +} |