Merge "Remove extra symlink that confuses IDE tools." into master-nova
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 3e4895b..b57ec80 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -29,7 +29,6 @@
import java.util.Date;
import java.util.Locale;
import java.util.Set;
-import java.util.UUID;
/**
* Encapsulates all aspects of a given phone call throughout its lifecycle, starting
@@ -37,9 +36,6 @@
* connected etc).
*/
final class Call {
- /** Unique identifier for the call as a UUID string. */
- private final String mId;
-
/** Additional contact information beyond handle above, optional. */
private final ContactInfo mContactInfo;
@@ -96,7 +92,7 @@
private String mDisconnectMessage;
/**
- * Creates an empty call object with a unique call ID.
+ * Creates an empty call object.
*
* @param isIncoming True if this is an incoming call.
*/
@@ -113,7 +109,6 @@
* @param isIncoming True if this is an incoming call.
*/
Call(Uri handle, ContactInfo contactInfo, GatewayInfo gatewayInfo, boolean isIncoming) {
- mId = UUID.randomUUID().toString(); // UUIDs should provide sufficient uniqueness.
mState = CallState.NEW;
setHandle(handle);
mContactInfo = contactInfo;
@@ -125,15 +120,11 @@
/** {@inheritDoc} */
@Override public String toString() {
- return String.format(Locale.US, "[%s, %s, %s, %s]", mId, mState,
+ return String.format(Locale.US, "[%s, %s, %s]", mState,
mCallService == null ? "<null>" : mCallService.getComponentName(),
Log.pii(mHandle));
}
- String getId() {
- return mId;
- }
-
CallState getState() {
return mState;
}
@@ -237,6 +228,7 @@
callService.incrementAssociatedCallCount();
mCallService = callService;
+ mCallService.addCall(this);
}
/**
@@ -245,21 +237,27 @@
void clearCallService() {
if (mCallService != null) {
decrementAssociatedCallCount(mCallService);
- mCallService.cancelOutgoingCall(getId());
+ mCallService.removeCall(this);
mCallService = null;
}
}
void setCallServiceSelector(CallServiceSelectorWrapper selector) {
Preconditions.checkNotNull(selector);
+
+ clearCallServiceSelector();
+
mCallServiceSelector = selector;
+ mCallServiceSelector.addCall(this);
}
void clearCallServiceSelector() {
- // TODO(gilad): Un-comment once selectors are converted into wrappers.
- // decrementAssociatedCallCount(mCallServiceSelector);
-
- mCallServiceSelector = null;
+ if (mCallServiceSelector != null) {
+ // TODO(sail): Stop leaking selectors.
+ // decrementAssociatedCallCount(mCallServiceSelector);
+ mCallServiceSelector.removeCall(this);
+ mCallServiceSelector = null;
+ }
}
/**
@@ -295,7 +293,7 @@
void abort() {
if (mState == CallState.NEW) {
if (mCallService != null) {
- mCallService.abort(mId);
+ mCallService.abort(this);
}
clearCallService();
clearCallServiceSelector();
@@ -309,8 +307,8 @@
if (mCallService == null) {
Log.w(this, "playDtmfTone() request on a call without a call service.");
} else {
- Log.i(this, "Send playDtmfTone to call service for call with id %s", mId);
- mCallService.playDtmfTone(mId, digit);
+ Log.i(this, "Send playDtmfTone to call service for call %s", this);
+ mCallService.playDtmfTone(this, digit);
}
}
@@ -321,8 +319,8 @@
if (mCallService == null) {
Log.w(this, "stopDtmfTone() request on a call without a call service.");
} else {
- Log.i(this, "Send stopDtmfTone to call service for call with id %s", mId);
- mCallService.stopDtmfTone(mId);
+ Log.i(this, "Send stopDtmfTone to call service for call %s", this);
+ mCallService.stopDtmfTone(this);
}
}
@@ -333,11 +331,11 @@
if (mCallService == null) {
Log.w(this, "disconnect() request on a call without a call service.");
} else {
- Log.i(this, "Send disconnect to call service for call with id %s", mId);
+ Log.i(this, "Send disconnect to call service for call: %s", this);
// The call isn't officially disconnected until the call service confirms that the call
// was actually disconnected. Only then is the association between call and call service
// severed, see {@link CallsManager#markCallAsDisconnected}.
- mCallService.disconnect(mId);
+ mCallService.disconnect(this);
}
}
@@ -354,7 +352,7 @@
// it will work. Instead, we wait until confirmation from the call service that the
// call is in a non-RINGING state before changing the UI. See
// {@link CallServiceAdapter#setActive} and other set* methods.
- mCallService.answer(mId);
+ mCallService.answer(this);
}
}
@@ -367,7 +365,7 @@
// Check to verify that the call is still in the ringing state. A call can change states
// between the time the user hits 'reject' and Telecomm receives the command.
if (isRinging("reject")) {
- mCallService.reject(mId);
+ mCallService.reject(this);
}
}
@@ -378,7 +376,7 @@
Preconditions.checkNotNull(mCallService);
if (mState == CallState.ACTIVE) {
- mCallService.hold(mId);
+ mCallService.hold(this);
}
}
@@ -389,15 +387,15 @@
Preconditions.checkNotNull(mCallService);
if (mState == CallState.ON_HOLD) {
- mCallService.unhold(mId);
+ mCallService.unhold(this);
}
}
/**
* @return An object containing read-only information about this call.
*/
- CallInfo toCallInfo() {
- return new CallInfo(mId, mState, mHandle, mGatewayInfo);
+ CallInfo toCallInfo(String callId) {
+ return new CallInfo(callId, mState, mHandle, mGatewayInfo);
}
/** Checks if this is a live call or not. */
diff --git a/src/com/android/telecomm/CallAudioManager.java b/src/com/android/telecomm/CallAudioManager.java
index bf41f70..d9ff8a5 100644
--- a/src/com/android/telecomm/CallAudioManager.java
+++ b/src/com/android/telecomm/CallAudioManager.java
@@ -304,7 +304,7 @@
private void updateAudioForForegroundCall() {
Call call = CallsManager.getInstance().getForegroundCall();
if (call != null && call.getCallService() != null) {
- call.getCallService().onAudioStateChanged(call.getId(), mAudioState);
+ call.getCallService().onAudioStateChanged(call, mAudioState);
}
}
}
diff --git a/src/com/android/telecomm/CallIdMapper.java b/src/com/android/telecomm/CallIdMapper.java
new file mode 100644
index 0000000..aec3b5e
--- /dev/null
+++ b/src/com/android/telecomm/CallIdMapper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashBiMap;
+
+/** Utility to map {@link Call} objects to unique IDs. IDs are generated when a call is added. */
+class CallIdMapper {
+ private final HashBiMap<String, Call> mCalls = HashBiMap.create();
+ private final String mCallIdPrefix;
+ private static int sIdCount;
+
+ CallIdMapper(String callIdPrefix) {
+ ThreadUtil.checkOnMainThread();
+ mCallIdPrefix = callIdPrefix + "@";
+ }
+
+ void addCall(Call call) {
+ ThreadUtil.checkOnMainThread();
+ Preconditions.checkNotNull(call);
+ sIdCount++;
+ String callId = mCallIdPrefix + sIdCount;
+ mCalls.put(callId, call);
+ }
+
+ void removeCall(Call call) {
+ ThreadUtil.checkOnMainThread();
+ Preconditions.checkNotNull(call);
+ mCalls.inverse().remove(call);
+ }
+
+ String getCallId(Call call) {
+ ThreadUtil.checkOnMainThread();
+ Preconditions.checkNotNull(call);
+ return mCalls.inverse().get(call);
+ }
+
+ Call getCall(Object objId) {
+ ThreadUtil.checkOnMainThread();
+
+ String callId = null;
+ if (objId instanceof String) {
+ callId = (String) objId;
+ }
+ Preconditions.checkArgument(isValidCallId(callId));
+
+ return mCalls.get(callId);
+ }
+
+ void checkValidCallId(String callId) {
+ // Note, no need for thread check, this method is thread safe.
+ if (!isValidCallId(callId)) {
+ Log.wtf(this, "%s is not a valid call ID", callId);
+ throw new IllegalArgumentException("Invalid call ID.");
+ }
+ }
+
+ boolean isValidCallId(String callId) {
+ // Note, no need for thread check, this method is thread safe.
+ return callId != null && callId.startsWith(mCallIdPrefix);
+ }
+}
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
index 7b1fc3e..2db6255 100644
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ b/src/com/android/telecomm/CallServiceAdapter.java
@@ -17,49 +17,142 @@
package com.android.telecomm;
import android.os.Handler;
-import android.os.Looper;
+import android.os.Message;
import android.telecomm.CallInfo;
import com.android.internal.telecomm.ICallServiceAdapter;
import com.google.android.collect.Sets;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.android.internal.os.SomeArgs;
-import java.util.Collections;
import java.util.Set;
/**
- * Used by call services in order to update state and control calls while the call service is bound
- * to Telecomm. Each call service is given its own instance for the lifetime of the binding between
- * Telecomm and the call service.
- * TODO(santoscordon): Whenever we get any method invocations from the call service, we need to
- * check that the invocation is expected from that call service.
- * TODO(santoscordon): Move away from Runnable objects and into messages so that we create fewer
- * objects per IPC method call.
+ * Used by call services to communicate with Telecomm. Each call service is given its own instance
+ * of the adapter for the lifetmie of the binding.
* TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity() in the service methods?
*/
public final class CallServiceAdapter extends ICallServiceAdapter.Stub {
+ private static final int MSG_SET_IS_COMPATIBLE_WITH = 0;
+ private static final int MSG_NOTIFY_INCOMING_CALL = 1;
+ private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
+ private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
+ private static final int MSG_SET_ACTIVE = 4;
+ private static final int MSG_SET_RINGING = 5;
+ private static final int MSG_SET_DIALING = 6;
+ private static final int MSG_SET_DISCONNECTED = 7;
+ private static final int MSG_SET_ON_HOLD = 8;
+
+ private final class CallServiceAdapterHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ Call call;
+ switch (msg.what) {
+ case MSG_SET_IS_COMPATIBLE_WITH:
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call != null && !call.isIncoming()) {
+ mOutgoingCallsManager.setIsCompatibleWith(call,
+ msg.arg1 == 1 ? true : false);
+ } else {
+ Log.w(this, "Unknown call: %s, id: %s", call, msg.obj);
+ }
+ break;
+ case MSG_NOTIFY_INCOMING_CALL:
+ CallInfo clientCallInfo = (CallInfo) msg.obj;
+ call = mCallIdMapper.getCall(clientCallInfo.getId());
+ if (call != null && mPendingCalls.remove(call) && call.isIncoming()) {
+ CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
+ clientCallInfo.getHandle());
+ mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
+ } else {
+ Log.w(this, "Unknown incoming call: %s, id: %s", call,
+ clientCallInfo.getId());
+ }
+ break;
+ case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL:
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
+ mOutgoingCallsManager.handleSuccessfulCallAttempt(call);
+ } else {
+ // TODO(gilad): Figure out how to wire up the callService.abort() call.
+ Log.w(this, "Unknown outgoing call: %s, id: %s", call, msg.obj);
+ }
+ break;
+ case MSG_HANDLE_FAILED_OUTGOING_CALL: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ call = mCallIdMapper.getCall(args.arg1);
+ String reason = (String) args.arg2;
+ if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
+ mOutgoingCallsManager.handleFailedCallAttempt(call, reason);
+ } else {
+ Log.w(this, "Unknown outgoing call: %s, id: %s", call, args.arg1);
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_ACTIVE:
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call != null) {
+ mCallsManager.markCallAsActive(call);
+ } else {
+ Log.w(this, "Unknown call id: %s", msg.obj);
+ }
+ break;
+ case MSG_SET_RINGING:
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call != null) {
+ mCallsManager.markCallAsRinging(call);
+ } else {
+ Log.w(this, "Unknown call id: %s", msg.obj);
+ }
+ break;
+ case MSG_SET_DIALING:
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call != null) {
+ mCallsManager.markCallAsDialing(call);
+ } else {
+ Log.w(this, "Unknown call id: %s", msg.obj);
+ }
+ break;
+ case MSG_SET_DISCONNECTED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ call = mCallIdMapper.getCall(args.arg1);
+ String disconnectMessage = (String) args.arg2;
+ int disconnectCause = args.argi1;
+ if (call != null) {
+ mCallsManager.markCallAsDisconnected(call, disconnectCause,
+ disconnectMessage);
+ } else {
+ Log.w(this, "Unknown call id: %s", args.arg1);
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_ON_HOLD:
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call != null) {
+ mCallsManager.markCallAsOnHold(call);
+ } else {
+ Log.w(this, "Unknown call id: %s", msg.obj);
+ }
+ break;
+ }
+ }
+ }
+
private final CallsManager mCallsManager;
-
private final OutgoingCallsManager mOutgoingCallsManager;
-
private final IncomingCallsManager mIncomingCallsManager;
-
- /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- /**
- * The set of pending outgoing call IDs. Any {@link #handleSuccessfulOutgoingCall} and
- * {@link #handleFailedOutgoingCall} invocations with a call ID that is not in this set
- * are ignored.
- */
- private final Set<String> mPendingOutgoingCallIds = Sets.newHashSet();
-
- /**
- * The set of pending incoming call IDs. Any {@link #handleIncomingCall} invocations with
- * a call ID not in this set are ignored.
- */
- private final Set<String> mPendingIncomingCallIds = Sets.newHashSet();
+ private final Handler mHandler = new CallServiceAdapterHandler();
+ private final CallIdMapper mCallIdMapper;
+ private final Set<Call> mPendingCalls = Sets.newHashSet();
/**
* Persists the specified parameters.
@@ -68,199 +161,126 @@
* @param incomingCallsManager Manages the incoming call initialization flow.
*/
CallServiceAdapter(
- OutgoingCallsManager outgoingCallsManager, IncomingCallsManager incomingCallsManager) {
-
+ OutgoingCallsManager outgoingCallsManager,
+ IncomingCallsManager incomingCallsManager,
+ CallIdMapper callIdMapper) {
+ ThreadUtil.checkOnMainThread();
mCallsManager = CallsManager.getInstance();
mOutgoingCallsManager = outgoingCallsManager;
mIncomingCallsManager = incomingCallsManager;
+ mCallIdMapper = callIdMapper;
}
/** {@inheritDoc} */
- @Override public void setIsCompatibleWith(final String callId, final boolean isCompatible) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- if (mPendingOutgoingCallIds.contains(callId)) {
- mOutgoingCallsManager.setIsCompatibleWith(callId, isCompatible);
- } else {
- Log.wtf(CallServiceAdapter.this, "Unknown outgoing call: %s", callId);
- }
- }
- });
+ @Override
+ public void setIsCompatibleWith(String callId, boolean isCompatible) {
+ Log.v(this, "setIsCompatibleWith id: %d, isCompatible: %b", callId, isCompatible);
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_SET_IS_COMPATIBLE_WITH, isCompatible ? 1 : 0, 0, callId).
+ sendToTarget();
}
/** {@inheritDoc} */
- @Override public void notifyIncomingCall(final CallInfo callInfo) {
- checkValidCallId(callInfo.getId());
- mHandler.post(new Runnable() {
- @Override public void run() {
- if (mPendingIncomingCallIds.remove(callInfo.getId())) {
- mIncomingCallsManager.handleSuccessfulIncomingCall(callInfo);
- } else {
- Log.wtf(CallServiceAdapter.this, "Unknown incoming call: %s", callInfo);
- }
- }
- });
+ @Override
+ public void notifyIncomingCall(CallInfo callInfo) {
+ mCallIdMapper.checkValidCallId(callInfo.getId());
+ mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
}
/** {@inheritDoc} */
- @Override public void handleSuccessfulOutgoingCall(final String callId) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- if (mPendingOutgoingCallIds.remove(callId)) {
- mOutgoingCallsManager.handleSuccessfulCallAttempt(callId);
- } else {
- // TODO(gilad): Figure out how to wire up the callService.abort() call.
- Log.wtf(CallServiceAdapter.this, "Unknown outgoing call: %s", callId);
- }
- }
- });
+ @Override
+ public void handleSuccessfulOutgoingCall(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
}
/** {@inheritDoc} */
- @Override public void handleFailedOutgoingCall(final String callId, final String reason) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- if (mPendingOutgoingCallIds.remove(callId)) {
- mOutgoingCallsManager.handleFailedCallAttempt(callId, reason);
- } else {
- Log.wtf(CallServiceAdapter.this, "Unknown outgoing call: %s", callId);
- }
- }
- });
+ @Override
+ public void handleFailedOutgoingCall(String callId, String reason) {
+ mCallIdMapper.checkValidCallId(callId);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = reason;
+ mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
}
/** {@inheritDoc} */
- @Override public void setActive(final String callId) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.markCallAsActive(callId);
- }
- });
+ @Override
+ public void setActive(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
}
/** {@inheritDoc} */
- @Override public void setRinging(final String callId) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.markCallAsRinging(callId);
- }
- });
+ @Override
+ public void setRinging(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
}
/** {@inheritDoc} */
- @Override public void setDialing(final String callId) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.markCallAsDialing(callId);
- }
- });
+ @Override
+ public void setDialing(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
}
/** {@inheritDoc} */
- // TODO(gilad): Ensure that any communication from the underlying ICallService
- // implementation is expected (or otherwise suppressed at the adapter level).
- @Override public void setDisconnected(
- final String callId, final int disconnectCause, final String disconnectMessage) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.markCallAsDisconnected(callId, disconnectCause, disconnectMessage);
- }
- });
+ @Override
+ public void setDisconnected(
+ String callId, int disconnectCause, String disconnectMessage) {
+ mCallIdMapper.checkValidCallId(callId);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = disconnectMessage;
+ args.argi1 = disconnectCause;
+ mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
}
/** {@inheritDoc} */
- @Override public void setOnHold(final String callId) {
- checkValidCallId(callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.markCallAsOnHold(callId);
- }
- });
+ @Override
+ public void setOnHold(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
}
/**
- * Adds the specified call ID to the list of pending outgoing call IDs.
- * TODO(gilad): Consider passing the call processor (instead of the ID) both here and in the
- * remove case (same for incoming) such that the detour via the *CallsManager can be avoided.
- *
- * @param callId The ID of the call.
+ * Adds the specified call to the list of pending calls. Only calls in this list which are
+ * outgoing will be handled by {@link #isCompatibleWith}, {@link handleSuccessfulOutgoingCall},
+ * and {@link handleFailedOutgoingCall}. Similarly, only calls in this list which are incoming
+ * will be handled by {@link notifyIncomingCall}.
*/
- void addPendingOutgoingCallId(String callId) {
- mPendingOutgoingCallIds.add(callId);
+ void addPendingCall(Call call) {
+ mPendingCalls.add(call);
}
/**
- * Removes the specified call ID from the list of pending outgoing call IDs.
- *
- * @param callId The ID of the call.
+ * Removes the specified call from the list of pending calls.
*/
- void removePendingOutgoingCallId(String callId) {
- mPendingOutgoingCallIds.remove(callId);
- }
-
- /**
- * Adds a call ID to the list of pending incoming call IDs. Only calls with call IDs in the
- * list will be handled by {@link #handleIncomingCall}.
- *
- * @param callId The ID of the call.
- */
- void addPendingIncomingCallId(String callId) {
- mPendingIncomingCallIds.add(callId);
- }
-
- /**
- * Removes the specified call ID from the list of pending incoming call IDs.
- *
- * @param callId The ID of the call.
- */
- void removePendingIncomingCallId(String callId) {
- mPendingIncomingCallIds.remove(callId);
+ void removePendingCall(Call call) {
+ mPendingCalls.remove(call);
}
/**
* Called when the associated call service dies.
*/
void handleCallServiceDeath() {
- if (!mPendingIncomingCallIds.isEmpty()) {
- // Here and in the for loop below, we need to iterate through a copy because the code
- // inside the loop will modify the original list.
- for (String callId : ImmutableList.copyOf(mPendingIncomingCallIds)) {
- mIncomingCallsManager.handleFailedIncomingCall(callId);
+ if (!mPendingCalls.isEmpty()) {
+ // Iterate through a copy because the code inside the loop will modify the original
+ // list.
+ for (Call call : ImmutableList.copyOf(mPendingCalls)) {
+ if (call.isIncoming()) {
+ mIncomingCallsManager.handleFailedIncomingCall(call);
+ } else {
+ mOutgoingCallsManager.handleFailedCallAttempt(call,
+ "Call service disconnected.");
+ }
}
- if (!mPendingIncomingCallIds.isEmpty()) {
- Log.wtf(this, "Pending incoming calls did not get cleared.");
- mPendingIncomingCallIds.clear();
+ if (!mPendingCalls.isEmpty()) {
+ Log.wtf(this, "Pending calls did not get cleared.");
+ mPendingCalls.clear();
}
}
-
- if (!mPendingOutgoingCallIds.isEmpty()) {
- for (String callId : ImmutableList.copyOf(mPendingOutgoingCallIds)) {
- mOutgoingCallsManager.handleFailedCallAttempt(callId, "Call service disconnected.");
- }
-
- if (!mPendingOutgoingCallIds.isEmpty()) {
- Log.wtf(this, "Pending outgoing calls did not get cleared.");
- mPendingOutgoingCallIds.clear();
- }
- }
- }
-
- /**
- * Throws an IllegalArgumentException if the specified call ID is invalid.
- *
- * @param callId The call ID to check.
- */
- private void checkValidCallId(String callId) {
- if (Strings.isNullOrEmpty(callId)) {
- throw new IllegalArgumentException("Invalid call ID.");
- }
}
}
diff --git a/src/com/android/telecomm/CallServiceRepository.java b/src/com/android/telecomm/CallServiceRepository.java
index 40a1e7b..7247259 100644
--- a/src/com/android/telecomm/CallServiceRepository.java
+++ b/src/com/android/telecomm/CallServiceRepository.java
@@ -281,9 +281,8 @@
CallServiceWrapper callService = mCallServices.get(callServiceName);
if (callService == null) {
- CallServiceAdapter adapter =
- new CallServiceAdapter(mOutgoingCallsManager, mIncomingCallsManager);
- mCallServices.put(callServiceName, new CallServiceWrapper(descriptor, adapter));
+ mCallServices.put(callServiceName, new CallServiceWrapper(descriptor,
+ mOutgoingCallsManager, mIncomingCallsManager));
}
}
diff --git a/src/com/android/telecomm/CallServiceSelectorAdapter.java b/src/com/android/telecomm/CallServiceSelectorAdapter.java
new file mode 100644
index 0000000..5c805c5
--- /dev/null
+++ b/src/com/android/telecomm/CallServiceSelectorAdapter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telecomm.CallServiceDescriptor;
+
+import com.android.internal.telecomm.ICallServiceSelectorAdapter;
+import com.android.internal.os.SomeArgs;
+
+import java.util.List;
+
+/**
+ * Used by call service selector to communicate with Telecomm.
+ */
+public final class CallServiceSelectorAdapter extends ICallServiceSelectorAdapter.Stub {
+ private static final int MSG_SET_SELECTED_CALL_SERVICES = 0;
+ private static final int MSG_SET_HANDOFF_INFO = 1;
+
+ private final class CallServiceSelectorAdapaterHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_SELECTED_CALL_SERVICES: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Call call = mCallIdMapper.getCall(args.arg1);
+ List<CallServiceDescriptor> descriptors =
+ (List<CallServiceDescriptor>) args.arg2;
+ if (call != null) {
+ mOutgoingCallsManager.processSelectedCallServices(call, descriptors);
+ } else {
+ Log.w(this, "Unknown call: %s, id: %s", call, args.arg1);
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_HANDOFF_INFO: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Call call = mCallIdMapper.getCall(args.arg1);
+ Uri handle = (Uri) args.arg2;
+ Bundle extras = (Bundle) args.arg3;
+ if (call != null) {
+ mCallsManager.setHandoffInfo(call, handle, extras);
+ } else {
+ Log.w(this, "Unknown call: %s, id: %s", call, args.arg1);
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private final Handler mHandler = new CallServiceSelectorAdapaterHandler();
+ private final CallsManager mCallsManager;
+ private final OutgoingCallsManager mOutgoingCallsManager;
+ private final CallIdMapper mCallIdMapper;
+
+ CallServiceSelectorAdapter(
+ CallsManager callsManager,
+ OutgoingCallsManager outgoingCallsManager,
+ CallIdMapper callIdMapper) {
+ ThreadUtil.checkOnMainThread();
+ mCallsManager = callsManager;
+ mOutgoingCallsManager = outgoingCallsManager;
+ mCallIdMapper = callIdMapper;
+ }
+
+ /**
+ * @see CallServiceSelectorAdapater#setSelectedCallServices(String,List<CallServiceDescriptor>)
+ */
+ @Override
+ public void setSelectedCallServices(String callId, List<CallServiceDescriptor> descriptors) {
+ mCallIdMapper.checkValidCallId(callId);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = descriptors;
+ mHandler.obtainMessage(MSG_SET_SELECTED_CALL_SERVICES, args).sendToTarget();
+ }
+
+ /** @see CallServiceSelectorAdapater#setHandoffInfo(String,Uri,Bundle) */
+ @Override
+ public void setHandoffInfo(String callId, Uri handle, Bundle extras) {
+ mCallIdMapper.checkValidCallId(callId);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = handle;
+ args.arg3 = extras;
+ mHandler.obtainMessage(MSG_SET_HANDOFF_INFO, args).sendToTarget();
+ }
+}
diff --git a/src/com/android/telecomm/CallServiceSelectorRepository.java b/src/com/android/telecomm/CallServiceSelectorRepository.java
index 0ec805f..50d26e8 100644
--- a/src/com/android/telecomm/CallServiceSelectorRepository.java
+++ b/src/com/android/telecomm/CallServiceSelectorRepository.java
@@ -39,9 +39,7 @@
final class CallServiceSelectorRepository {
private final Switchboard mSwitchboard;
-
- /** The application context. */
- private final Context mContext;
+ private final OutgoingCallsManager mOutgoingCallsManager;
/**
* The set of call-service selectors. Only populated via initiateLookup scenarios.
@@ -54,9 +52,11 @@
*
* @param switchboard The switchboard for this finer to work against.
*/
- CallServiceSelectorRepository(Switchboard switchboard) {
+ CallServiceSelectorRepository(
+ Switchboard switchboard,
+ OutgoingCallsManager outgoingCallsManager) {
mSwitchboard = switchboard;
- mContext = TelecommApp.getInstance();
+ mOutgoingCallsManager = outgoingCallsManager;
}
/**
@@ -70,7 +70,8 @@
List<ComponentName> selectorNames = getSelectorNames();
for (ComponentName name : selectorNames) {
if (!mCallServiceSelectors.containsKey(name)) {
- mCallServiceSelectors.put(name, new CallServiceSelectorWrapper(name));
+ mCallServiceSelectors.put(name, new CallServiceSelectorWrapper(name,
+ CallsManager.getInstance(), mOutgoingCallsManager));
}
}
@@ -86,7 +87,7 @@
// The list of selector names to return to the caller, may be populated below.
List<ComponentName> selectorNames = Lists.newArrayList();
- PackageManager packageManager = mContext.getPackageManager();
+ PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
Intent intent = new Intent(TelecommConstants.ACTION_CALL_SERVICE_SELECTOR);
for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
ServiceInfo serviceInfo = entry.serviceInfo;
diff --git a/src/com/android/telecomm/CallServiceSelectorWrapper.java b/src/com/android/telecomm/CallServiceSelectorWrapper.java
index a535798..f4ddf19 100644
--- a/src/com/android/telecomm/CallServiceSelectorWrapper.java
+++ b/src/com/android/telecomm/CallServiceSelectorWrapper.java
@@ -22,12 +22,12 @@
import android.os.RemoteException;
import android.telecomm.CallInfo;
import android.telecomm.CallServiceDescriptor;
+import android.telecomm.CallServiceSelector;
import android.telecomm.TelecommConstants;
-import android.telecomm.CallServiceSelector.CallServiceSelectionResponse;
import com.google.common.base.Preconditions;
-import com.android.internal.telecomm.ICallServiceSelectionResponse;
import com.android.internal.telecomm.ICallServiceSelector;
+import com.android.internal.telecomm.ICallServiceSelectorAdapter;
import java.util.List;
@@ -36,60 +36,53 @@
* safely be unbound.
*/
final class CallServiceSelectorWrapper extends ServiceBinder<ICallServiceSelector> {
- class SelectionResponseImpl extends ICallServiceSelectionResponse.Stub {
- private final CallServiceSelectionResponse mResponse;
-
- SelectionResponseImpl(CallServiceSelectionResponse response) {
- mResponse = response;
- }
-
- @Override
- public void setSelectedCallServiceDescriptors(
- final List<CallServiceDescriptor> descriptors) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mResponse.setSelectedCallServices(descriptors);
- }
- });
- }
- }
-
private ICallServiceSelector mSelectorInterface;
-
- private Binder mBinder = new Binder();
-
- private Handler mHandler = new Handler();
+ private final Binder mBinder = new Binder();
+ private final Handler mHandler = new Handler();
+ private final CallIdMapper mCallIdMapper = new CallIdMapper("CallServiceSelector");
+ private final CallServiceSelectorAdapter mAdapter;
/**
* Creates a call-service selector for the specified component.
*
* @param componentName The component name of the service.
*/
- CallServiceSelectorWrapper(ComponentName componentName) {
+ CallServiceSelectorWrapper(ComponentName componentName, CallsManager callsManager,
+ OutgoingCallsManager outgoingCallsManager) {
super(TelecommConstants.ACTION_CALL_SERVICE_SELECTOR, componentName);
+ mAdapter = new CallServiceSelectorAdapter(callsManager, outgoingCallsManager,
+ mCallIdMapper);
+ }
+
+ /** See {@link CallServiceSelector#setCallServiceSelectorAdapter}. */
+ private void setCallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) {
+ if (isServiceValid("setCallServiceSelectorAdapter")) {
+ try {
+ mSelectorInterface.setCallServiceSelectorAdapter(adapter);
+ } catch (RemoteException e) {
+ }
+ }
}
/**
* Retrieves the sorted set of call services that are preferred by this selector. Upon failure,
* the error callback is invoked. Can be invoked even when the call service is unbound.
*
- * @param callInfo The details of the call.
- * @param selectionResponse The selection response callback to invoke upon success.
+ * @param call 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.
* @param errorCallback The callback to invoke upon failure.
*/
- void select(final CallInfo callInfo, final List<CallServiceDescriptor> callServiceDescriptors,
- final CallServiceSelectionResponse selectionResponse, final Runnable errorCallback) {
+ void select(final Call call, final List<CallServiceDescriptor> descriptors,
+ final Runnable errorCallback) {
BindCallback callback = new BindCallback() {
@Override
public void onSuccess() {
if (isServiceValid("select")) {
try {
- mSelectorInterface.select(callInfo, callServiceDescriptors,
- new SelectionResponseImpl(selectionResponse));
+ CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
+ mSelectorInterface.select(callInfo, descriptors);
} catch (RemoteException e) {
- Log.e(CallServiceSelectorWrapper.this, e, "Failed calling select for selector: %s.",
- getComponentName());
}
}
}
@@ -103,9 +96,22 @@
mBinder.bind(callback);
}
+ void addCall(Call call) {
+ mCallIdMapper.addCall(call);
+ }
+
+ void removeCall(Call call) {
+ mCallIdMapper.removeCall(call);
+ }
+
/** {@inheritDoc} */
@Override
protected void setServiceInterface(IBinder binder) {
- mSelectorInterface = ICallServiceSelector.Stub.asInterface(binder);
+ if (binder == null) {
+ mSelectorInterface = null;
+ } else {
+ mSelectorInterface = ICallServiceSelector.Stub.asInterface(binder);
+ setCallServiceSelectorAdapter(mAdapter);
+ }
}
}
diff --git a/src/com/android/telecomm/CallServiceWrapper.java b/src/com/android/telecomm/CallServiceWrapper.java
index 4475b2d..007d35a 100644
--- a/src/com/android/telecomm/CallServiceWrapper.java
+++ b/src/com/android/telecomm/CallServiceWrapper.java
@@ -48,18 +48,25 @@
private ICallService mServiceInterface;
private Binder mBinder = new Binder();
+ private final CallIdMapper mCallIdMapper;
/**
- * Creates a call-service provider for the specified descriptor.
+ * Creates a call-service for the specified descriptor.
*
* @param descriptor The call-service descriptor from
* {@link ICallServiceProvider#lookupCallServices}.
- * @param adapter The call-service adapter.
+ * @param outgoingCallsManager Manages the placing of outgoing calls.
+ * @param incomingCallsManager Manages the incoming call initialization flow.
*/
- CallServiceWrapper(CallServiceDescriptor descriptor, CallServiceAdapter adapter) {
+ CallServiceWrapper(
+ CallServiceDescriptor descriptor,
+ OutgoingCallsManager outgoingCallsManager,
+ IncomingCallsManager incomingCallsManager) {
super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
mDescriptor = descriptor;
- mAdapter = adapter;
+ mCallIdMapper = new CallIdMapper("CallService");
+ mAdapter = new CallServiceAdapter(outgoingCallsManager, incomingCallsManager,
+ mCallIdMapper);
}
CallServiceDescriptor getDescriptor() {
@@ -67,12 +74,11 @@
}
/** See {@link ICallService#setCallServiceAdapter}. */
- void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
+ private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
if (isServiceValid("setCallServiceAdapter")) {
try {
mServiceInterface.setCallServiceAdapter(callServiceAdapter);
} catch (RemoteException e) {
- Log.e(this, e, "Failed to setCallServiceAdapter.");
}
}
}
@@ -82,20 +88,18 @@
* see {@link ICallService#isCompatibleWith}. Upon failure, the specified error callback is
* invoked. Can be invoked even when the call service is unbound.
*
- * @param callInfo The details of the call.
* @param errorCallback The callback to invoke upon failure.
*/
- void isCompatibleWith(final CallInfo callInfo, final Runnable errorCallback) {
- Log.d(this, "isCompatibleWith(%s) via %s.", callInfo, getComponentName());
+ void isCompatibleWith(final Call call, final Runnable errorCallback) {
+ Log.d(this, "isCompatibleWith(%s) via %s.", call, getComponentName());
BindCallback callback = new BindCallback() {
@Override public void onSuccess() {
if (isServiceValid("isCompatibleWith")) {
try {
- mAdapter.addPendingOutgoingCallId(callInfo.getId());
+ mAdapter.addPendingCall(call);
+ CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
mServiceInterface.isCompatibleWith(callInfo);
} catch (RemoteException e) {
- mAdapter.removePendingOutgoingCallId(callInfo.getId());
- Log.e(CallServiceWrapper.this, e, "Failed checking isCompatibleWith.");
}
}
}
@@ -110,72 +114,56 @@
/**
* Attempts to place the specified call, see {@link ICallService#call}. Upon failure, the
* specified error callback is invoked. Can be invoked even when the call service is unbound.
- *
- * @param callInfo The details of the call.
- * @param errorCallback The callback to invoke upon failure.
*/
- void call(final CallInfo callInfo, final Runnable errorCallback) {
- Log.d(this, "call(%s) via %s.", callInfo, getComponentName());
- BindCallback callback = new BindCallback() {
- @Override public void onSuccess() {
- String callId = callInfo.getId();
- if (isServiceValid("call")) {
- try {
- mServiceInterface.call(callInfo);
- } catch (RemoteException e) {
- Log.e(CallServiceWrapper.this, e, "Failed to place call %s", callId);
- }
- }
+ void call(Call call) {
+ Log.d(this, "call(%s) via %s.", call, getComponentName());
+ if (isServiceValid("call")) {
+ try {
+ CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
+ mServiceInterface.call(callInfo);
+ } catch (RemoteException e) {
}
- @Override public void onFailure() {
- errorCallback.run();
- }
- };
-
- mBinder.bind(callback);
+ }
}
/** @see CallService#abort(String) */
- void abort(String callId) {
- mAdapter.removePendingOutgoingCallId(callId);
+ void abort(Call call) {
+ mAdapter.removePendingCall(call);
if (isServiceValid("abort")) {
try {
- mServiceInterface.abort(callId);
+ mServiceInterface.abort(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to abort call %s", callId);
}
}
}
/** @see CallService#hold(String) */
- void hold(String callId) {
+ void hold(Call call) {
if (isServiceValid("hold")) {
try {
- mServiceInterface.hold(callId);
+ mServiceInterface.hold(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to put on hold for call %s", callId);
}
}
}
/** @see CallService#unhold(String) */
- void unhold(String callId) {
+ void unhold(Call call) {
if (isServiceValid("unhold")) {
try {
- mServiceInterface.unhold(callId);
+ mServiceInterface.unhold(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to remove from hold for call %s", callId);
}
}
}
/** @see CallService#onAudioStateChanged(String,CallAudioState) */
- void onAudioStateChanged(String activeCallId, CallAudioState audioState) {
+ void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
if (isServiceValid("onAudioStateChanged")) {
try {
- mServiceInterface.onAudioStateChanged(activeCallId, audioState);
+ mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
+ audioState);
} catch (RemoteException e) {
- Log.e(this, e, "Failed to update audio state for call %s", activeCallId);
}
}
}
@@ -186,26 +174,20 @@
* is invoked. Can be invoked even when the call service is unbound.
* See {@link ICallService#setIncomingCallId}.
*
- * @param callId The call ID used for the incoming call.
+ * @param call The call used for the incoming call.
* @param extras The {@link CallService}-provided extras which need to be sent back.
* @param errorCallback The callback to invoke upon failure.
*/
- void setIncomingCallId(
- final String callId,
- final Bundle extras,
- final Runnable errorCallback) {
-
- Log.d(this, "setIncomingCall(%s) via %s.", callId, getComponentName());
+ void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
+ Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
BindCallback callback = new BindCallback() {
@Override public void onSuccess() {
if (isServiceValid("setIncomingCallId")) {
- mAdapter.addPendingIncomingCallId(callId);
+ mAdapter.addPendingCall(call);
try {
- mServiceInterface.setIncomingCallId(callId, extras);
+ mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
+ extras);
} catch (RemoteException e) {
- Log.e(CallServiceWrapper.this, e,
- "Failed to setIncomingCallId for call %s", callId);
- mAdapter.removePendingIncomingCallId(callId);
}
}
}
@@ -218,78 +200,62 @@
}
/** @see CallService#disconnect(String) */
- void disconnect(String callId) {
+ void disconnect(Call call) {
if (isServiceValid("disconnect")) {
try {
- mServiceInterface.disconnect(callId);
+ mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to disconnect call %s", callId);
}
}
}
/** @see CallService#answer(String) */
- void answer(String callId) {
+ void answer(Call call) {
if (isServiceValid("answer")) {
try {
- mServiceInterface.answer(callId);
+ mServiceInterface.answer(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to answer call %s", callId);
}
}
}
/** @see CallService#reject(String) */
- void reject(String callId) {
+ void reject(Call call) {
if (isServiceValid("reject")) {
try {
- mServiceInterface.reject(callId);
+ mServiceInterface.reject(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to reject call %s", callId);
}
}
}
/** @see CallService#playDtmfTone(String,char) */
- void playDtmfTone(String callId, char digit) {
+ void playDtmfTone(Call call, char digit) {
if (isServiceValid("playDtmfTone")) {
try {
- mServiceInterface.playDtmfTone(callId, digit);
+ mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
} catch (RemoteException e) {
- Log.e(this, e, "Failed to play DTMF tone for call %s", callId);
}
}
}
/** @see CallService#stopDtmfTone(String) */
- void stopDtmfTone(String callId) {
+ void stopDtmfTone(Call call) {
if (isServiceValid("stopDtmfTone")) {
try {
- mServiceInterface.stopDtmfTone(callId);
+ mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
} catch (RemoteException e) {
- Log.e(this, e, "Failed to stop DTMF tone for call %s", callId);
}
}
}
- /**
- * Cancels the incoming call for the specified call ID.
- * TODO(santoscordon): This method should be called by IncomingCallsManager when the incoming
- * call has failed.
- *
- * @param callId The ID of the call.
- */
- void cancelIncomingCall(String callId) {
- mAdapter.removePendingIncomingCallId(callId);
+ void addCall(Call call) {
+ mCallIdMapper.addCall(call);
}
- /**
- * Cancels the outgoing call for the specified call ID.
- *
- * @param callId The ID of the call.
- */
- void cancelOutgoingCall(String callId) {
- mAdapter.removePendingOutgoingCallId(callId);
+ void removeCall(Call call) {
+ mAdapter.removePendingCall(call);
+ mCallIdMapper.removeCall(call);
}
/** {@inheritDoc} */
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 3821426..3e8a42e 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -62,12 +62,10 @@
private final Switchboard mSwitchboard;
/**
- * The main call repository. Keeps an instance of all live calls keyed by call ID. New incoming
- * and outgoing calls are added to the map and removed when the calls move to the disconnected
- * state.
- * TODO(santoscordon): Add new CallId class and use it in place of String.
+ * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
+ * calls are added to the map and removed when the calls move to the disconnected state.
*/
- private final Map<String, Call> mCalls = Maps.newHashMap();
+ private final Set<Call> mCalls = Sets.newLinkedHashSet();
/**
* The call the user is currently interacting with. This is the call that should have audio
@@ -104,7 +102,7 @@
}
ImmutableCollection<Call> getCalls() {
- return ImmutableList.copyOf(mCalls.values());
+ return ImmutableList.copyOf(mCalls);
}
Call getForegroundCall() {
@@ -112,7 +110,7 @@
}
boolean hasEmergencyCall() {
- for (Call call : mCalls.values()) {
+ for (Call call : mCalls) {
if (call.isEmergencyCall()) {
return true;
}
@@ -135,8 +133,8 @@
void processIncomingCallIntent(CallServiceDescriptor descriptor, Bundle extras) {
Log.d(this, "processIncomingCallIntent");
// Create a call with no handle. Eventually, switchboard will update the call with
- // additional information from the call service, but for now we just need one to pass around
- // with a unique call ID.
+ // additional information from the call service, but for now we just need one to pass
+ // around.
Call call = new Call(true /* isIncoming */);
mSwitchboard.retrieveIncomingCall(call, descriptor, extras);
@@ -238,7 +236,7 @@
removeCall(call);
} else {
// TODO: Replace disconnect cause with more specific disconnect causes.
- markCallAsDisconnected(call.getId(), DisconnectCause.ERROR_UNSPECIFIED, null);
+ markCallAsDisconnected(call, DisconnectCause.ERROR_UNSPECIFIED, null);
}
}
@@ -246,22 +244,17 @@
* Instructs Telecomm to answer the specified call. Intended to be invoked by the in-call
* app through {@link InCallAdapter} after Telecomm notifies it of an incoming call followed by
* the user opting to answer said call.
- *
- * @param callId The ID of the call.
*/
- void answerCall(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.i(this, "Request to answer a non-existent call %s", callId);
+ void answerCall(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.i(this, "Request to answer a non-existent call %s", call);
} else {
for (CallsManagerListener listener : mListeners) {
listener.onIncomingCallAnswered(call);
}
// We do not update the UI until we get confirmation of the answer() through
- // {@link #markCallAsActive}. However, if we ever change that to look more responsive,
- // then we need to make sure we add a timeout for the answer() in case the call never
- // comes out of RINGING.
+ // {@link #markCallAsActive}.
call.answer();
}
}
@@ -270,18 +263,14 @@
* Instructs Telecomm to reject the specified call. Intended to be invoked by the in-call
* app through {@link InCallAdapter} after Telecomm notifies it of an incoming call followed by
* the user opting to reject said call.
- *
- * @param callId The ID of the call.
*/
- void rejectCall(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.i(this, "Request to reject a non-existent call %s", callId);
+ void rejectCall(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.i(this, "Request to reject a non-existent call %s", call);
} else {
for (CallsManagerListener listener : mListeners) {
listener.onIncomingCallRejected(call);
}
-
call.reject();
}
}
@@ -289,13 +278,11 @@
/**
* Instructs Telecomm to play the specified DTMF tone within the specified call.
*
- * @param callId The ID of the call.
* @param digit The DTMF digit to play.
*/
- void playDtmfTone(String callId, char digit) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.i(this, "Request to play DTMF in a non-existent call %s", callId);
+ void playDtmfTone(Call call, char digit) {
+ if (!mCalls.contains(call)) {
+ Log.i(this, "Request to play DTMF in a non-existent call %s", call);
} else {
call.playDtmfTone(digit);
}
@@ -303,13 +290,10 @@
/**
* Instructs Telecomm to stop the currently playing DTMF tone, if any.
- *
- * @param callId The ID of the call.
*/
- void stopDtmfTone(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.i(this, "Request to stop DTMF in a non-existent call %s", callId);
+ void stopDtmfTone(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
} else {
call.stopDtmfTone();
}
@@ -317,13 +301,10 @@
/**
* Instructs Telecomm to continue the current post-dial DTMF string, if any.
- *
- * @param callId The ID of the call.
*/
- void postDialContinue(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.i(this, "Request to continue post-dial string in a non-existent call %s", callId);
+ void postDialContinue(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
} else {
// TODO(ihab): Implement this from this level on downwards
// call.postDialContinue();
@@ -335,13 +316,10 @@
* Instructs Telecomm to disconnect the specified call. Intended to be invoked by the
* in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
* the user hitting the end-call button.
- *
- * @param callId The ID of the call.
*/
- void disconnectCall(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.w(this, "Unknown call (%s) asked to disconnect", callId);
+ void disconnectCall(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.w(this, "Unknown call (%s) asked to disconnect", call);
} else {
call.disconnect();
}
@@ -351,15 +329,12 @@
* Instructs Telecomm to put the specified call on hold. Intended to be invoked by the
* in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
* the user hitting the hold button during an active call.
- *
- * @param callId The ID of the call.
*/
- void holdCall(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.w(this, "Unknown call (%s) asked to be put on hold", callId);
+ void holdCall(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.w(this, "Unknown call (%s) asked to be put on hold", call);
} else {
- Log.d(this, "Putting call on hold: (%s)", callId);
+ Log.d(this, "Putting call on hold: (%s)", call);
call.hold();
}
}
@@ -368,15 +343,12 @@
* Instructs Telecomm to release the specified call from hold. Intended to be invoked by
* the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
* by the user hitting the hold button during a held call.
- *
- * @param callId The ID of the call
*/
- void unholdCall(String callId) {
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.w(this, "Unknown call (%s) asked to be removed from hold", callId);
+ void unholdCall(Call call) {
+ if (!mCalls.contains(call)) {
+ Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
} else {
- Log.d(this, "Removing call from hold: (%s)", callId);
+ Log.d(this, "Removing call from hold: (%s)", call);
call.unhold();
}
}
@@ -402,37 +374,37 @@
}
}
- void markCallAsRinging(String callId) {
- setCallState(callId, CallState.RINGING);
+ void markCallAsRinging(Call call) {
+ setCallState(call, CallState.RINGING);
}
- void markCallAsDialing(String callId) {
- setCallState(callId, CallState.DIALING);
+ void markCallAsDialing(Call call) {
+ setCallState(call, CallState.DIALING);
}
- void markCallAsActive(String callId) {
- setCallState(callId, CallState.ACTIVE);
+ void markCallAsActive(Call call) {
+ setCallState(call, CallState.ACTIVE);
}
- void markCallAsOnHold(String callId) {
- setCallState(callId, CallState.ON_HOLD);
+ void markCallAsOnHold(Call call) {
+ setCallState(call, CallState.ON_HOLD);
}
/**
* Marks the specified call as DISCONNECTED and notifies the in-call app. If this was the last
* live call, then also disconnect from the in-call controller.
*
- * @param callId The ID of the call.
* @param disconnectCause The disconnect reason, see {@link android.telephony.DisconnectCause}.
* @param disconnectMessage Optional call-service-provided message about the disconnect.
*/
- void markCallAsDisconnected(String callId, int disconnectCause, String disconnectMessage) {
- Call call = mCalls.get(callId);
- if (call != null) {
- call.setDisconnectCause(disconnectCause, disconnectMessage);
- setCallState(callId, CallState.DISCONNECTED);
- removeCall(call);
- }
+ void markCallAsDisconnected(Call call, int disconnectCause, String disconnectMessage) {
+ call.setDisconnectCause(disconnectCause, disconnectMessage);
+ setCallState(call, CallState.DISCONNECTED);
+ removeCall(call);
+ }
+
+ void setHandoffInfo(Call call, Uri handle, Bundle extras) {
+ // TODO(sail): Implement this.
}
/**
@@ -443,9 +415,9 @@
*/
void handleCallServiceDeath(CallServiceWrapper callService) {
Preconditions.checkNotNull(callService);
- for (Call call : ImmutableList.copyOf(mCalls.values())) {
+ for (Call call : ImmutableList.copyOf(mCalls)) {
if (call.getCallService() == callService) {
- markCallAsDisconnected(call.getId(), DisconnectCause.ERROR_UNSPECIFIED, null);
+ markCallAsDisconnected(call, DisconnectCause.ERROR_UNSPECIFIED, null);
}
}
}
@@ -456,7 +428,7 @@
* @param call The call to add.
*/
private void addCall(Call call) {
- mCalls.put(call.getId(), call);
+ mCalls.add(call);
for (CallsManagerListener listener : mListeners) {
listener.onCallAdded(call);
}
@@ -464,30 +436,21 @@
}
private void removeCall(Call call) {
- mCalls.remove(call.getId());
call.clearCallService();
- for (CallsManagerListener listener : mListeners) {
- listener.onCallRemoved(call);
+ call.clearCallServiceSelector();
+
+ boolean shouldNotify = false;
+ if (mCalls.contains(call)) {
+ mCalls.remove(call);
+ shouldNotify = true;
}
- updateForegroundCall();
- }
- /**
- * Sets the specified state on the specified call.
- *
- * @param callId The ID of the call to update.
- * @param newState The new state of the call.
- */
- private void setCallState(String callId, CallState newState) {
- Preconditions.checkState(!Strings.isNullOrEmpty(callId));
- Preconditions.checkNotNull(newState);
-
- Call call = mCalls.get(callId);
- if (call == null) {
- Log.w(this, "Call %s was not found while attempting to update the state to %s.",
- callId, newState );
- } else {
- setCallState(call, newState);
+ // Only broadcast changes for calls that are being tracked.
+ if (shouldNotify) {
+ for (CallsManagerListener listener : mListeners) {
+ listener.onCallRemoved(call);
+ }
+ updateForegroundCall();
}
}
@@ -498,6 +461,7 @@
* @param newState The new state of the call.
*/
private void setCallState(Call call, CallState newState) {
+ Preconditions.checkNotNull(newState);
CallState oldState = call.getState();
if (newState != oldState) {
// Unfortunately, in the telephony world the radio is king. So if the call notifies
@@ -510,7 +474,7 @@
call.setState(newState);
// Only broadcast state change for calls that are being tracked.
- if (mCalls.containsKey(call.getId())) {
+ if (mCalls.contains(call)) {
for (CallsManagerListener listener : mListeners) {
listener.onCallStateChanged(call, oldState, newState);
}
@@ -524,7 +488,7 @@
*/
private void updateForegroundCall() {
Call newForegroundCall = null;
- for (Call call : mCalls.values()) {
+ for (Call call : mCalls) {
// Incoming ringing calls have priority.
if (call.getState() == CallState.RINGING) {
newForegroundCall = call;
diff --git a/src/com/android/telecomm/InCallAdapter.java b/src/com/android/telecomm/InCallAdapter.java
index fec3080..14669c6 100644
--- a/src/com/android/telecomm/InCallAdapter.java
+++ b/src/com/android/telecomm/InCallAdapter.java
@@ -17,7 +17,7 @@
package com.android.telecomm;
import android.os.Handler;
-import android.os.Looper;
+import android.os.Message;
import com.android.internal.telecomm.IInCallAdapter;
@@ -27,115 +27,146 @@
* binding to it. This adapter can receive commands and updates until the in-call app is unbound.
*/
class InCallAdapter extends IInCallAdapter.Stub {
+ private static final int MSG_ANSWER_CALL = 0;
+ private static final int MSG_REJECT_CALL = 1;
+ private static final int MSG_PLAY_DTMF_TONE = 2;
+ private static final int MSG_STOP_DTMF_TONE = 3;
+ private static final int MSG_POST_DIAL_CONTINUE = 4;
+ private static final int MSG_DISCONNECT_CALL = 5;
+ private static final int MSG_HOLD_CALL = 6;
+ private static final int MSG_UNHOLD_CALL = 7;
+ private static final int MSG_MUTE = 8;
+ private static final int MSG_SET_AUDIO_ROUTE = 9;
+
+ private final class InCallAdapterHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ Call call = null;
+ if (msg.obj != null) {
+ call = mCallIdMapper.getCall(msg.obj);
+ if (call == null) {
+ Log.w(this, "Unknown call id: %s, msg: %d", msg.obj, msg.what);
+ return;
+ }
+ }
+
+ switch (msg.what) {
+ case MSG_ANSWER_CALL:
+ mCallsManager.answerCall(call);
+ break;
+ case MSG_REJECT_CALL:
+ mCallsManager.rejectCall(call);
+ break;
+ case MSG_PLAY_DTMF_TONE:
+ mCallsManager.playDtmfTone(call, (char) msg.arg1);
+ break;
+ case MSG_STOP_DTMF_TONE:
+ mCallsManager.stopDtmfTone(call);
+ break;
+ case MSG_POST_DIAL_CONTINUE:
+ mCallsManager.postDialContinue(call);
+ break;
+ case MSG_DISCONNECT_CALL:
+ mCallsManager.disconnectCall(call);
+ break;
+ case MSG_HOLD_CALL:
+ mCallsManager.holdCall(call);
+ break;
+ case MSG_UNHOLD_CALL:
+ mCallsManager.unholdCall(call);
+ break;
+ case MSG_MUTE:
+ mCallsManager.mute(msg.arg1 == 1 ? true : false);
+ break;
+ case MSG_SET_AUDIO_ROUTE:
+ mCallsManager.setAudioRoute(msg.arg1);
+ break;
+ }
+ }
+ }
private final CallsManager mCallsManager;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Handler mHandler = new InCallAdapterHandler();
+ private final CallIdMapper mCallIdMapper;
/** Persists the specified parameters. */
- public InCallAdapter(CallsManager callsManager) {
+ public InCallAdapter(CallsManager callsManager, CallIdMapper callIdMapper) {
+ ThreadUtil.checkOnMainThread();
mCallsManager = callsManager;
+ mCallIdMapper = callIdMapper;
}
/** {@inheritDoc} */
@Override
- public void answerCall(final String callId) {
+ public void answerCall(String callId) {
Log.d(this, "answerCall(%s)", callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.answerCall(callId);
- }
- });
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_ANSWER_CALL, callId).sendToTarget();
}
/** {@inheritDoc} */
@Override
- public void rejectCall(final String callId) {
+ public void rejectCall(String callId) {
Log.d(this, "rejectCall(%s)", callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.rejectCall(callId);
- }
- });
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_REJECT_CALL, callId).sendToTarget();
}
/** {@inheritDoc} */
- public void playDtmfTone(final String callId, final char digit) {
+ @Override
+ public void playDtmfTone(String callId, char digit) {
Log.d(this, "playDtmfTone(%s,%c)", callId, digit);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.playDtmfTone(callId, digit);
- }
- });
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, (int) digit, 0, callId).sendToTarget();
}
/** {@inheritDoc} */
- public void stopDtmfTone(final String callId) {
+ @Override
+ public void stopDtmfTone(String callId) {
Log.d(this, "stopDtmfTone(%s)", callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.stopDtmfTone(callId);
- }
- });
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
}
/** {@inheritDoc} */
- public void postDialContinue(final String callId) {
+ @Override
+ public void postDialContinue(String callId) {
Log.d(this, "postDialContinue(%s)", callId);
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.postDialContinue(callId);
- }
- });
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_POST_DIAL_CONTINUE, callId).sendToTarget();
}
/** {@inheritDoc} */
@Override
- public void disconnectCall(final String callId) {
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.disconnectCall(callId);
- }
- });
+ public void disconnectCall(String callId) {
+ Log.v(this, "disconnectCall: %s", callId);
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
}
/** {@inheritDoc} */
@Override
- public void holdCall(final String callId) {
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.holdCall(callId);
- }
- });
+ public void holdCall(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_HOLD_CALL, callId).sendToTarget();
}
/** {@inheritDoc} */
@Override
- public void unholdCall(final String callId) {
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.unholdCall(callId);
- }
- });
+ public void unholdCall(String callId) {
+ mCallIdMapper.checkValidCallId(callId);
+ mHandler.obtainMessage(MSG_UNHOLD_CALL, callId).sendToTarget();
}
/** {@inheritDoc} */
@Override
- public void mute(final boolean shouldMute) {
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.mute(shouldMute);
- }
- });
+ public void mute(boolean shouldMute) {
+ mHandler.obtainMessage(MSG_MUTE, shouldMute ? 1 : 0, 0).sendToTarget();
}
/** {@inheritDoc} */
@Override
- public void setAudioRoute(final int route) {
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallsManager.setAudioRoute(route);
- }
- });
+ public void setAudioRoute(int route) {
+ mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, route, 0).sendToTarget();
}
}
diff --git a/src/com/android/telecomm/InCallController.java b/src/com/android/telecomm/InCallController.java
index 5fbf1f8..d975b0b 100644
--- a/src/com/android/telecomm/InCallController.java
+++ b/src/com/android/telecomm/InCallController.java
@@ -32,16 +32,12 @@
/**
* Binds to {@link IInCallService} and provides the service to {@link CallsManager} through which it
* can send updates to the in-call app. This class is created and owned by CallsManager and retains
- * a binding to the {@link IInCallService} (implemented by the in-call app) until CallsManager
- * explicitly disconnects it. CallsManager starts the connection by calling {@link #connect} and
- * retains the connection as long as it has calls which need UI. When all calls are disconnected,
- * CallsManager will invoke {@link #disconnect} to sever the binding until the in-call UI is needed
- * again.
+ * a binding to the {@link IInCallService} (implemented by the in-call app).
*/
public final class InCallController extends CallsManagerListenerBase {
/**
* Used to bind to the in-call app and triggers the start of communication between
- * CallsManager and in-call app.
+ * this class and in-call app.
*/
private class InCallServiceConnection implements ServiceConnection {
/** {@inheritDoc} */
@@ -75,8 +71,8 @@
/** The in-call app implementation, see {@link IInCallService}. */
private IInCallService mInCallService;
- // TODO(santoscordon): May be better to expose the IInCallService methods directly from this
- // class as its own method to make the CallsManager code easier to read.
+ private final CallIdMapper mCallIdMapper = new CallIdMapper("InCall");
+
IInCallService getService() {
return mInCallService;
}
@@ -87,10 +83,11 @@
bind();
} else {
Log.i(this, "Adding call: %s", call);
+ mCallIdMapper.addCall(call);
+ CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
try {
- mInCallService.addCall(call.toCallInfo());
+ mInCallService.addCall(callInfo);
} catch (RemoteException e) {
- Log.e(this, e, "Exception attempting to addCall.");
}
}
}
@@ -101,6 +98,7 @@
// TODO(sail): Wait for all messages to be delivered to the service before unbinding.
unbind();
}
+ mCallIdMapper.removeCall(call);
}
@Override
@@ -109,29 +107,27 @@
return;
}
+ String callId = mCallIdMapper.getCallId(call);
switch (newState) {
case ACTIVE:
- Log.i(this, "Mark call as ACTIVE: %s", call.getId());
+ Log.i(this, "Mark call as ACTIVE: %s", callId);
try {
- mInCallService.setActive(call.getId());
+ mInCallService.setActive(callId);
} catch (RemoteException e) {
- Log.e(this, e, "Exception attempting to call setActive.");
}
break;
case ON_HOLD:
- Log.i(this, "Mark call as HOLD: %s", call.getId());
+ Log.i(this, "Mark call as HOLD: %s", callId);
try {
- mInCallService.setOnHold(call.getId());
+ mInCallService.setOnHold(callId);
} catch (RemoteException e) {
- Log.e(this, e, "Exception attempting to call setOnHold.");
}
break;
case DISCONNECTED:
- Log.i(this, "Mark call as DISCONNECTED: %s", call.getId());
+ Log.i(this, "Mark call as DISCONNECTED: %s", callId);
try {
- mInCallService.setDisconnected(call.getId(), call.getDisconnectCause());
+ mInCallService.setDisconnected(callId, call.getDisconnectCause());
} catch (RemoteException e) {
- Log.e(this, e, "Exception attempting to call setDisconnected.");
}
break;
default:
@@ -147,7 +143,6 @@
try {
mInCallService.onAudioStateChanged(newAudioState);
} catch (RemoteException e) {
- Log.e(this, e, "Exception attempting to update audio state.");
}
}
}
@@ -189,7 +184,7 @@
/**
* Persists the {@link IInCallService} instance and starts the communication between
- * CallsManager and in-call app by sending the first update to in-call app. This method is
+ * this class and in-call app by sending the first update to in-call app. This method is
* called after a successful binding connection is established.
*
* @param service The {@link IInCallService} implementation.
@@ -199,7 +194,8 @@
mInCallService = IInCallService.Stub.asInterface(service);
try {
- mInCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance()));
+ mInCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),
+ mCallIdMapper));
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-call adapter.");
mInCallService = null;
diff --git a/src/com/android/telecomm/IncomingCallsManager.java b/src/com/android/telecomm/IncomingCallsManager.java
index 0d98dc4..41b98f2 100644
--- a/src/com/android/telecomm/IncomingCallsManager.java
+++ b/src/com/android/telecomm/IncomingCallsManager.java
@@ -22,8 +22,9 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
-import java.util.Map;
+import java.util.Set;
/**
* Used to retrieve details about an incoming call. This is invoked after an incoming call intent.
@@ -31,9 +32,7 @@
final class IncomingCallsManager {
private final Switchboard mSwitchboard;
-
- /** Maps call ID to the call. */
- private final Map<String, Call> mPendingIncomingCalls = Maps.newHashMap();
+ private final Set<Call> mPendingIncomingCalls = Sets.newLinkedHashSet();
/**
* Persists the specified parameters.
@@ -55,19 +54,18 @@
ThreadUtil.checkOnMainThread();
Log.d(this, "retrieveIncomingCall");
- final String callId = call.getId();
// Just to be safe, lets make sure we're not already processing this call.
- Preconditions.checkState(!mPendingIncomingCalls.containsKey(callId));
+ Preconditions.checkState(!mPendingIncomingCalls.contains(call));
- mPendingIncomingCalls.put(callId, call);
+ mPendingIncomingCalls.add(call);
Runnable errorCallback = new Runnable() {
@Override public void run() {
- handleFailedIncomingCall(callId);
+ handleFailedIncomingCall(call);
}
};
- call.getCallService().setIncomingCallId(callId, extras, errorCallback);
+ call.getCallService().setIncomingCallId(call, extras, errorCallback);
}
/**
@@ -76,27 +74,25 @@
*
* @param callInfo The details of the call.
*/
- void handleSuccessfulIncomingCall(CallInfo callInfo) {
+ void handleSuccessfulIncomingCall(Call call, CallInfo callInfo) {
ThreadUtil.checkOnMainThread();
- Call call = mPendingIncomingCalls.remove(callInfo.getId());
- if (call != null) {
- Log.d(this, "Incoming call %s found.", call.getId());
+ if (mPendingIncomingCalls.contains(call)) {
+ Log.d(this, "Incoming call %s found.", call);
+ mPendingIncomingCalls.remove(call);
mSwitchboard.handleSuccessfulIncomingCall(call, callInfo);
}
}
/**
* Notifies switchboard of the failed incoming call after removing it from the pending list.
- *
- * @param callId The ID of the call.
*/
- void handleFailedIncomingCall(String callId) {
+ void handleFailedIncomingCall(Call call) {
ThreadUtil.checkOnMainThread();
- Call call = mPendingIncomingCalls.remove(callId);
- if (call != null) {
+ if (mPendingIncomingCalls.contains(call)) {
Log.i(this, "Failed to get details for incoming call %s", call);
+ mPendingIncomingCalls.remove(call);
// The call was found still waiting for details. Consider it failed.
mSwitchboard.handleFailedIncomingCall(call);
}
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index 3d41fe7..315a9bd 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -21,7 +21,6 @@
import android.os.RemoteException;
import android.telecomm.CallState;
import android.telecomm.CallServiceDescriptor;
-import android.telecomm.CallServiceSelector.CallServiceSelectionResponse;
import com.google.android.collect.Sets;
import com.google.common.collect.Maps;
@@ -164,15 +163,14 @@
* TODO(gilad): Consider making this class stateful, potentially rejecting out-of-order/
* unexpected invocations (i.e. beyond checking for unexpected call IDs).
*
- * @param callId The ID of the call.
* @param isCompatible True if the call-service is compatible with the corresponding call and
* false otherwise.
*/
- void setIsCompatibleWith(String callId, boolean isCompatible) {
- Log.v(this, "setIsCompatibleWith, callId: %s, isCompatible: %b", callId, isCompatible);
- if (!callId.equals(mCall.getId())) {
- Log.wtf(this, "setIsCompatibleWith invoked with unexpected call ID: %s - expected call"
- + " ID: %s", callId, mCall.getId());
+ void setIsCompatibleWith(Call call, boolean isCompatible) {
+ Log.v(this, "setIsCompatibleWith, call: %s, isCompatible: %b", call, isCompatible);
+ if (call != mCall) {
+ Log.wtf(this, "setIsCompatibleWith invoked with unexpected call: %s, expected call: %s",
+ call, mCall);
return;
}
@@ -180,7 +178,7 @@
CallServiceWrapper callService = mCall.getCallService();
if (callService != null) {
if (isCompatible) {
- callService.call(mCall.toCallInfo(), mNextCallServiceCallback);
+ callService.call(mCall);
return;
}
mIncompatibleCallServices.add(callService);
@@ -198,6 +196,7 @@
ThreadUtil.checkOnMainThread();
if (!mIsAborted) {
mIsAborted = true;
+ // This will also clear the call's call service and selector.
mOutgoingCallsManager.handleFailedOutgoingCall(mCall, true /* isAborted */);
}
}
@@ -232,9 +231,23 @@
Log.v(this, "handleFailedCallAttempt");
if (!mIsAborted) {
ThreadUtil.checkOnMainThread();
+ attemptNextCallService();
+ }
+ }
- mCall.clearCallService();
- mCall.clearCallServiceSelector();
+ /**
+ * Persists the ordered-list of call-service descriptor as selected by the current selector and
+ * starts iterating through the corresponding call services continuing the attempt to place the
+ * call.
+ *
+ * @param descriptors The (ordered) list of call-service descriptor.
+ */
+ void processSelectedCallServices(List<CallServiceDescriptor> descriptors) {
+ Log.v(this, "processSelectedCallServices");
+ if (descriptors == null || descriptors.isEmpty()) {
+ attemptNextSelector();
+ } else if (mCallServiceDescriptorIterator == null) {
+ mCallServiceDescriptorIterator = descriptors.iterator();
attemptNextCallService();
}
}
@@ -252,41 +265,15 @@
if (mSelectorIterator.hasNext()) {
CallServiceSelectorWrapper selector = mSelectorIterator.next();
mCall.setCallServiceSelector(selector);
-
- CallServiceSelectionResponse responseCallback = new CallServiceSelectionResponse() {
- @Override
- public void setSelectedCallServices(List<CallServiceDescriptor> callServices) {
- processSelectedCallServiceDescriptors(callServices);
- }
- };
- selector.select(mCall.toCallInfo(), mCallServiceDescriptors, responseCallback,
- mNextSelectorCallback);
+ selector.select(mCall, mCallServiceDescriptors, mNextSelectorCallback);
} else {
Log.v(this, "attemptNextSelector, no more selectors, failing");
+ mCall.clearCallServiceSelector();
mOutgoingCallsManager.handleFailedOutgoingCall(mCall, false /* isAborted */);
}
}
/**
- * Persists the ordered-list of call-service descriptor as selected by the current selector and
- * starts iterating through the corresponding call services continuing the attempt to place the
- * call.
- *
- * @selectedCallServiceDescriptors The (ordered) list of call-service descriptor.
- */
- private void processSelectedCallServiceDescriptors(
- List<CallServiceDescriptor> selectedCallServiceDescriptors) {
-
- Log.v(this, "processSelectedCallServiceDescriptors");
- if (selectedCallServiceDescriptors == null || selectedCallServiceDescriptors.isEmpty()) {
- attemptNextSelector();
- } else if (mCallServiceDescriptorIterator == null) {
- mCallServiceDescriptorIterator = selectedCallServiceDescriptors.iterator();
- attemptNextCallService();
- }
- }
-
- /**
* Attempts to place the call using the call service specified by the next call-service
* descriptor of mCallServiceDescriptorIterator. If there are no more call services to
* attempt, the process continues to the next call-service selector via
@@ -310,10 +297,11 @@
} else {
mAttemptedCallServices.add(callService);
mCall.setCallService(callService);
- callService.isCompatibleWith(mCall.toCallInfo(), mNextCallServiceCallback);
+ callService.isCompatibleWith(mCall, mNextCallServiceCallback);
}
} else {
mCallServiceDescriptorIterator = null;
+ mCall.clearCallService();
attemptNextSelector();
}
}
diff --git a/src/com/android/telecomm/OutgoingCallsManager.java b/src/com/android/telecomm/OutgoingCallsManager.java
index 50c24ea..def5211 100644
--- a/src/com/android/telecomm/OutgoingCallsManager.java
+++ b/src/com/android/telecomm/OutgoingCallsManager.java
@@ -16,17 +16,20 @@
package com.android.telecomm;
+import android.telecomm.CallServiceDescriptor;
+
import com.android.internal.telecomm.ICallServiceSelector;
import com.google.common.collect.Maps;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Responsible for placing all outgoing calls. For each outgoing call, this class creates an
* instance of {@link OutgoingCallProcessor} which handles the details of connecting to the
- * appropriate call service and placing the call. This class maintains a mapping from call ID
+ * appropriate call service and placing the call. This class maintains a mapping from call
* to {@link OutgoingCallProcessor} so that other classes (Switchboard, CallServiceAdapter, etc),
* can simply call into this class instead of individual OutgoingCallProcessors.
*/
@@ -34,9 +37,9 @@
private final Switchboard mSwitchboard;
/**
- * Maps call IDs to {@link OutgoingCallProcessor}s.
+ * Maps call to {@link OutgoingCallProcessor}s.
*/
- private Map<String, OutgoingCallProcessor> mOutgoingCallProcessors = Maps.newHashMap();
+ private Map<Call, OutgoingCallProcessor> mOutgoingCallProcessors = Maps.newHashMap();
/** Persists specified parameters. */
OutgoingCallsManager(Switchboard switchboard) {
@@ -58,7 +61,7 @@
Set<CallServiceWrapper> callServices,
Collection<CallServiceSelectorWrapper> selectors) {
- Log.i(this, "Placing an outgoing call (%s)", call.getId());
+ Log.i(this, "Placing an outgoing call: %s", call);
// Create the processor for this (outgoing) call and store it in a map such that call
// attempts can be aborted etc.
@@ -66,26 +69,24 @@
OutgoingCallProcessor processor =
new OutgoingCallProcessor(call, callServices, selectors, this, mSwitchboard);
- mOutgoingCallProcessors.put(call.getId(), processor);
+ mOutgoingCallProcessors.put(call, processor);
processor.process();
}
/**
- * Forwards the compatibility status from the call-service implementation to the corresponding
- * outgoing-call processor.
+ * Forwards the compatibility status from the call-service to the corresponding outgoing-call
+ * processor.
*
- * @param callId The ID of the call.
- * @param isCompatible True if the call-service is compatible with the corresponding call and
- * false otherwise.
+ * @param isCompatible True if the call-service is compatible with the call.
*/
- void setIsCompatibleWith(String callId, boolean isCompatible) {
- Log.v(this, "setIsCompatibleWith, callId %s, isCompatible: %b", callId, isCompatible);
- OutgoingCallProcessor processor = mOutgoingCallProcessors.get(callId);
+ void setIsCompatibleWith(Call call, boolean isCompatible) {
+ Log.v(this, "setIsCompatibleWith, call %s, isCompatible: %b", call, isCompatible);
+ OutgoingCallProcessor processor = mOutgoingCallProcessors.get(call);
if (processor == null) {
// Shouldn't happen, so log a wtf if it does.
Log.wtf(this, "Received unexpected setCompatibleWith notification.");
} else {
- processor.setIsCompatibleWith(callId, isCompatible);
+ processor.setIsCompatibleWith(call, isCompatible);
}
}
@@ -93,12 +94,10 @@
* Removes the outgoing call processor mapping for the successful call and returns execution to
* the switchboard. This method is invoked from {@link CallServiceAdapter} after a call service
* has notified Telecomm that it successfully placed the call.
- *
- * @param callId The ID of the call.
*/
- void handleSuccessfulCallAttempt(String callId) {
- Log.v(this, "handleSuccessfulCallAttempt, callId: %s", callId);
- OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(callId);
+ void handleSuccessfulCallAttempt(Call call) {
+ Log.v(this, "handleSuccessfulCallAttempt, call: %s", call);
+ OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(call);
if (processor == null) {
// Shouldn't happen, so log a wtf if it does.
@@ -114,14 +113,12 @@
* service. This method is called from {@link CallServiceAdapter} after a call service has
* notified Telecomm that it could not place the call.
*
- * @param callId The ID of the failed outgoing call.
* @param reason The call-service supplied reason for the failed call attempt.
*/
- void handleFailedCallAttempt(String callId, String reason) {
- Log.v(this, "handleFailedCallAttempt, callId: %s, reason: %s", callId, reason);
- OutgoingCallProcessor processor = mOutgoingCallProcessors.get(callId);
+ void handleFailedCallAttempt(Call call, String reason) {
+ Log.v(this, "handleFailedCallAttempt, call: %s, reason: %s", call, reason);
+ OutgoingCallProcessor processor = mOutgoingCallProcessors.get(call);
- // TODO(santoscordon): Consider combining the check here and in handleSuccessfulCallAttempt.
if (processor == null) {
// Shouldn't happen, so log a wtf if it does.
Log.wtf(this, "Received an unexpected failed-call notification.");
@@ -142,18 +139,33 @@
*/
void handleFailedOutgoingCall(Call call, boolean isAborted) {
Log.v(this, "handleFailedOutgoingCall, call: %s", call);
- mOutgoingCallProcessors.remove(call.getId());
+ mOutgoingCallProcessors.remove(call);
mSwitchboard.handleFailedOutgoingCall(call, isAborted);
}
/**
+ * Forwards the selected call service from the selector to the corresponding outgoing-call
+ * processor.
+ */
+ void processSelectedCallServices(Call call, List<CallServiceDescriptor> descriptors) {
+ Log.v(this, "processSelectedCallServices, call %s, descriptors: %s", call, descriptors);
+ OutgoingCallProcessor processor = mOutgoingCallProcessors.get(call);
+ if (processor == null) {
+ // Shouldn't happen, so log a wtf if it does.
+ Log.wtf(this, "Received unexpected setSelectedCallServices notification.");
+ } else {
+ processor.processSelectedCallServices(descriptors);
+ }
+ }
+
+ /**
* Aborts any ongoing attempts to connect the specified (outgoing) call.
*
* @param call The call to be aborted.
*/
void abort(Call call) {
Log.v(this, "abort, call: %s", call);
- OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(call.getId());
+ OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(call);
if (processor != null) {
processor.abort();
}
diff --git a/src/com/android/telecomm/PhoneStateBroadcaster.java b/src/com/android/telecomm/PhoneStateBroadcaster.java
index 5728802..f929f1f 100644
--- a/src/com/android/telecomm/PhoneStateBroadcaster.java
+++ b/src/com/android/telecomm/PhoneStateBroadcaster.java
@@ -21,6 +21,7 @@
import android.telecomm.CallService;
import android.telecomm.CallState;
import android.telecomm.TelecommConstants;
+import android.telephony.DisconnectCause;
import android.telephony.TelephonyManager;
/**
@@ -46,7 +47,7 @@
break;
default:
Log.w(this, "Call is in an unknown state (%s), not broadcasting: %s",
- newState, call.getId());
+ newState, call);
return;
}
sendPhoneStateChangedBroadcast(call, phoneState);
@@ -57,8 +58,6 @@
Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
intent.putExtra(TelephonyManager.EXTRA_STATE, phoneState);
- // TODO: See if we can add this (the current API doesn't have a callId).
- intent.putExtra(TelecommConstants.EXTRA_CALL_ID, call.getId());
// Populate both, since the original API was needlessly complicated.
String callHandle = call.getHandle().getSchemeSpecificPart();
@@ -71,6 +70,21 @@
if (callService != null) {
intent.putExtra(CallService.class.getName(), callService.getComponentName());
}
+
+ // TODO: Replace these with real constants once this API has been vetted.
+ int disconnectCause = call.getDisconnectCause();
+ String disconnectMessage = call.getDisconnectMessage();
+ if (disconnectCause != DisconnectCause.NOT_VALID) {
+ intent.putExtra(TelecommConstants.EXTRA_CALL_DISCONNECT_CAUSE, disconnectCause);
+ if (disconnectMessage == null) {
+ disconnectMessage = DisconnectCause.toString(disconnectCause);
+ }
+ }
+ if (disconnectMessage != null) {
+ intent.putExtra(TelecommConstants.EXTRA_CALL_DISCONNECT_MESSAGE,
+ call.getDisconnectMessage());
+ }
+
TelecommApp.getInstance().sendBroadcast(intent, Manifest.permission.READ_PHONE_STATE);
Log.i(this, "Broadcasted state change: %s", phoneState);
}
diff --git a/src/com/android/telecomm/RingbackPlayer.java b/src/com/android/telecomm/RingbackPlayer.java
index 5e0834f..df4d861 100644
--- a/src/com/android/telecomm/RingbackPlayer.java
+++ b/src/com/android/telecomm/RingbackPlayer.java
@@ -33,9 +33,9 @@
private final InCallTonePlayer.Factory mPlayerFactory;
/**
- * The ID of the current call for which the ringback tone is being played.
+ * The current call for which the ringback tone is being played.
*/
- private String mCallId;
+ private Call mCall;
/**
* The currently active player.
@@ -83,13 +83,13 @@
Preconditions.checkState(call.getState() == CallState.DIALING);
ThreadUtil.checkOnMainThread();
- if (mCallId != null) {
+ if (mCall != null) {
// We only get here for the foreground call so, there's no reason why there should
- // exist a current dialing call ID.
+ // exist a current dialing call.
Log.wtf(this, "Ringback player thinks there are two foreground-dialing calls.");
}
- mCallId = call.getId();
+ mCall = call;
if (mTonePlayer == null) {
Log.d(this, "Playing the ringback tone.");
mTonePlayer = mPlayerFactory.createPlayer(InCallTonePlayer.TONE_RING_BACK);
@@ -105,10 +105,10 @@
private void stopRingbackForCall(Call call) {
ThreadUtil.checkOnMainThread();
- if (mCallId != null && mCallId.equals(call.getId())) {
+ if (mCall == call) {
// The foreground call is no longer dialing or is no longer the foreground call. In
// either case, stop the ringback tone.
- mCallId = null;
+ mCall = null;
if (mTonePlayer == null) {
Log.w(this, "No player found to stop.");
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
index dbf76d5..bb32cc3 100644
--- a/src/com/android/telecomm/Ringer.java
+++ b/src/com/android/telecomm/Ringer.java
@@ -34,7 +34,7 @@
* Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
* calls and explicit ordering is useful for maintaining the proper state of the ringer.
*/
- private final List<String> mUnansweredCallIds = Lists.newLinkedList();
+ private final List<Call> mUnansweredCalls = Lists.newLinkedList();
private final CallAudioManager mCallAudioManager;
@@ -45,11 +45,11 @@
@Override
public void onCallAdded(Call call) {
if (call.isIncoming() && call.getState() == CallState.RINGING) {
- if (mUnansweredCallIds.contains(call.getId())) {
+ if (mUnansweredCalls.contains(call)) {
Log.wtf(this, "New ringing call is already in list of unanswered calls");
}
- mUnansweredCallIds.add(call.getId());
- if (mUnansweredCallIds.size() == 1) {
+ mUnansweredCalls.add(call);
+ if (mUnansweredCalls.size() == 1) {
// Start the ringer if we are the top-most incoming call (the only one in this
// case).
startRinging();
@@ -59,13 +59,13 @@
@Override
public void onCallRemoved(Call call) {
- removeFromUnansweredCallIds(call.getId());
+ removeFromUnansweredCall(call);
}
@Override
public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
if (newState != CallState.RINGING) {
- removeFromUnansweredCallIds(call.getId());
+ removeFromUnansweredCall(call);
}
}
@@ -81,21 +81,19 @@
private void onRespondedToIncomingCall(Call call) {
// Only stop the ringer if this call is the top-most incoming call.
- if (!mUnansweredCallIds.isEmpty() && mUnansweredCallIds.get(0).equals(call.getId())) {
+ if (!mUnansweredCalls.isEmpty() && mUnansweredCalls.get(0) == call) {
stopRinging();
}
}
/**
* Removes the specified call from the list of unanswered incoming calls and updates the ringer
- * based on the new state of {@link #mUnansweredCallIds}. Safe to call with a call ID that
- * is not present in the list of incoming calls.
- *
- * @param callId The ID of the call.
+ * based on the new state of {@link #mUnansweredCalls}. Safe to call with a call that is not
+ * present in the list of incoming calls.
*/
- private void removeFromUnansweredCallIds(String callId) {
- if (mUnansweredCallIds.remove(callId)) {
- if (mUnansweredCallIds.isEmpty()) {
+ private void removeFromUnansweredCall(Call call) {
+ if (mUnansweredCalls.remove(call)) {
+ if (mUnansweredCalls.isEmpty()) {
stopRinging();
} else {
startRinging();
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index d8c57fb..77c3c16 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -107,7 +107,7 @@
mCallsManager = callsManager;
mOutgoingCallsManager = new OutgoingCallsManager(this);
mIncomingCallsManager = new IncomingCallsManager(this);
- mSelectorRepository = new CallServiceSelectorRepository(this);
+ mSelectorRepository = new CallServiceSelectorRepository(this, mOutgoingCallsManager);
mCallServiceRepository =
new CallServiceRepository(this, mOutgoingCallsManager, mIncomingCallsManager);
@@ -258,8 +258,7 @@
*/
private boolean isTicking() {
// TODO(gilad): return true every time at least one outgoing call is pending (i.e. waiting
- // to be connected by a call service) and also when at least one active call is switch-able
- // between call services, see {@link ICallServiceSelector#isSwitchable}.
+ // to be connected by a call service).
return false;
}
@@ -334,7 +333,7 @@
while (iterator.hasNext()) {
Call call = iterator.next();
if (call.getAgeInMilliseconds() >= newCallTimeoutMs) {
- Log.d(this, "Call %s timed out.", call.getId());
+ Log.d(this, "Call %s timed out.", call);
mOutgoingCallsManager.abort(call);
calls.remove(call);
diff --git a/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java b/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
index dfcb6fd..f759291 100644
--- a/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
+++ b/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
@@ -19,6 +19,7 @@
import android.telecomm.CallInfo;
import android.telecomm.CallServiceDescriptor;
import android.telecomm.CallServiceSelector;
+import android.telecomm.CallServiceSelectorAdapter;
import com.google.common.collect.Lists;
@@ -29,26 +30,25 @@
* was given. Also returns false for every request on switchability.
*/
public class DummyCallServiceSelector extends CallServiceSelector {
+ private CallServiceSelectorAdapter mAdapter;
+
@Override
- protected void isSwitchable(CallInfo callInfo, CallSwitchabilityResponse response) {
- response.setSwitchable(false);
+ protected void setCallServiceSelectorAdapter(CallServiceSelectorAdapter adapter) {
+ mAdapter = adapter;
}
@Override
- protected void select(
- CallInfo callInfo, List<CallServiceDescriptor> callServiceDescriptors,
- CallServiceSelectionResponse response) {
-
+ protected void select(CallInfo callInfo, List<CallServiceDescriptor> descriptors) {
List<CallServiceDescriptor> orderedList = Lists.newLinkedList();
// Make sure that the test call services are the only ones
- for (CallServiceDescriptor descriptor : callServiceDescriptors) {
+ for (CallServiceDescriptor descriptor : descriptors) {
String packageName = descriptor.getServiceComponent().getPackageName();
if (getPackageName().equals(packageName)) {
orderedList.add(descriptor);
}
}
- response.setSelectedCallServices(orderedList);
+ mAdapter.setSelectedCallServices(callInfo.getId(), orderedList);
}
}