Implement mid-call RTT initiation and teardown
Adds functionality to testapps and to framework to allow for local and
remote initiation and disconnection of RTT.
Test: manual
Merged-In: I0e8248b495a7d3750c840591f1fa5388b34a32e2
Change-Id: I0e8248b495a7d3750c840591f1fa5388b34a32e2
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 776759e..cc6470e 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -120,6 +120,10 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.telecom.testapps.ACTION_REMOTE_RTT_UPGRADE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
</activity>
<receiver android:name="com.android.server.telecom.testapps.CallNotificationReceiver"
diff --git a/testapps/res/layout/incall_screen.xml b/testapps/res/layout/incall_screen.xml
index 3ca8781..502bdf4 100644
--- a/testapps/res/layout/incall_screen.xml
+++ b/testapps/res/layout/incall_screen.xml
@@ -26,7 +26,8 @@
android:divider="#FFCC00"
android:dividerHeight="4px">
</ListView>
- <LinearLayout
+ <GridLayout
+ android:columnCount="4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
@@ -55,5 +56,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/answerCallButton"/>
- </LinearLayout>
+ <Button
+ android:id="@+id/start_rtt_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/startRttButton"/>
+ <Button
+ android:id="@+id/accept_rtt_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/acceptRttButton"/>
+ </GridLayout>
</LinearLayout>
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index 56cb476..9dc7cae 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -48,6 +48,10 @@
<string name="endRttButton">End RTT</string>
+ <string name="startRttButton">Start RTT</string>
+
+ <string name="acceptRttButton">Accept RTT request</string>
+
<string name="muteButton">Mute</string>
<string name="holdButton">Hold</string>
diff --git a/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java b/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
index 8fd2378..2d43e76 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
@@ -144,4 +144,9 @@
intent.setData(data);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
+
+ public static void remoteRttUpgrade(Context context) {
+ final Intent intent = new Intent(TestCallActivity.ACTION_REMOTE_RTT_UPGRADE);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+ }
}
diff --git a/testapps/src/com/android/server/telecom/testapps/RttChatbot.java b/testapps/src/com/android/server/telecom/testapps/RttChatbot.java
index 3b16bd4..44439ee 100644
--- a/testapps/src/com/android/server/telecom/testapps/RttChatbot.java
+++ b/testapps/src/com/android/server/telecom/testapps/RttChatbot.java
@@ -47,6 +47,8 @@
private final Random mRandom = new Random();
private final String[] mOneLiners;
private Handler mHandler;
+ private HandlerThread mSenderThread;
+ private Thread mReceiverThread;
private final class ReplyHandler extends Handler {
private StringBuilder mInputSoFar;
@@ -110,8 +112,9 @@
Log.i(LOG_TAG, "Starting RTT chatbot.");
HandlerThread ht = new HandlerThread("RttChatbotSender");
ht.start();
+ mSenderThread = ht;
mHandler = new ReplyHandler(ht.getLooper());
- Thread receiveThread = new Thread(() -> {
+ mReceiverThread = new Thread(() -> {
while (true) {
String charsReceived = mRttTextStream.read();
if (charsReceived == null) {
@@ -129,6 +132,15 @@
.sendToTarget();
}
}, "RttChatbotReceiver");
- receiveThread.start();
+ mReceiverThread.start();
+ }
+
+ public void stop() {
+ if (mSenderThread != null && mSenderThread.isAlive()) {
+ mSenderThread.quit();
+ }
+ if (mReceiverThread != null && mReceiverThread.isAlive()) {
+ mReceiverThread.interrupt();
+ }
}
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java b/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
index 76f2058..ae606c8 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
@@ -55,6 +55,9 @@
static final String ACTION_RTT_CALL =
"android.telecom.testapps.ACTION_RTT_CALL";
+ public static final String ACTION_REMOTE_RTT_UPGRADE =
+ "android.telecom.testapps.ACTION_REMOTE_RTT_UPGRADE";
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -73,6 +76,8 @@
this, data, VideoProfile.STATE_AUDIO_ONLY);
} else if (ACTION_SEND_UPGRADE_REQUEST.equals(action)) {
CallNotificationReceiver.sendUpgradeRequest(this, data);
+ } else if (ACTION_REMOTE_RTT_UPGRADE.equals(action)) {
+ CallNotificationReceiver.remoteRttUpgrade(this);
} else {
CallServiceNotifier.getInstance().updateNotification(this);
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallList.java b/testapps/src/com/android/server/telecom/testapps/TestCallList.java
index 4419b17..1b32690 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallList.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallList.java
@@ -16,6 +16,7 @@
package com.android.server.telecom.testapps;
+import android.content.Context;
import android.telecom.Call;
import android.telecom.InCallService;
import android.telecom.VideoProfile;
@@ -23,6 +24,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.widget.Toast;
import java.util.LinkedList;
import java.util.List;
@@ -37,6 +39,10 @@
public static abstract class Listener {
public void onCallAdded(Call call) {}
public void onCallRemoved(Call call) {}
+ public void onRttStarted(Call call) {}
+ public void onRttStopped(Call call) {}
+ public void onRttInitiationFailed(Call call, int reason) {}
+ public void onRttRequest(Call call, int id) {}
}
private static final TestCallList INSTANCE = new TestCallList();
@@ -97,6 +103,8 @@
private Map<Call, TestVideoCallListener> mVideoCallListeners =
new ArrayMap<Call, TestVideoCallListener>();
private Set<Listener> mListeners = new ArraySet<Listener>();
+ private Context mContext;
+ private int mLastRttRequestId = -1;
/**
* Singleton accessor.
@@ -164,6 +172,10 @@
return mCalls.size();
}
+ public int getLastRttRequestId() {
+ return mLastRttRequestId;
+ }
+
/**
* For any video calls tracked, sends an upgrade to video request.
*/
@@ -218,11 +230,29 @@
@Override
public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) {
Log.v(TAG, "onRttStatusChanged: call = " + call + " " + System.identityHashCode(this));
+ if (enabled) {
+ for (Listener l : mListeners) {
+ l.onRttStarted(call);
+ }
+ } else {
+ for (Listener l : mListeners) {
+ l.onRttStopped(call);
+ }
+ }
+ }
- if (call != null) {
- // Did you have another call? Well too bad, this class isn't gonna handle it.
- mCalls.clear();
- mCalls.add(call);
+ @Override
+ public void onRttInitiationFailure(Call call, int reason) {
+ for (Listener l : mListeners) {
+ l.onRttInitiationFailed(call, reason);
+ }
+ }
+
+ @Override
+ public void onRttRequest(Call call, int id) {
+ mLastRttRequestId = id;
+ for (Listener l : mListeners) {
+ l.onRttRequest(call, id);
}
}
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
index c2d8852..abb9108 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
@@ -124,6 +124,26 @@
}
setConferenceableConnections(c);
}
+
+ @Override
+ public void onRttInitiationSuccess(RemoteConnection connection) {
+ sendRttInitiationSuccess();
+ }
+
+ @Override
+ public void onRttInitiationFailure(RemoteConnection connection, int reason) {
+ sendRttInitiationFailure(reason);
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(RemoteConnection connection) {
+ sendRttSessionRemotelyTerminated();
+ }
+
+ @Override
+ public void onRemoteRttRequest(RemoteConnection connection) {
+ sendRemoteRttRequest();
+ }
};
private final RemoteConnection mRemote;
@@ -143,13 +163,17 @@
mRemote.abort();
}
- /** ${inheritDoc} */
+ /**
+ * ${inheritDoc}
+ */
@Override
public void onAnswer(int videoState) {
mRemote.answer(videoState);
}
- /** ${inheritDoc} */
+ /**
+ * ${inheritDoc}
+ */
@Override
public void onDisconnect() {
mRemote.disconnect();
@@ -160,19 +184,25 @@
mRemote.playDtmfTone(c);
}
- /** ${inheritDoc} */
+ /**
+ * ${inheritDoc}
+ */
@Override
public void onHold() {
mRemote.hold();
}
- /** ${inheritDoc} */
+ /**
+ * ${inheritDoc}
+ */
@Override
public void onReject() {
mRemote.reject();
}
- /** ${inheritDoc} */
+ /**
+ * ${inheritDoc}
+ */
@Override
public void onUnhold() {
mRemote.unhold();
@@ -183,6 +213,21 @@
mRemote.setCallAudioState(state);
}
+ @Override
+ public void onStartRtt(RttTextStream rttTextStream) {
+ mRemote.startRtt(rttTextStream);
+ }
+
+ @Override
+ public void onStopRtt() {
+ mRemote.stopRtt();
+ }
+
+ @Override
+ public void handleRttUpgradeResponse(RttTextStream rttTextStream) {
+ mRemote.sendRttUpgradeResponse(rttTextStream);
+ }
+
private void setState(int state) {
log("setState: " + state);
switch (state) {
@@ -201,7 +246,6 @@
}
}
}
-
public final class TestManagedConference extends Conference {
private final RemoteConference.Callback mRemoteCallback = new RemoteConference.Callback() {
@Override
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index 6c07073..71af9a8 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -25,7 +25,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.ParcelFileDescriptor;
import android.support.v4.content.LocalBroadcastManager;
import android.telecom.Conference;
import android.telecom.Connection;
@@ -39,8 +38,6 @@
import android.telecom.Log;
import android.widget.Toast;
-import com.android.server.telecom.testapps.R;
-
import java.lang.String;
import java.util.ArrayList;
import java.util.List;
@@ -135,6 +132,8 @@
/** Used to cleanup camera and media when done with connection. */
private TestVideoProvider mTestVideoCallProvider;
+ private ConnectionRequest mOriginalRequest;
+ private RttChatbot mRttChatbot;
private BroadcastReceiver mHangupReceiver = new BroadcastReceiver() {
@Override
@@ -154,8 +153,16 @@
}
};
- TestConnection(boolean isIncoming) {
+ private BroadcastReceiver mRttUpgradeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sendRemoteRttRequest();
+ }
+ };
+
+ TestConnection(boolean isIncoming, ConnectionRequest request) {
mIsIncoming = isIncoming;
+ mOriginalRequest = request;
// Assume all calls are video capable.
int capabilities = getConnectionCapabilities();
capabilities |= CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL;
@@ -167,6 +174,12 @@
capabilities |= CAPABILITY_RESPOND_VIA_TEXT;
setConnectionCapabilities(capabilities);
+ int properties = getConnectionProperties();
+ if (mOriginalRequest.isRequestingRtt()) {
+ properties |= PROPERTY_IS_RTT;
+ }
+ setConnectionProperties(properties);
+
if (isIncoming) {
putExtra(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
}
@@ -177,17 +190,24 @@
filter.addDataScheme("int");
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(
mUpgradeRequestReceiver, filter);
+
+ LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(
+ mRttUpgradeReceiver,
+ new IntentFilter(TestCallActivity.ACTION_REMOTE_RTT_UPGRADE));
}
void startOutgoing() {
setDialing();
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- setActive();
- activateCall(TestConnection.this);
- }
+ mHandler.postDelayed(() -> {
+ setActive();
+ activateCall(TestConnection.this);
}, 4000);
+ if (mOriginalRequest.isRequestingRtt()) {
+ Log.i(LOG_TAG, "Is RTT call. Starting chatbot service.");
+ mRttChatbot = new RttChatbot(getApplicationContext(),
+ mOriginalRequest.getRttTextStream());
+ mRttChatbot.start();
+ }
}
/** ${inheritDoc} */
@@ -204,6 +224,12 @@
activateCall(this);
setActive();
updateConferenceable();
+ if (mOriginalRequest.isRequestingRtt()) {
+ Log.i(LOG_TAG, "Is RTT call. Starting chatbot service.");
+ mRttChatbot = new RttChatbot(getApplicationContext(),
+ mOriginalRequest.getRttTextStream());
+ mRttChatbot.start();
+ }
}
/** ${inheritDoc} */
@@ -246,6 +272,39 @@
setActive();
}
+ @Override
+ public void onStopRtt() {
+ int newProperties = getConnectionProperties() & ~PROPERTY_IS_RTT;
+ setConnectionProperties(newProperties);
+ mRttChatbot.stop();
+ mRttChatbot = null;
+ }
+
+ @Override
+ public void handleRttUpgradeResponse(RttTextStream rttTextStream) {
+ Log.i(this, "RTT request response was %s", rttTextStream == null);
+ if (rttTextStream != null) {
+ mRttChatbot = new RttChatbot(getApplicationContext(), rttTextStream);
+ mRttChatbot.start();
+ setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT);
+ sendRttInitiationSuccess();
+ }
+ }
+
+ @Override
+ public void onStartRtt(RttTextStream textStream) {
+ boolean doAccept = Math.random() < 0.5;
+ if (doAccept) {
+ Log.i(this, "Accepting RTT request.");
+ mRttChatbot = new RttChatbot(getApplicationContext(), textStream);
+ mRttChatbot.start();
+ setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT);
+ sendRttInitiationSuccess();
+ } else {
+ sendRttInitiationFailure(RttModifyStatus.SESSION_MODIFY_REQUEST_FAIL);
+ }
+ }
+
public void setTestVideoCallProvider(TestVideoProvider testVideoCallProvider) {
mTestVideoCallProvider = testVideoCallProvider;
}
@@ -273,8 +332,6 @@
/** Used to play an audio tone during a call. */
private MediaPlayer mMediaPlayer;
- // Used to provide text reply in an RTT call
- private RttChatbot mRttChatbot;
@Override
public boolean onUnbind(Intent intent) {
@@ -313,22 +370,12 @@
Toast.LENGTH_SHORT).show();
}
- if (originalRequest.isRequestingRtt()) {
- Log.i(LOG_TAG, "Is RTT call. Starting chatbot service.");
- mRttChatbot = new RttChatbot(getApplicationContext(),
- originalRequest.getRttTextStream());
- mRttChatbot.start();
- }
-
log("gateway package [" + gatewayPackage + "], original handle [" +
originalHandle + "]");
- final TestConnection connection = new TestConnection(false /* isIncoming */);
+ final TestConnection connection =
+ new TestConnection(false /* isIncoming */, originalRequest);
setAddress(connection, handle);
- if (originalRequest.isRequestingRtt()) {
- connection.setConnectionProperties(
- connection.getConnectionProperties() | Connection.PROPERTY_IS_RTT);
- }
// If the number starts with 555, then we handle it ourselves. If not, then we
// use a remote connection service.
@@ -364,7 +411,7 @@
ComponentName componentName = new ComponentName(this, TestConnectionService.class);
if (accountHandle != null && componentName.equals(accountHandle.getComponentName())) {
- final TestConnection connection = new TestConnection(true);
+ final TestConnection connection = new TestConnection(true, request);
// Get the stashed intent extra that determines if this is a video call or audio call.
Bundle extras = request.getExtras();
int videoState = extras.getInt(EXTRA_START_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY);
@@ -393,12 +440,6 @@
"This is a test of call subject lines.");
}
- if (request.isRequestingRtt()) {
- Log.i(LOG_TAG, "Is RTT call. Starting chatbot service.");
- mRttChatbot = new RttChatbot(getApplicationContext(), request.getRttTextStream());
- mRttChatbot.start();
- }
-
connection.putExtras(connectionExtras);
setAddress(connection, address);
@@ -421,7 +462,7 @@
PhoneAccountHandle accountHandle = request.getAccountHandle();
ComponentName componentName = new ComponentName(this, TestConnectionService.class);
if (accountHandle != null && componentName.equals(accountHandle.getComponentName())) {
- final TestConnection connection = new TestConnection(false);
+ final TestConnection connection = new TestConnection(false, request);
final Bundle extras = request.getExtras();
final Uri providedHandle = extras.getParcelable(EXTRA_HANDLE);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
index 809036c..2920ca7 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
@@ -25,6 +25,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ListView;
+import android.widget.Toast;
public class TestInCallUI extends Activity {
@@ -51,6 +52,28 @@
finish();
}
}
+
+ @Override
+ public void onRttStarted(Call call) {
+ Toast.makeText(TestInCallUI.this, "RTT now enabled", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onRttStopped(Call call) {
+ Toast.makeText(TestInCallUI.this, "RTT now disabled", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onRttInitiationFailed(Call call, int reason) {
+ Toast.makeText(TestInCallUI.this, String.format("RTT failed to init: %d", reason),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onRttRequest(Call call, int id) {
+ Toast.makeText(TestInCallUI.this, String.format("RTT request: %d", id),
+ Toast.LENGTH_SHORT).show();
+ }
});
View endCallButton = findViewById(R.id.end_call_button);
@@ -58,6 +81,8 @@
View muteButton = findViewById(R.id.mute_button);
View rttIfaceButton = findViewById(R.id.rtt_iface_button);
View answerButton = findViewById(R.id.answer_button);
+ View startRttButton = findViewById(R.id.start_rtt_button);
+ View acceptRttButton = findViewById(R.id.accept_rtt_button);
endCallButton.setOnClickListener(new OnClickListener() {
@Override
@@ -90,6 +115,7 @@
}
}
});
+
rttIfaceButton.setOnClickListener((view) -> {
Call call = mCallList.getCall(0);
if (call.isRttActive()) {
@@ -98,12 +124,27 @@
startActivity(intent);
}
});
+
answerButton.setOnClickListener(view -> {
Call call = mCallList.getCall(0);
if (call.getState() == Call.STATE_RINGING) {
call.answer(VideoProfile.STATE_AUDIO_ONLY);
}
});
+
+ startRttButton.setOnClickListener(view -> {
+ Call call = mCallList.getCall(0);
+ if (!call.isRttActive()) {
+ call.sendRttRequest();
+ }
+ });
+
+ acceptRttButton.setOnClickListener(view -> {
+ Call call = mCallList.getCall(0);
+ if (!call.isRttActive()) {
+ call.respondToRttRequest(mCallList.getLastRttRequestId(), true);
+ }
+ });
}
/** ${inheritDoc} */
diff --git a/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java b/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java
index ce962b4..9bb6977 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java
@@ -32,6 +32,7 @@
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
+import android.widget.Toast;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -146,6 +147,11 @@
finish();
}
}
+
+ @Override
+ public void onRttStopped(Call call) {
+ TestRttActivity.this.finish();
+ }
});
endRttButton.setOnClickListener((view) -> {