Merge "Telecom: Refactor to move DEVICE_ID_DEFAULT from vdm to Context" into udc-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 5847960..02dff13 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -2701,7 +2701,8 @@
// hangup, not reject.
setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
if (mTransactionalService != null) {
- mTransactionalService.onReject(this, DisconnectCause.REJECTED);
+ mTransactionalService.onDisconnect(this,
+ new DisconnectCause(DisconnectCause.REJECTED));
} else if (mConnectionService != null) {
mConnectionService.disconnect(this);
} else {
@@ -2714,7 +2715,8 @@
mVideoStateHistory |= mVideoState;
if (mTransactionalService != null) {
- mTransactionalService.onReject(this, DisconnectCause.REJECTED);
+ mTransactionalService.onDisconnect(this,
+ new DisconnectCause(DisconnectCause.REJECTED));
} else if (mConnectionService != null) {
mConnectionService.reject(this, rejectWithMessage, textMessage);
} else {
@@ -2737,7 +2739,8 @@
// Since its simulated reason we can't pass along the reject reason.
setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
if (mTransactionalService != null) {
- mTransactionalService.onReject(this, DisconnectCause.REJECTED);
+ mTransactionalService.onDisconnect(this,
+ new DisconnectCause(DisconnectCause.REJECTED));
} else if (mConnectionService != null) {
mConnectionService.disconnect(this);
} else {
@@ -2749,7 +2752,8 @@
// Ensure video state history tracks video state at time of rejection.
mVideoStateHistory |= mVideoState;
if (mTransactionalService != null) {
- mTransactionalService.onReject(this, rejectReason);
+ mTransactionalService.onDisconnect(this,
+ new DisconnectCause(DisconnectCause.REJECTED));
} else if (mConnectionService != null) {
mConnectionService.rejectWithReason(this, rejectReason);
} else {
@@ -3205,9 +3209,7 @@
* @param extras Associated extras.
*/
public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
- if (mTransactionalService != null) {
- Log.i(this, "sendCallEvent: called on TransactionalService. doing nothing");
- } else if (mConnectionService != null) {
+ if (mConnectionService != null || mTransactionalService != null) {
if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
if (targetSdkVer > Build.VERSION_CODES.P) {
Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
@@ -3230,8 +3232,8 @@
if (extras == null) {
Log.w(this, "sendCallEvent: %s event received with null extras.",
android.telecom.Call.EVENT_REQUEST_HANDOVER);
- mConnectionService.sendCallEvent(this,
- android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+ sendEventToService(this, android.telecom.Call.EVENT_HANDOVER_FAILED,
+ null);
return;
}
Parcelable parcelable = extras.getParcelable(
@@ -3239,8 +3241,7 @@
if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) {
Log.w(this, "sendCallEvent: %s event received with invalid handover acct.",
android.telecom.Call.EVENT_REQUEST_HANDOVER);
- mConnectionService.sendCallEvent(this,
- android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+ sendEventToService(this, android.telecom.Call.EVENT_HANDOVER_FAILED, null);
return;
}
PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable;
@@ -3263,7 +3264,7 @@
));
}
Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
- mConnectionService.sendCallEvent(this, event, extras);
+ sendEventToService(this, event, extras);
}
} else {
Log.e(this, new NullPointerException(),
@@ -3272,6 +3273,17 @@
}
/**
+ * This method should only be called from sendCallEvent(String, int, Bundle).
+ */
+ private void sendEventToService(Call call, String event, Bundle extras) {
+ if (mConnectionService != null) {
+ mConnectionService.sendCallEvent(call, event, extras);
+ } else if (mTransactionalService != null) {
+ mTransactionalService.onEvent(call, event, extras);
+ }
+ }
+
+ /**
* Notifies listeners when a bluetooth quality report is received.
* @param report The bluetooth quality report.
*/
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 56ce17b..cd8a690 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -111,11 +111,11 @@
if (mCallsManager.getCallStreamingController().isStreaming()) {
future.complete(new VoipCallTransactionResult(
- VoipCallTransactionResult.RESULT_SUCCEED, null));
- } else {
- future.complete(new VoipCallTransactionResult(
VoipCallTransactionResult.RESULT_FAILED,
"STREAMING_FAILED_ALREADY_STREAMING"));
+ } else {
+ future.complete(new VoipCallTransactionResult(
+ VoipCallTransactionResult.RESULT_SUCCEED, null));
}
return future;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 48aa9e6..09d8787 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -154,6 +154,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -455,6 +456,9 @@
private LinkedList<HandlerThread> mGraphHandlerThreads;
+ // An executor that can be used to fire off async tasks that do not block Telecom in any manner.
+ private final Executor mAsyncTaskExecutor;
+
private boolean mHasActiveRttCall = false;
private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
@@ -553,7 +557,9 @@
RoleManagerAdapter roleManagerAdapter,
ToastFactory toastFactory,
CallEndpointControllerFactory callEndpointControllerFactory,
- CallAnomalyWatchdog callAnomalyWatchdog) {
+ CallAnomalyWatchdog callAnomalyWatchdog,
+ Executor asyncTaskExecutor,
+ Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter) {
mContext = context;
mLock = lock;
mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -614,7 +620,7 @@
ringtoneFactory, systemVibrator,
new Ringer.VibrationEffectProxy(), mInCallController,
mContext.getSystemService(NotificationManager.class),
- mContext.getSystemService(AccessibilityManager.class));
+ accessibilityManagerAdapter);
mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, audioManager,
mTimeoutsAdapter, mLock);
mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
@@ -670,6 +676,7 @@
mGraphHandlerThreads = new LinkedList<>();
mCallAnomalyWatchdog = callAnomalyWatchdog;
+ mAsyncTaskExecutor = asyncTaskExecutor;
}
public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -3327,7 +3334,8 @@
setCallState(call, CallState.RINGING, "ringing set explicitly");
}
- void markCallAsDialing(Call call) {
+ @VisibleForTesting
+ public void markCallAsDialing(Call call) {
setCallState(call, CallState.DIALING, "dialing set explicitly");
maybeMoveToSpeakerPhone(call);
maybeTurnOffMute(call);
@@ -4893,17 +4901,28 @@
}
}
+ /**
+ * Ensures that the call will be audible to the user by checking if the voice call stream is
+ * audible, and if not increasing the volume to the default value.
+ */
private void ensureCallAudible() {
- AudioManager am = mContext.getSystemService(AudioManager.class);
- if (am == null) {
- Log.w(this, "ensureCallAudible: audio manager is null");
- return;
- }
- if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
- Log.i(this, "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
- am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
- AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
- }
+ // Audio manager APIs can be somewhat slow. To prevent a potential ANR we will fire off
+ // this opreation on the async task executor. Note that this operation does not have any
+ // dependency on any Telecom state, so we can safely launch this on a different thread
+ // without worrying that it is in the Telecom sync lock.
+ mAsyncTaskExecutor.execute(() -> {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ if (am == null) {
+ Log.w(this, "ensureCallAudible: audio manager is null");
+ return;
+ }
+ if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
+ Log.i(this,
+ "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
+ am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
+ }
+ });
}
/**
@@ -5941,18 +5960,24 @@
}
}
- // driver method to create and execute a new TransactionalFocusRequestCallback
- public void transactionRequestNewFocusCall(Call call,
+ /**
+ * Intended for ongoing or new calls that would like to go active/answered and need to
+ * update the mConnectionSvrFocusMgr before setting the state
+ */
+ public void transactionRequestNewFocusCall(Call call, int newCallState,
OutcomeReceiver<Boolean, CallException> callback) {
Log.d(this, "transactionRequestNewFocusCall");
- PendingAction pendingAction = new ActionSetCallState(call, CallState.ACTIVE,
+ PendingAction pendingAction = new ActionSetCallState(call, newCallState,
"transactional ActionSetCallState");
mConnectionSvrFocusMgr
.requestFocus(call,
new TransactionalFocusRequestCallback(pendingAction, call, callback));
}
- // request a new call focus and ensure the request was successful
+ /**
+ * Request a new call focus and ensure the request was successful via an OutcomeReceiver. Also,
+ * include a PendingAction that will execute if the call focus change is successful.
+ */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class TransactionalFocusRequestCallback implements
ConnectionServiceFocusManager.RequestFocusCallback {
@@ -5971,26 +5996,18 @@
@Override
public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) {
Call currentCallFocus = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
-
- if (currentCallFocus == null) {
- Log.i(this, "TransactionalFocusRequestCallback: "
- + "currentCallFocus is null.");
- mCallback.onError(new CallException("currentCallFocus is null",
+ // verify the update was successful before updating the state
+ Log.i(this, "tFRC: currentCallFocus=[%s], targetFocus=[%s]",
+ mTargetCallFocus, currentCallFocus);
+ if (currentCallFocus == null ||
+ !currentCallFocus.getId().equals(mTargetCallFocus.getId())) {
+ mCallback.onError(new CallException("failed to switch focus to requested call",
CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
return;
}
-
- Log.i(this, "TransactionalFocusRequestCallback: targetId=[%s], "
- + "currentId=[%s]", mTargetCallFocus.getId(), currentCallFocus.getId());
- if (!currentCallFocus.getId().equals(mTargetCallFocus.getId())) {
- Log.i(this, "TransactionalFocusRequestCallback: "
- + "currentCallFocus is not equal to targetCallFocus.");
- mCallback.onError(new CallException("current focus is not target focus",
- CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
- return;
- }
- mPendingAction.performAction();
- mCallback.onResult(true);
+ // at this point, we know the FocusManager is able to update successfully
+ mPendingAction.performAction(); // set the call state
+ mCallback.onResult(true); // complete the transaction
}
}
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 171745f..c04cf6b 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -21,6 +21,7 @@
import static android.provider.CallLog.Calls.USER_MISSED_NO_VIBRATE;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Person;
@@ -54,20 +55,25 @@
*/
@VisibleForTesting
public class Ringer {
- /**
- * Flag only for local debugging. Do not submit enabled.
- */
- private static final boolean DEBUG_RINGER = false;
-
- public static class VibrationEffectProxy {
- public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
- return VibrationEffect.createWaveform(timings, amplitudes, repeat);
+ public interface AccessibilityManagerAdapter {
+ boolean startFlashNotificationSequence(@NonNull Context context,
+ @AccessibilityManager.FlashNotificationReason int reason);
+ boolean stopFlashNotificationSequence(@NonNull Context context);
}
+ /**
+ * Flag only for local debugging. Do not submit enabled.
+ */
+ private static final boolean DEBUG_RINGER = false;
- public VibrationEffect get(Uri ringtoneUri, Context context) {
- return VibrationEffect.get(ringtoneUri, context);
+ public static class VibrationEffectProxy {
+ public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
+ return VibrationEffect.createWaveform(timings, amplitudes, repeat);
+ }
+
+ public VibrationEffect get(Uri ringtoneUri, Context context) {
+ return VibrationEffect.get(ringtoneUri, context);
+ }
}
- }
@VisibleForTesting
public VibrationEffect mDefaultVibrationEffect;
@@ -162,7 +168,7 @@
private RingtoneFactory mRingtoneFactory;
private AudioManager mAudioManager;
private NotificationManager mNotificationManager;
- private AccessibilityManager mAccessibilityManager;
+ private AccessibilityManagerAdapter mAccessibilityManagerAdapter;
/**
* Call objects that are ringing, vibrating or call-waiting. These are used only for logging
@@ -197,7 +203,7 @@
VibrationEffectProxy vibrationEffectProxy,
InCallController inCallController,
NotificationManager notificationManager,
- AccessibilityManager accessibilityManager) {
+ AccessibilityManagerAdapter accessibilityManagerAdapter) {
mLock = new Object();
mSystemSettingsUtil = systemSettingsUtil;
@@ -211,7 +217,7 @@
mInCallController = inCallController;
mVibrationEffectProxy = vibrationEffectProxy;
mNotificationManager = notificationManager;
- mAccessibilityManager = accessibilityManager;
+ mAccessibilityManagerAdapter = accessibilityManagerAdapter;
if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
@@ -295,10 +301,11 @@
stopCallWaiting();
final boolean shouldFlash = attributes.shouldRingForContact();
- if (mAccessibilityManager != null && shouldFlash) {
+ if (mAccessibilityManagerAdapter != null && shouldFlash) {
Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
- getHandler().post(() -> mAccessibilityManager.startFlashNotificationSequence(mContext,
- AccessibilityManager.FLASH_REASON_CALL));
+ getHandler().post(() ->
+ mAccessibilityManagerAdapter.startFlashNotificationSequence(mContext,
+ AccessibilityManager.FLASH_REASON_CALL));
}
// Determine if the settings and DND mode indicate that the vibrator can be used right now.
@@ -512,9 +519,10 @@
public void stopRinging() {
final Call foregroundCall = mRingingCall != null ? mRingingCall : mVibratingCall;
- if (mAccessibilityManager != null) {
+ if (mAccessibilityManagerAdapter != null) {
Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_STOP);
- getHandler().post(() -> mAccessibilityManager.stopFlashNotificationSequence(mContext));
+ getHandler().post(() ->
+ mAccessibilityManagerAdapter.stopFlashNotificationSequence(mContext));
}
synchronized (mLock) {
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 63852f3..f9c2508 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -31,6 +31,7 @@
import android.telecom.Log;
import android.telecom.PhoneAccountHandle;
import android.telephony.AnomalyReporter;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -208,7 +209,8 @@
ClockProxy clockProxy,
RoleManagerAdapter roleManagerAdapter,
ContactsAsyncHelper.Factory contactsAsyncHelperFactory,
- DeviceIdleControllerAdapter deviceIdleControllerAdapter) {
+ DeviceIdleControllerAdapter deviceIdleControllerAdapter,
+ Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter) {
mContext = context.getApplicationContext();
LogUtils.initLogging(mContext);
AnomalyReporter.initialize(mContext);
@@ -332,6 +334,7 @@
CallAnomalyWatchdog callAnomalyWatchdog = new CallAnomalyWatchdog(
Executors.newSingleThreadScheduledExecutor(),
mLock, timeoutsAdapter, clockProxy);
+
mCallsManager = new CallsManager(
mContext,
mLock,
@@ -363,7 +366,9 @@
roleManagerAdapter,
toastFactory,
callEndpointControllerFactory,
- callAnomalyWatchdog);
+ callAnomalyWatchdog,
+ Executors.newSingleThreadExecutor(),
+ accessibilityManagerAdapter);
mIncomingCallNotifier = incomingCallNotifier;
incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index 6931996..d97d192 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -38,6 +38,7 @@
import com.android.internal.telecom.ICallControl;
import com.android.internal.telecom.ICallEventCallback;
+import com.android.server.telecom.voip.AnswerCallTransaction;
import com.android.server.telecom.voip.CallEventCallbackAckTransaction;
import com.android.server.telecom.voip.EndpointChangeTransaction;
import com.android.server.telecom.voip.HoldCallTransaction;
@@ -67,7 +68,7 @@
// CallControl : Client (ex. voip app) --> Telecom
public static final String SET_ACTIVE = "SetActive";
public static final String SET_INACTIVE = "SetInactive";
- public static final String REJECT = "Reject";
+ public static final String ANSWER = "Answer";
public static final String DISCONNECT = "Disconnect";
public static final String START_STREAMING = "StartStreaming";
@@ -75,7 +76,6 @@
public static final String ON_SET_ACTIVE = "onSetActive";
public static final String ON_SET_INACTIVE = "onSetInactive";
public static final String ON_ANSWER = "onAnswer";
- public static final String ON_REJECT = "onReject";
public static final String ON_DISCONNECT = "onDisconnect";
public static final String ON_STREAMING_STARTED = "onStreamingStarted";
@@ -196,6 +196,17 @@
}
@Override
+ public void answer(int videoState, String callId, android.os.ResultReceiver callback)
+ throws RemoteException {
+ try {
+ Log.startSession("TSW.a");
+ createTransactions(callId, callback, ANSWER, videoState);
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void setInactive(String callId, android.os.ResultReceiver callback)
throws RemoteException {
try {
@@ -238,6 +249,10 @@
case SET_ACTIVE:
addTransactionsToManager(createSetActiveTransactions(call), callback);
break;
+ case ANSWER:
+ addTransactionsToManager(createSetAnswerTransactions(call,
+ (int) objects[0]), callback);
+ break;
case DISCONNECT:
addTransactionsToManager(new EndCallTransaction(mCallsManager,
(DisconnectCause) objects[0], call), callback);
@@ -273,6 +288,28 @@
Log.endSession();
}
}
+
+ /**
+ * Application would like to inform InCallServices of an event
+ */
+ @Override
+ public void sendEvent(String callId, String event, Bundle extras) {
+ try {
+ Log.startSession("TSW.sE");
+ Call call = mTrackedCalls.get(callId);
+ if (call != null) {
+ call.onConnectionEvent(event, extras);
+ }
+ else{
+ Log.i(TAG,
+ "sendEvent: was called but there is no call with id=[%s] cannot be "
+ + "found. Most likely the call has been disconnected");
+ }
+ }
+ finally {
+ Log.endSession();
+ }
+ }
};
private void addTransactionsToManager(VoipCallTransaction transaction,
@@ -366,7 +403,7 @@
Log.i(TAG, String.format(Locale.US, "onSetInactive: callId=[%s]", call.getId()));
mTransactionManager.addTransaction(
new CallEventCallbackAckTransaction(mICallEventCallback,
- ON_SET_INACTIVE, call.getId(), 0), new OutcomeReceiver<>() {
+ ON_SET_INACTIVE, call.getId()), new OutcomeReceiver<>() {
@Override
public void onResult(VoipCallTransactionResult result) {
mCallsManager.markCallAsOnHold(call);
@@ -389,7 +426,7 @@
mTransactionManager.addTransaction(
new CallEventCallbackAckTransaction(mICallEventCallback, ON_DISCONNECT,
- call.getId(), 0), new OutcomeReceiver<>() {
+ call.getId(), cause), new OutcomeReceiver<>() {
@Override
public void onResult(VoipCallTransactionResult result) {
removeCallFromCallsManager(call, cause);
@@ -406,32 +443,6 @@
}
}
- public void onReject(Call call, @android.telecom.Call.RejectReason int rejectReason) {
- try {
- Log.startSession("TSW.oR");
- Log.d(TAG, String.format(Locale.US, "onReject: callId=[%s]", call.getId()));
-
- mTransactionManager.addTransaction(
- new CallEventCallbackAckTransaction(mICallEventCallback, ON_REJECT,
- call.getId(), 0), new OutcomeReceiver<>() {
- @Override
- public void onResult(VoipCallTransactionResult result) {
- removeCallFromCallsManager(call,
- new DisconnectCause(DisconnectCause.REJECTED));
- }
-
- @Override
- public void onError(CallException exception) {
- removeCallFromCallsManager(call,
- new DisconnectCause(DisconnectCause.REJECTED));
- }
- }
- );
- } finally {
- Log.endSession();
- }
- }
-
public void onCallStreamingStarted(Call call) {
try {
Log.startSession("TSW.oCSS");
@@ -440,7 +451,7 @@
mTransactionManager.addTransaction(
new CallEventCallbackAckTransaction(mICallEventCallback, ON_STREAMING_STARTED,
- call.getId(), 0), new OutcomeReceiver<>() {
+ call.getId()), new OutcomeReceiver<>() {
@Override
public void onResult(VoipCallTransactionResult result) {
}
@@ -508,6 +519,15 @@
}
}
+ public void onEvent(Call call, String event, Bundle extras){
+ if (call != null) {
+ try {
+ mICallEventCallback.onEvent(call.getId(), event, extras);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/***
*********************************************************************************************
** Helpers **
@@ -536,13 +556,27 @@
// add t1. hold potential active call
transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
- // add t2. answer current call
+ // add t2. send request to set the current call active
transactions.add(new RequestFocusTransaction(mCallsManager, call));
// send off to Transaction Manager to process
return new SerialTransaction(transactions);
}
+ private SerialTransaction createSetAnswerTransactions(Call call, int videoState) {
+ // create list for multiple transactions
+ List<VoipCallTransaction> transactions = new ArrayList<>();
+
+ // add t1. hold potential active call
+ transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
+
+ // add t2. answer current call
+ transactions.add(new AnswerCallTransaction(mCallsManager, call, videoState));
+
+ // send off to Transaction Manager to process
+ return new SerialTransaction(transactions);
+ }
+
/***
*********************************************************************************************
** FocusManager **
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 9ad0da4..3881154 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -29,6 +29,7 @@
import android.telecom.Log;
import android.telecom.CallerInfoAsyncQuery;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.telecom.IInternalServiceRetriever;
import com.android.internal.telecom.ITelecomLoader;
@@ -53,6 +54,7 @@
import com.android.server.telecom.ProximitySensorManagerFactory;
import com.android.server.telecom.InCallWakeLockController;
import com.android.server.telecom.ProximitySensorManager;
+import com.android.server.telecom.Ringer;
import com.android.server.telecom.RoleManagerAdapterImpl;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.TelecomWakeLock;
@@ -191,7 +193,22 @@
new RoleManagerAdapterImpl(context,
(RoleManager) context.getSystemService(Context.ROLE_SERVICE)),
new ContactsAsyncHelper.Factory(),
- internalServiceRetriever.getDeviceIdleController()));
+ internalServiceRetriever.getDeviceIdleController(),
+ new Ringer.AccessibilityManagerAdapter() {
+ @Override
+ public boolean startFlashNotificationSequence(
+ @androidx.annotation.NonNull Context context, int reason) {
+ return context.getSystemService(AccessibilityManager.class)
+ .startFlashNotificationSequence(context, reason);
+ }
+
+ @Override
+ public boolean stopFlashNotificationSequence(
+ @androidx.annotation.NonNull Context context) {
+ return context.getSystemService(AccessibilityManager.class)
+ .stopFlashNotificationSequence(context);
+ }
+ }));
}
}
diff --git a/src/com/android/server/telecom/voip/AnswerCallTransaction.java b/src/com/android/server/telecom/voip/AnswerCallTransaction.java
new file mode 100644
index 0000000..1eb34c4
--- /dev/null
+++ b/src/com/android/server/telecom/voip/AnswerCallTransaction.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.server.telecom.voip;
+
+import android.os.OutcomeReceiver;
+import android.telecom.CallException;
+import android.util.Log;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * This transaction should be created for new incoming calls that request to go from
+ * CallState.Ringing to CallState.Answered. Before changing the CallState, the focus manager must
+ * be updated. Once the focus manager updates, the call state will be set. If there is an issue
+ * answering the call, the transaction will fail.
+ */
+public class AnswerCallTransaction extends VoipCallTransaction {
+
+ private static final String TAG = AnswerCallTransaction.class.getSimpleName();
+ private final CallsManager mCallsManager;
+ private final Call mCall;
+ private final int mVideoState;
+
+ public AnswerCallTransaction(CallsManager callsManager, Call call, int videoState) {
+ mCallsManager = callsManager;
+ mCall = call;
+ mVideoState = videoState;
+ }
+
+ @Override
+ public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+ Log.d(TAG, "processTransaction");
+ CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+
+ mCall.setVideoState(mVideoState);
+
+ mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ANSWERED,
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(Boolean result) {
+ Log.d(TAG, "processTransaction: onResult");
+ future.complete(new VoipCallTransactionResult(
+ VoipCallTransactionResult.RESULT_SUCCEED, null));
+ }
+
+ @Override
+ public void onError(CallException exception) {
+ Log.d(TAG, "processTransaction: onError");
+ future.complete(new VoipCallTransactionResult(
+ exception.getCode(), exception.getMessage()));
+ }
+ });
+
+ return future;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
index 327694e..f47e4c5 100644
--- a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
+++ b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.telecom.DisconnectCause;
import android.util.Log;
import com.android.internal.telecom.ICallEventCallback;
@@ -41,7 +42,10 @@
private final ICallEventCallback mICallEventCallback;
private final String mAction;
private final String mCallId;
- private final int mVideoState;
+ // optional values
+ private int mVideoState = 0;
+ private DisconnectCause mDisconnectCause = null;
+
private final VoipCallTransactionResult TRANSACTION_FAILED = new VoipCallTransactionResult(
CODE_OPERATION_TIMED_OUT, "failed to complete the operation before timeout");
@@ -61,6 +65,14 @@
}
}
+ public CallEventCallbackAckTransaction(ICallEventCallback service, String action,
+ String callId) {
+ mICallEventCallback = service;
+ mAction = action;
+ mCallId = callId;
+ }
+
+
public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId,
int videoState) {
mICallEventCallback = service;
@@ -69,6 +81,14 @@
mVideoState = videoState;
}
+ public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId,
+ DisconnectCause cause) {
+ mICallEventCallback = service;
+ mAction = action;
+ mCallId = callId;
+ mDisconnectCause = cause;
+ }
+
@Override
public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
@@ -82,10 +102,7 @@
mICallEventCallback.onSetInactive(mCallId, receiver);
break;
case TransactionalServiceWrapper.ON_DISCONNECT:
- mICallEventCallback.onDisconnect(mCallId, receiver);
- break;
- case TransactionalServiceWrapper.ON_REJECT:
- mICallEventCallback.onReject(mCallId, receiver);
+ mICallEventCallback.onDisconnect(mCallId, mDisconnectCause, receiver);
break;
case TransactionalServiceWrapper.ON_SET_ACTIVE:
mICallEventCallback.onSetActive(mCallId, receiver);
diff --git a/src/com/android/server/telecom/voip/RequestFocusTransaction.java b/src/com/android/server/telecom/voip/RequestFocusTransaction.java
index f13525f..5dedbda 100644
--- a/src/com/android/server/telecom/voip/RequestFocusTransaction.java
+++ b/src/com/android/server/telecom/voip/RequestFocusTransaction.java
@@ -21,6 +21,7 @@
import android.util.Log;
import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
import com.android.server.telecom.CallsManager;
import java.util.concurrent.CompletableFuture;
@@ -42,7 +43,8 @@
Log.d(TAG, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
- mCallsManager.transactionRequestNewFocusCall(mCall, new OutcomeReceiver<>() {
+ mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ACTIVE,
+ new OutcomeReceiver<>() {
@Override
public void onResult(Boolean result) {
Log.d(TAG, "processTransaction: onResult");
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
index cdf2efa..690311e 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
@@ -16,11 +16,14 @@
package com.android.server.telecom.transactionalVoipApp;
+import android.os.Bundle;
import android.telecom.CallControlCallback;
import android.telecom.CallEndpoint;
import android.telecom.CallControl;
import android.telecom.CallEventCallback;
+import android.telecom.DisconnectCause;
import android.util.Log;
+
import java.util.List;
import androidx.annotation.NonNull;
@@ -60,13 +63,8 @@
}
@Override
- public void onReject(@NonNull Consumer<Boolean> wasCompleted) {
- Log.i(TAG, String.format("onReject: callId=[%s]", mCallId));
- wasCompleted.accept(Boolean.TRUE);
- }
-
- @Override
- public void onDisconnect(@NonNull Consumer<Boolean> wasCompleted) {
+ public void onDisconnect(@NonNull DisconnectCause cause,
+ @NonNull Consumer<Boolean> wasCompleted) {
Log.i(TAG, String.format("onDisconnect: callId=[%s]", mCallId));
wasCompleted.accept(Boolean.TRUE);
}
@@ -83,6 +81,12 @@
}
@Override
+ public void onEvent(String event, Bundle extras) {
+ Log.i(TAG, String.format("onEvent: id=[%s], event=[%s], extras=[%s]",
+ mCallId, event, extras));
+ }
+
+ @Override
public void onCallEndpointChanged(@NonNull CallEndpoint newCallEndpoint) {
Log.i(TAG, String.format("onCallEndpointChanged: endpoint=[%s]", newCallEndpoint));
}
diff --git a/tests/src/com/android/server/telecom/tests/CallTest.java b/tests/src/com/android/server/telecom/tests/CallTest.java
index fc78810..6b817d8 100644
--- a/tests/src/com/android/server/telecom/tests/CallTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -426,11 +427,6 @@
call.answer(0);
verify(mMockTransactionalService, times(1)).onAnswer(call, 0);
- // assert CallEventCallback#onReject is called
- call.setState(CallState.RINGING, "test");
- call.reject(0);
- verify(mMockTransactionalService, times(1)).onReject(call, 0);
-
// assert CallEventCallback#onDisconnect is called
call.setState(CallState.ACTIVE, "test");
call.disconnect();
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 75a9a96..7544dc9 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -48,6 +48,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -105,6 +106,7 @@
import com.android.server.telecom.PhoneNumberUtilsAdapter;
import com.android.server.telecom.ProximitySensorManager;
import com.android.server.telecom.ProximitySensorManagerFactory;
+import com.android.server.telecom.Ringer;
import com.android.server.telecom.RoleManagerAdapter;
import com.android.server.telecom.SystemStateHelper;
import com.android.server.telecom.TelecomSystem;
@@ -136,6 +138,7 @@
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -233,6 +236,7 @@
@Mock private Toast mToast;
@Mock private CallAnomalyWatchdog mCallAnomalyWatchdog;
@Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
+ @Mock private Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
private CallsManager mCallsManager;
@@ -294,7 +298,10 @@
mRoleManagerAdapter,
mToastFactory,
mCallEndpointControllerFactory,
- mCallAnomalyWatchdog);
+ mCallAnomalyWatchdog,
+ // Just do async tasks synchronously to support testing.
+ command -> command.run(),
+ mAccessibilityManagerAdapter);
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
@@ -2415,6 +2422,50 @@
@MediumTest
@Test
+ public void testSetCallDialingAndDontIncreaseVolume() {
+ // Start with a non zero volume.
+ mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ 4, 0 /* flags */);
+
+ Call call = mock(Call.class);
+ mCallsManager.markCallAsDialing(call);
+
+ // We set the volume to non-zero above, so expect 1
+ verify(mComponentContextFixture.getAudioManager(), times(1)).setStreamVolume(
+ eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+ }
+ @MediumTest
+ @Test
+ public void testSetCallDialingAndIncreaseVolume() {
+ // Start with a zero volume stream.
+ mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ 0, 0 /* flags */);
+
+ Call call = mock(Call.class);
+ mCallsManager.markCallAsDialing(call);
+
+ // We set the volume to zero above, so expect 2
+ verify(mComponentContextFixture.getAudioManager(), times(2)).setStreamVolume(
+ eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+ }
+
+ @MediumTest
+ @Test
+ public void testSetCallActiveAndDontIncreaseVolume() {
+ // Start with a non-zero volume.
+ mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ 4, 0 /* flags */);
+
+ Call call = mock(Call.class);
+ mCallsManager.markCallAsActive(call);
+
+ // We set the volume to non-zero above, so expect 1 only.
+ verify(mComponentContextFixture.getAudioManager(), times(1)).setStreamVolume(
+ eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+ }
+
+ @MediumTest
+ @Test
public void testHandoverToIsAccepted() {
Call sourceCall = addSpyCall(CONNECTION_MGR_1_HANDLE, CallState.NEW);
Call call = addSpyCall(CONNECTION_MGR_1_HANDLE, CallState.NEW);
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 17bf85e..e3a1471 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -783,6 +783,10 @@
return mTelephonyManager;
}
+ public AudioManager getAudioManager() {
+ return mAudioManager;
+ }
+
public CarrierConfigManager getCarrierConfigManager() {
return mCarrierConfigManager;
}
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 35eda43..d397910 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -150,7 +150,7 @@
@Mock Vibrator mockVibrator;
@Mock InCallController mockInCallController;
@Mock NotificationManager mockNotificationManager;
- @Mock AccessibilityManager mockAccessibilityManager;
+ @Mock Ringer.AccessibilityManagerAdapter mockAccessibilityManagerAdapter;
@Spy Ringer.VibrationEffectProxy spyVibrationEffectProxy;
@@ -180,13 +180,12 @@
when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class))).thenReturn(true);
mockNotificationManager =mContext.getSystemService(NotificationManager.class);
- mockAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
when(mockTonePlayer.startTone()).thenReturn(true);
when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
- mockInCallController, mockNotificationManager, mockAccessibilityManager);
+ mockInCallController, mockNotificationManager, mockAccessibilityManagerAdapter);
when(mockCall1.getState()).thenReturn(CallState.RINGING);
when(mockCall2.getState()).thenReturn(CallState.RINGING);
when(mockCall1.getUserHandleFromTargetPhoneAccount()).thenReturn(PA_HANDLE.getUserHandle());
@@ -558,7 +557,7 @@
assertFalse(mRingerUnderTest.shouldRingForContact(mockCall2));
assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
- verify(mockAccessibilityManager, never())
+ verify(mockAccessibilityManagerAdapter, never())
.startFlashNotificationSequence(any(Context.class), anyInt());
}
@@ -573,7 +572,7 @@
assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
- verify(mockAccessibilityManager, atLeastOnce())
+ verify(mockAccessibilityManagerAdapter, atLeastOnce())
.startFlashNotificationSequence(any(Context.class), anyInt());
}
@@ -589,7 +588,7 @@
assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
mRingerUnderTest.stopRinging();
- verify(mockAccessibilityManager, atLeastOnce())
+ verify(mockAccessibilityManagerAdapter, atLeastOnce())
.stopFlashNotificationSequence(any(Context.class));
}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 2ab3160..7e7235d 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -89,6 +89,7 @@
import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
import com.android.server.telecom.ProximitySensorManager;
import com.android.server.telecom.ProximitySensorManagerFactory;
+import com.android.server.telecom.Ringer;
import com.android.server.telecom.RoleManagerAdapter;
import com.android.server.telecom.StatusBarNotifier;
import com.android.server.telecom.SystemStateHelper;
@@ -209,6 +210,8 @@
@Mock ToneGenerator mToneGenerator;
@Mock DeviceIdleControllerAdapter mDeviceIdleControllerAdapter;
+ @Mock Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
+
final ComponentName mInCallServiceComponentNameX =
new ComponentName(
"incall-service-package-X",
@@ -537,7 +540,7 @@
ContactsAsyncHelper.ContentResolverAdapter adapter) {
return new ContactsAsyncHelper(adapter, mHandlerThread.getLooper());
}
- }, mDeviceIdleControllerAdapter);
+ }, mDeviceIdleControllerAdapter, mAccessibilityManagerAdapter);
mComponentContextFixture.setTelecomManager(new TelecomManager(
mComponentContextFixture.getTestDouble(),
diff --git a/tests/src/com/android/server/telecom/tests/TransactionTests.java b/tests/src/com/android/server/telecom/tests/TransactionTests.java
index a82fa78..1e6734b 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionTests.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionTests.java
@@ -47,6 +47,7 @@
import com.android.server.telecom.PhoneNumberUtilsAdapter;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.ui.ToastFactory;
+import com.android.server.telecom.voip.AnswerCallTransaction;
import com.android.server.telecom.voip.EndCallTransaction;
import com.android.server.telecom.voip.HoldCallTransaction;
import com.android.server.telecom.voip.IncomingCallTransaction;
@@ -152,7 +153,23 @@
// THEN
verify(mCallsManager, times(1))
- .transactionRequestNewFocusCall(eq(mMockCall1), isA(OutcomeReceiver.class));
+ .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ACTIVE),
+ isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testAnswerCallTransaction() throws Exception {
+ // GIVEN
+ AnswerCallTransaction transaction =
+ new AnswerCallTransaction(mCallsManager, mMockCall1, 0);
+
+ // WHEN
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ANSWERED),
+ isA(OutcomeReceiver.class));
}
@Test