Merge "Repeat ringtones every 3 seconds." into lmp-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 6f7177b..9eeaa7f 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -186,8 +186,6 @@
private final List<Call> mConferenceableCalls = new ArrayList<>();
- private PhoneAccountHandle mPhoneAccountHandle;
-
private long mConnectTimeMillis = 0;
/** The state of the call. */
@@ -308,8 +306,8 @@
setHandle(handle);
setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
mGatewayInfo = gatewayInfo;
- mConnectionManagerPhoneAccountHandle = connectionManagerPhoneAccountHandle;
- mTargetPhoneAccountHandle = targetPhoneAccountHandle;
+ setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
+ setTargetPhoneAccount(targetPhoneAccountHandle);
mIsIncoming = isIncoming;
mIsConference = isConference;
maybeLoadCannedSmsResponses();
@@ -905,7 +903,7 @@
return mConferenceableCalls;
}
- private boolean can(int capability) {
+ boolean can(int capability) {
return (mCallCapabilities & capability) == capability;
}
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index a29ef54..060c33b 100644
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -123,7 +123,7 @@
* {@link android.provider.CallLog.Calls#OUTGOING_TYPE}
* {@link android.provider.CallLog.Calls#MISSED_TYPE}
*/
- private void logCall(Call call, int callLogType) {
+ void logCall(Call call, int callLogType) {
final long creationTime = call.getCreationTimeMillis();
final long age = call.getAgeMillis();
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 8a5647c..2636c2f 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -18,19 +18,25 @@
import android.net.Uri;
import android.os.Bundle;
+
+import android.provider.CallLog.Calls;
import android.telecom.AudioState;
import android.telecom.CallState;
import android.telecom.GatewayInfo;
import android.telecom.ParcelableConference;
import android.telecom.PhoneAccountHandle;
+import android.telecom.PhoneCapabilities;
import android.telephony.DisconnectCause;
import android.telephony.TelephonyManager;
+import com.android.internal.util.ArrayUtils;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -64,6 +70,17 @@
private static final CallsManager INSTANCE = new CallsManager();
+ private static final int MAXIMUM_LIVE_CALLS = 1;
+ private static final int MAXIMUM_HOLD_CALLS = 1;
+ private static final int MAXIMUM_RINGING_CALLS = 1;
+ private static final int MAXIMUM_OUTGOING_CALLS = 1;
+
+ private static final int[] LIVE_CALL_STATES =
+ {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE};
+
+ private static final int[] OUTGOING_CALL_STATES =
+ {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING};
+
/**
* 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.
@@ -90,6 +107,7 @@
private final TtyManager mTtyManager;
private final ProximitySensorManager mProximitySensorManager;
private final PhoneStateBroadcaster mPhoneStateBroadcaster;
+ private final CallLogManager mCallLogManager;
/**
* The call the user is currently interacting with. This is the call that should have audio
@@ -117,9 +135,10 @@
mTtyManager = new TtyManager(app, mWiredHeadsetManager);
mProximitySensorManager = new ProximitySensorManager(app);
mPhoneStateBroadcaster = new PhoneStateBroadcaster();
+ mCallLogManager = new CallLogManager(app);
mListeners.add(statusBarNotifier);
- mListeners.add(new CallLogManager(app));
+ mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
mListeners.add(mInCallController);
mListeners.add(mRinger);
@@ -161,10 +180,20 @@
}
@Override
- public void onSuccessfulIncomingCall(Call call) {
+ public void onSuccessfulIncomingCall(Call incomingCall) {
Log.d(this, "onSuccessfulIncomingCall");
- setCallState(call, CallState.RINGING);
- addCall(call);
+ setCallState(incomingCall, CallState.RINGING);
+
+ if (hasMaximumRingingCalls()) {
+ incomingCall.reject(false, null);
+ // since the call was not added to the list of calls, we have to call the missed
+ // call notifier and the call logger manually.
+ TelecomApp.getInstance().getMissedCallNotifier()
+ .showMissedCallNotification(incomingCall);
+ mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
+ } else {
+ addCall(incomingCall);
+ }
}
@Override
@@ -284,16 +313,6 @@
* @param extras The optional extras Bundle passed with the intent used for the incoming call.
*/
Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
- // We only allow a single outgoing call at any given time. Before placing a call, make sure
- // there doesn't already exist another outgoing call.
- Call call = getFirstCallWithState(CallState.NEW, CallState.DIALING,
- CallState.CONNECTING, CallState.PRE_DIAL_WAIT);
-
- if (call != null) {
- Log.i(this, "Canceling simultaneous outgoing call.");
- return null;
- }
-
TelecomApp app = TelecomApp.getInstance();
// Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
@@ -320,7 +339,7 @@
// Create a call with original handle. The handle may be changed when the call is attached
// to a connection service, but in most cases will remain the same.
- call = new Call(
+ Call call = new Call(
mConnectionServiceRepository,
handle,
null /* gatewayInfo */,
@@ -330,8 +349,16 @@
false /* isConference */);
call.setExtras(extras);
- final boolean emergencyCall = TelephonyUtil.shouldProcessAsEmergency(app, call.getHandle());
- if (phoneAccountHandle == null && !emergencyCall) {
+ boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(app, call.getHandle());
+
+ // Do not support any more live calls. Our options are to move a call to hold, disconnect
+ // a call, or cancel this call altogether.
+ if (!makeRoomForOutgoingCall(call, isEmergencyCall)) {
+ // just cancel at this point.
+ return null;
+ }
+
+ if (phoneAccountHandle == null && !isEmergencyCall) {
// This is the state where the user is expected to select an account
call.setState(CallState.PRE_DIAL_WAIT);
} else {
@@ -379,13 +406,13 @@
call.setVideoState(videoState);
TelecomApp app = TelecomApp.getInstance();
- final boolean emergencyCall = TelephonyUtil.shouldProcessAsEmergency(app, call.getHandle());
- if (emergencyCall) {
+ boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(app, call.getHandle());
+ if (isEmergencyCall) {
// Emergency -- CreateConnectionProcessor will choose accounts automatically
call.setTargetPhoneAccount(null);
}
- if (call.getTargetPhoneAccount() != null || emergencyCall) {
+ if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
// If the account has been set, proceed to place the outgoing call.
// Otherwise the connection will be initiated when the account is set by the user.
call.startCreateConnection();
@@ -419,9 +446,18 @@
if (mForegroundCall != null && mForegroundCall != call &&
(mForegroundCall.isActive() ||
mForegroundCall.getState() == CallState.DIALING)) {
- Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
- mForegroundCall, call);
- mForegroundCall.hold();
+ if (0 == (mForegroundCall.getCallCapabilities() & PhoneCapabilities.HOLD)) {
+ // This call does not support hold. If it is from a different connection
+ // service, then disconnect it, otherwise allow the connection service to
+ // figure out the right states.
+ if (mForegroundCall.getConnectionService() != call.getConnectionService()) {
+ mForegroundCall.disconnect();
+ }
+ } else {
+ Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
+ mForegroundCall, call);
+ mForegroundCall.hold();
+ }
// TODO: Wait until we get confirmation of the active call being
// on-hold before answering the new call.
// TODO: Import logic from CallManager.acceptCall()
@@ -579,10 +615,23 @@
void phoneAccountSelected(Call call, PhoneAccountHandle account) {
if (!mCalls.contains(call)) {
- Log.i(this, "Attemped to add account to unknown call %s", call);
+ Log.i(this, "Attempted to add account to unknown call %s", call);
} else {
+ // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
+ // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the
+ // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
+ // respecting a rewritten number or a canceled number. This is unlikely since
+ // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
+ // a phone account from the in-call UI.
call.setTargetPhoneAccount(account);
- call.startCreateConnection();
+
+ // Note: emergency calls never go through account selection dialog so they never
+ // arrive here.
+ if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
+ call.startCreateConnection();
+ } else {
+ call.disconnect();
+ }
}
}
@@ -618,8 +667,8 @@
}
/**
- * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the last
- * live call, then also disconnect from the in-call controller.
+ * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
+ * last live call, then also disconnect from the in-call controller.
*
* @param disconnectCause The disconnect reason, see {@link android.telephony.DisconnectCause}.
* @param disconnectMessage Optional message about the disconnect.
@@ -712,12 +761,18 @@
return true;
}
+ Call getFirstCallWithState(int... states) {
+ return getFirstCallWithState(null, states);
+ }
+
/**
* Returns the first call that it finds with the given states. The states are treated as having
* priority order so that any call with the first state will be returned before any call with
* states listed later in the parameter list.
+ *
+ * @param callToSkip Call that this method should skip while searching
*/
- Call getFirstCallWithState(int... states) {
+ Call getFirstCallWithState(Call callToSkip, int... states) {
for (int currentState : states) {
// check the foreground first
if (mForegroundCall != null && mForegroundCall.getState() == currentState) {
@@ -725,6 +780,15 @@
}
for (Call call : mCalls) {
+ if (Objects.equals(callToSkip, call)) {
+ continue;
+ }
+
+ // Only operate on top-level calls
+ if (call.getParentCall() != null) {
+ continue;
+ }
+
if (currentState == call.getState()) {
return call;
}
@@ -849,6 +913,11 @@
// be notified when its calls enter and exit foreground state. Foreground will mean that
// the call should play audio and listen to microphone if it wants.
+ // Only top-level calls can be in foreground
+ if (call.getParentCall() != null) {
+ continue;
+ }
+
// Active calls have priority.
if (call.isActive()) {
newForegroundCall = call;
@@ -876,4 +945,78 @@
return (handle != null && handle.getSchemeSpecificPart() != null
&& handle.getSchemeSpecificPart().contains("#"));
}
+
+ private int getNumCallsWithState(int... states) {
+ int count = 0;
+ for (int state : states) {
+ for (Call call : mCalls) {
+ if (call.getState() == state) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ private boolean hasMaximumLiveCalls() {
+ return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
+ }
+
+ private boolean hasMaximumHoldingCalls() {
+ return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
+ }
+
+ private boolean hasMaximumRingingCalls() {
+ return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
+ }
+
+ private boolean hasMaximumOutgoingCalls() {
+ return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
+ }
+
+ private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
+ if (hasMaximumLiveCalls()) {
+ // NOTE: If the amount of live calls changes beyond 1, this logic will probably
+ // have to change.
+ Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES);
+
+ if (hasMaximumHoldingCalls()) {
+ // There is no more room for any more calls, unless it's an emergency.
+ if (isEmergency) {
+ // Kill the current active call, this is easier then trying to disconnect a
+ // holding call and hold an active call.
+ liveCall.disconnect();
+ return true;
+ }
+ return false; // No more room!
+ }
+
+ // We have room for at least one more holding call at this point.
+
+ // First thing, if we are trying to make a call with the same phone account as the live
+ // call, then allow it so that the connection service can make its own decision about
+ // how to handle the new call relative to the current one.
+ if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) {
+ return true;
+ } else if (call.getTargetPhoneAccount() == null) {
+ // Without a phone account, we can't say reliably that the call will fail.
+ // If the user chooses the same phone account as the live call, then it's
+ // still possible that the call can be made (like with CDMA calls not supporting
+ // hold but they still support adding a call by going immediately into conference
+ // mode). Return true here and we'll run this code again after user chooses an
+ // account.
+ return true;
+ }
+
+ // Try to hold the live call before attempting the new outgoing call.
+ if (liveCall.can(PhoneCapabilities.HOLD)) {
+ liveCall.hold();
+ return true;
+ }
+
+ // The live call cannot be held so we're out of luck here. There's no room.
+ return false;
+ }
+ return true;
+ }
}
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 553d2e1..d9ab3b9 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -99,7 +99,7 @@
*
* @param call The missed call.
*/
- private void showMissedCallNotification(Call call) {
+ void showMissedCallNotification(Call call) {
mMissedCallCount++;
final int titleResId;