Merge "Add unit tests for some VideoProfile static methods."
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index 2173fa5..0ad5d4c 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -630,7 +630,12 @@
} else {
addressUri = call.getHandle();
}
+
String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
+ if (address != null) {
+ address = PhoneNumberUtils.stripSeparators(address);
+ }
+
int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
if (shouldLog) {
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 62b6d45..a786b55 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.Trace;
import android.provider.ContactsContract.Contacts;
@@ -34,6 +35,7 @@
import android.telecom.Log;
import android.telecom.Logging.EventManager;
import android.telecom.ParcelableConnection;
+import android.telecom.ParcelableRttCall;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.Response;
@@ -50,6 +52,7 @@
import com.android.internal.telephony.SmsApplication;
import com.android.internal.util.Preconditions;
+import java.io.IOException;
import java.lang.String;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -82,6 +85,8 @@
/** Identifies extras changes which originated from an incall service. */
public static final int SOURCE_INCALL_SERVICE = 2;
+ private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
+ private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
/**
* Listener for events on the call.
*/
@@ -97,7 +102,7 @@
void onPostDialWait(Call call, String remaining);
void onPostDialChar(Call call, char nextChar);
void onConnectionCapabilitiesChanged(Call call);
- void onConnectionPropertiesChanged(Call call);
+ void onConnectionPropertiesChanged(Call call, boolean didRttChange);
void onParentChanged(Call call);
void onChildrenChanged(Call call);
void onCannedSmsResponsesLoaded(Call call);
@@ -142,7 +147,7 @@
@Override
public void onConnectionCapabilitiesChanged(Call call) {}
@Override
- public void onConnectionPropertiesChanged(Call call) {}
+ public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {}
@Override
public void onParentChanged(Call call) {}
@Override
@@ -407,6 +412,20 @@
private String mOriginalConnectionId;
/**
+ * Two pairs of {@link android.os.ParcelFileDescriptor}s that handle RTT text communication
+ * between the in-call app and the connection service. If both non-null, this call should be
+ * treated as an RTT call.
+ * Each array should be of size 2. First one is the read side and the second one is the write
+ * side.
+ */
+ private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
+ private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
+ /**
+ * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
+ */
+ private int mRttMode;
+
+ /**
* Persists the specified parameters and initializes the new instance.
*
* @param context The context.
@@ -1087,11 +1106,17 @@
connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
}
- if (mConnectionProperties != connectionProperties) {
+ int changedProperties = mConnectionProperties ^ connectionProperties;
+
+ if (changedProperties != 0) {
int previousProperties = mConnectionProperties;
mConnectionProperties = connectionProperties;
+ setIsRttCall((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
+ Connection.PROPERTY_IS_RTT);
+ boolean didRttChange =
+ (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
for (Listener l : mListeners) {
- l.onConnectionPropertiesChanged(this);
+ l.onConnectionPropertiesChanged(this, didRttChange);
}
boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
@@ -1105,7 +1130,6 @@
for (Listener l : mListeners) {
l.onExternalCallChanged(this, isExternal);
}
-
}
mAnalytics.addCallProperties(mConnectionProperties);
@@ -2003,6 +2027,55 @@
return mSpeakerphoneOn;
}
+ public void setIsRttCall(boolean shouldBeRtt) {
+ boolean areStreamsInitialized = mInCallToConnectionServiceStreams != null
+ && mConnectionServiceToInCallStreams != null;
+ if (shouldBeRtt && !areStreamsInitialized) {
+ try {
+ mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
+ mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
+ } catch (IOException e) {
+ Log.e(this, e, "Failed to create pipes for RTT call.");
+ }
+ } else if (!shouldBeRtt && areStreamsInitialized) {
+ closeRttPipes();
+ mInCallToConnectionServiceStreams = null;
+ mConnectionServiceToInCallStreams = null;
+ }
+ }
+
+ public void closeRttPipes() {
+ // TODO: may defer this until call is removed?
+ }
+
+ public boolean isRttCall() {
+ return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
+ }
+
+ public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
+ return mConnectionServiceToInCallStreams == null ? null
+ : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
+ }
+
+ public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
+ return mInCallToConnectionServiceStreams == null ? null
+ : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
+ }
+
+ public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
+ return mConnectionServiceToInCallStreams == null ? null
+ : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
+ }
+
+ public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
+ return mInCallToConnectionServiceStreams == null ? null
+ : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
+ }
+
+ public int getRttMode() {
+ return mRttMode;
+ }
+
/**
* Sets a video call provider for the call.
*/
@@ -2221,6 +2294,11 @@
return mCallDataUsage;
}
+ public void setRttMode(int mode) {
+ mRttMode = mode;
+ // TODO: hook this up to CallAudioManager
+ }
+
/**
* Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
* has come back to telecom and was processed.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 0060680..c30641b 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -720,6 +720,12 @@
if (phoneAccount != null) {
call.setIsSelfManaged(phoneAccount.isSelfManaged());
}
+ if (extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+ if (phoneAccount != null &&
+ phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
+ call.setIsRttCall(true);
+ }
+ }
call.initAnalytics();
if (getForegroundCall() != null) {
@@ -854,9 +860,9 @@
isReusedCall = false;
}
- // Set the video state on the call early so that when it is added to the InCall UI the UI
- // knows to configure itself as a video call immediately.
if (extras != null) {
+ // Set the video state on the call early so that when it is added to the InCall UI the
+ // UI knows to configure itself as a video call immediately.
int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
@@ -956,8 +962,16 @@
call.setState(
CallState.CONNECTING,
phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
+ PhoneAccount accountToUse =
+ mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
+ if (extras != null
+ && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+ if (accountToUse != null
+ && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
+ call.setIsRttCall(true);
+ }
+ }
}
-
setIntentExtrasAndStartTime(call, extras);
// Do not add the call if it is a potential MMI code.
@@ -1401,6 +1415,16 @@
} else {
call.setTargetPhoneAccount(account);
+ if (call.getIntentExtras()
+ .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+ PhoneAccount realPhoneAccount =
+ mPhoneAccountRegistrar.getPhoneAccountUnchecked(account);
+ if (realPhoneAccount != null
+ && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
+ call.setIsRttCall(true);
+ }
+ }
+
if (!call.isNewOutgoingCallIntentBroadcastDone()) {
return;
}
@@ -1829,6 +1853,7 @@
call.setParentCall(null); // need to clean up parent relationship before destroying.
call.removeListener(this);
call.clearConnectionService();
+ // TODO: clean up RTT pipes
boolean shouldNotify = false;
if (mCalls.contains(call)) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index e4129ae..df1bb4a 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -861,24 +861,30 @@
}
Log.addEvent(call, LogUtils.Events.START_CONNECTION, Log.piiHandle(call.getHandle()));
- try {
- // For self-managed incoming calls, if there is another ongoing call Telecom is
- // responsible for showing a UI to ask the user if they'd like to answer this
- // new incoming call.
- boolean shouldShowIncomingCallUI = call.isSelfManaged() &&
- !mCallsManager.hasCallsForOtherPhoneAccount(
- call.getTargetPhoneAccount());
+ // For self-managed incoming calls, if there is another ongoing call Telecom is
+ // responsible for showing a UI to ask the user if they'd like to answer this
+ // new incoming call.
+ boolean shouldShowIncomingCallUI = call.isSelfManaged() &&
+ !mCallsManager.hasCallsForOtherPhoneAccount(
+ call.getTargetPhoneAccount());
+
+ ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(call.getTargetPhoneAccount())
+ .setAddress(call.getHandle())
+ .setExtras(extras)
+ .setVideoState(call.getVideoState())
+ .setTelecomCallId(callId)
+ .setShouldShowIncomingCallUi(shouldShowIncomingCallUI)
+ .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
+ .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
+ .build();
+
+ try {
mServiceInterface.createConnection(
call.getConnectionManagerPhoneAccount(),
callId,
- new ConnectionRequest(
- call.getTargetPhoneAccount(),
- call.getHandle(),
- extras,
- call.getVideoState(),
- callId,
- shouldShowIncomingCallUI),
+ connectionRequest,
call.shouldAttachToExistingConnection(),
call.isUnknown(),
Log.getExternalSession());
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index e775818..4b54760 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -494,4 +494,72 @@
Log.endSession();
}
}
+
+ @Override
+ public void sendRttRequest() {
+ try {
+ Log.startSession("ICA.sRR");
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ // TODO
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void respondToRttRequest(int id, boolean accept) {
+ try {
+ Log.startSession("ICA.rTRR");
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ // TODO
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void stopRtt() {
+ try {
+ Log.startSession("ICA.sRTT");
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ // TODO
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void setRttMode(int mode) {
+ try {
+ Log.startSession("ICA.sRM");
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ // TODO
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index ec46800..dce4364 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -29,6 +29,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -76,6 +77,7 @@
public void setListener(Listener l) {
mListener = l;
}
+ public InCallServiceInfo getInfo() { return null; }
public void dump(IndentingPrintWriter pw) {}
}
@@ -211,6 +213,11 @@
}
@Override
+ public InCallServiceInfo getInfo() {
+ return mInCallServiceInfo;
+ }
+
+ @Override
public void disconnect() {
if (mIsConnected) {
mContext.unbindService(mServiceConnection);
@@ -319,6 +326,14 @@
}
@Override
+ public InCallServiceInfo getInfo() {
+ if (mIsProxying) {
+ return mSubConnection.getInfo();
+ } else {
+ return super.getInfo();
+ }
+ }
+ @Override
protected void onDisconnected() {
// Save this here because super.onDisconnected() could force us to explicitly
// disconnect() as a cleanup step and that sets mIsConnected to false.
@@ -427,6 +442,11 @@
}
@Override
+ public InCallServiceInfo getInfo() {
+ return mCurrentConnection.getInfo();
+ }
+
+ @Override
public void dump(IndentingPrintWriter pw) {
pw.println("Car Swapping ICS");
pw.increaseIndent();
@@ -490,8 +510,8 @@
}
@Override
- public void onConnectionPropertiesChanged(Call call) {
- updateCall(call);
+ public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {
+ updateCall(call, false /* includeVideoProvider */, didRttChange);
}
@Override
@@ -501,7 +521,7 @@
@Override
public void onVideoCallProviderChanged(Call call) {
- updateCall(call, true /* videoProviderChanged */);
+ updateCall(call, true /* videoProviderChanged */, false);
}
@Override
@@ -660,12 +680,15 @@
continue;
}
+ // Only send the RTT call if it's a UI in-call service
+ boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
+
componentsUpdated.add(info.getComponentName());
IInCallService inCallService = entry.getValue();
ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
- info.isExternalCallsSupported());
+ info.isExternalCallsSupported(), includeRttCall);
try {
inCallService.addCall(parcelableCall);
} catch (RemoteException ignored) {
@@ -728,9 +751,12 @@
componentsUpdated.add(info.getComponentName());
IInCallService inCallService = entry.getValue();
+ // Only send the RTT call if it's a UI in-call service
+ boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
+
ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
- info.isExternalCallsSupported());
+ info.isExternalCallsSupported(), includeRttCall);
try {
inCallService.addCall(parcelableCall);
} catch (RemoteException ignored) {
@@ -746,7 +772,8 @@
false /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar(),
false /* supportsExternalCalls */,
- android.telecom.Call.STATE_DISCONNECTED /* overrideState */);
+ android.telecom.Call.STATE_DISCONNECTED /* overrideState */,
+ false /* includeRttCall */);
Log.i(this, "Removing external call %s ==> %s", call, parcelableCall);
for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
@@ -1138,6 +1165,9 @@
continue;
}
+ // Only send the RTT call if it's a UI in-call service
+ boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
+
// Track the call if we don't already know about it.
addCall(call);
numCallsSent += 1;
@@ -1145,7 +1175,8 @@
call,
true /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar(),
- info.isExternalCallsSupported()));
+ info.isExternalCallsSupported(),
+ includeRttCall));
} catch (RemoteException ignored) {
}
}
@@ -1176,7 +1207,7 @@
* @param call The {@link Call}.
*/
private void updateCall(Call call) {
- updateCall(call, false /* videoProviderChanged */);
+ updateCall(call, false /* videoProviderChanged */, false);
}
/**
@@ -1185,8 +1216,10 @@
* @param call The {@link Call}.
* @param videoProviderChanged {@code true} if the video provider changed, {@code false}
* otherwise.
+ * @param rttInfoChanged {@code true} if any information about the RTT session changed,
+ * {@code false} otherwise.
*/
- private void updateCall(Call call, boolean videoProviderChanged) {
+ private void updateCall(Call call, boolean videoProviderChanged, boolean rttInfoChanged) {
if (!mInCallServices.isEmpty()) {
Log.i(this, "Sending updateCall %s", call);
List<ComponentName> componentsUpdated = new ArrayList<>();
@@ -1200,7 +1233,8 @@
call,
videoProviderChanged /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar(),
- info.isExternalCallsSupported());
+ info.isExternalCallsSupported(),
+ rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()));
ComponentName componentName = info.getComponentName();
IInCallService inCallService = entry.getValue();
componentsUpdated.add(componentName);
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index c4ea9cf..e855549 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.telecom.Connection;
import android.telecom.ParcelableCall;
+import android.telecom.ParcelableRttCall;
import android.telecom.TelecomManager;
import java.util.ArrayList;
@@ -34,7 +35,7 @@
public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
PhoneAccountRegistrar phoneAccountRegistrar) {
return ParcelableCallUtils.toParcelableCall(
- call, includeVideoProvider, phoneAccountRegistrar, false);
+ call, includeVideoProvider, phoneAccountRegistrar, false, false);
}
}
@@ -54,9 +55,11 @@
Call call,
boolean includeVideoProvider,
PhoneAccountRegistrar phoneAccountRegistrar,
- boolean supportsExternalCalls) {
+ boolean supportsExternalCalls,
+ boolean includeRttCall) {
return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
- supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */);
+ supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */,
+ includeRttCall);
}
/**
@@ -79,7 +82,8 @@
boolean includeVideoProvider,
PhoneAccountRegistrar phoneAccountRegistrar,
boolean supportsExternalCalls,
- int overrideState) {
+ int overrideState,
+ boolean includeRttCall) {
int state;
if (overrideState == CALL_STATE_OVERRIDE_NONE) {
state = getParcelableState(call, supportsExternalCalls);
@@ -152,6 +156,8 @@
conferenceableCallIds.add(otherCall.getId());
}
+ ParcelableRttCall rttCall = includeRttCall ? getParcelableRttCall(call) : null;
+
return new ParcelableCall(
call.getId(),
state,
@@ -169,6 +175,8 @@
call.getTargetPhoneAccount(),
includeVideoProvider,
includeVideoProvider ? call.getVideoProvider() : null,
+ includeRttCall,
+ rttCall,
parentCallId,
childCallIds,
call.getStatusHints(),
@@ -346,5 +354,13 @@
return capabilities & ~capability;
}
+ private static ParcelableRttCall getParcelableRttCall(Call call) {
+ if (!call.isRttCall()) {
+ return null;
+ }
+ return new ParcelableRttCall(call.getRttMode(), call.getInCallToCsRttPipeForInCall(),
+ call.getCsToInCallRttPipeForInCall());
+ }
+
private ParcelableCallUtils() {}
}
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 03c1d60..a6a0d76 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -81,6 +81,16 @@
</intent-filter>
</activity>
+ <activity android:name="com.android.server.telecom.testapps.TestRttActivity"
+ android:process="com.android.server.telecom.testapps.TestInCallService"
+ android:label="@string/rttUiLabel"
+ android:launchMode="singleInstance">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name="com.android.server.telecom.testapps.TestCallActivity"
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/testCallActivityLabel">
@@ -105,6 +115,11 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="int" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.telecom.testapps.ACTION_RTT_CALL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="tel" />
+ </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 6a891e7..3ca8781 100644
--- a/testapps/res/layout/incall_screen.xml
+++ b/testapps/res/layout/incall_screen.xml
@@ -44,7 +44,16 @@
android:id="@+id/hold_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/holdButton" >
- </Button>
+ android:text="@string/holdButton"/>
+ <Button
+ android:id="@+id/rtt_iface_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rttIfaceButton"/>
+ <Button
+ android:id="@+id/answer_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/answerCallButton"/>
</LinearLayout>
</LinearLayout>
diff --git a/testapps/res/layout/rtt_incall_screen.xml b/testapps/res/layout/rtt_incall_screen.xml
new file mode 100644
index 0000000..e7cbac4
--- /dev/null
+++ b/testapps/res/layout/rtt_incall_screen.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/received_messages_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="> "
+ android:lines="7" />
+ <TextView
+ android:id="@+id/sent_messages_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="> "
+ android:lines="7" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/end_rtt_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/endRttButton" />
+ <Spinner
+ android:id="@+id/rtt_mode_selection_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <EditText
+ android:id="@+id/rtt_typing_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:lines="1"
+ android:hint="Type here" />
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/res/layout/testdialer_main.xml b/testapps/res/layout/testdialer_main.xml
index 2c3e5e4..e6c56b7 100644
--- a/testapps/res/layout/testdialer_main.xml
+++ b/testapps/res/layout/testdialer_main.xml
@@ -44,4 +44,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancelMissedButton" />
+ <CheckBox
+ android:id="@+id/call_with_rtt_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/startCallWithRtt"/>
</LinearLayout>
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index b9c2aa0..56cb476 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -40,6 +40,14 @@
<string name="endCallButton">End Call</string>
+ <string name="answerCallButton">Answer</string>
+
+ <string name="startCallWithRtt">Start call with RTT</string>
+
+ <string name="rttIfaceButton">RTT</string>
+
+ <string name="endRttButton">End RTT</string>
+
<string name="muteButton">Mute</string>
<string name="holdButton">Hold</string>
@@ -58,4 +66,22 @@
<string name="incomingCallNotPermitted">Incoming call not permitted.</string>
<string name="incomingCallNotPermittedCS">Incoming call not permitted (CS Reported).</string>
+
+ <string name="rttUiLabel">Test RTT UI</string>
+
+ <string-array name="rtt_mode_array">
+ <item>Full</item>
+ <item>HCO</item>
+ <item>VCO</item>
+ </string-array>
+
+ <string-array name="rtt_reply_one_liners">
+ <item>To RTT or not to RTT, that is the question...</item>
+ <item>Making TTY great again!</item>
+ <item>I would be more comfortable with real "Thyme" chatting. I don\'t know how to end
+ this pun</item>
+ <item>お疲れ様でした</item>
+ <item>The FCC has mandated that I respond... I will do so begrudgingly</item>
+ <item>😂😂😂💯</item>
+ </string-array>
</resources>
diff --git a/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java b/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java
index bea0e63..85785d5 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java
@@ -103,7 +103,8 @@
state.setText(getStateString(call));
- Log.i(TAG, "Call found: " + handle.getSchemeSpecificPart() + ", " + durationMs);
+ Log.i(TAG, "Call found: " + ((handle == null) ? "null" : handle.getSchemeSpecificPart())
+ + ", " + durationMs);
return convertView;
}
diff --git a/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java b/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
index aee5514..8fd2378 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
@@ -51,6 +51,8 @@
"com.android.server.telecom.testapps.ACTION_TWO_WAY_VIDEO_CALL";
static final String ACTION_AUDIO_CALL =
"com.android.server.telecom.testapps.ACTION_AUDIO_CALL";
+ static final String ACTION_RTT_CALL =
+ "com.android.server.telecom.testapps.ACTION_RTT_CALL";
/** {@inheritDoc} */
@Override
@@ -66,6 +68,8 @@
sendIncomingCallIntent(context, null, VideoProfile.STATE_RX_ENABLED);
} else if (ACTION_TWO_WAY_VIDEO_CALL.equals(action)) {
sendIncomingCallIntent(context, null, VideoProfile.STATE_BIDIRECTIONAL);
+ } else if (ACTION_RTT_CALL.equals(action)) {
+ sendIncomingRttCallIntent(context, null, VideoProfile.STATE_AUDIO_ONLY);
} else if (ACTION_AUDIO_CALL.equals(action)) {
sendIncomingCallIntent(context, null, VideoProfile.STATE_AUDIO_ONLY);
}
@@ -93,6 +97,23 @@
TelecomManager.from(context).addNewIncomingCall(phoneAccount, extras);
}
+ public static void sendIncomingRttCallIntent(Context context, Uri handle, int videoState) {
+ PhoneAccountHandle phoneAccount = new PhoneAccountHandle(
+ new ComponentName(context, TestConnectionService.class),
+ CallServiceNotifier.SIM_SUBSCRIPTION_ID);
+
+ // For the purposes of testing, indicate whether the incoming call is a video call by
+ // stashing an indicator in the EXTRA_INCOMING_CALL_EXTRAS.
+ Bundle extras = new Bundle();
+ extras.putInt(TestConnectionService.EXTRA_START_VIDEO_STATE, videoState);
+ if (handle != null) {
+ extras.putParcelable(TestConnectionService.EXTRA_HANDLE, handle);
+ }
+ extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true);
+
+ TelecomManager.from(context).addNewIncomingCall(phoneAccount, extras);
+ }
+
public static void addNewUnknownCall(Context context, Uri handle, Bundle extras) {
Log.i(TAG, "Adding new unknown call with handle " + handle);
PhoneAccountHandle phoneAccount = new PhoneAccountHandle(
diff --git a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
index 9292273..ba58655 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
@@ -119,6 +119,7 @@
.setSubscriptionAddress(Uri.parse("tel:555-TEST"))
.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
PhoneAccount.CAPABILITY_VIDEO_CALLING |
+ PhoneAccount.CAPABILITY_RTT |
PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
.setIcon(Icon.createWithResource(
context.getResources(), R.drawable.stat_sys_phone_call))
@@ -139,6 +140,7 @@
.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
PhoneAccount.CAPABILITY_VIDEO_CALLING |
+ PhoneAccount.CAPABILITY_RTT |
PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
.setIcon(Icon.createWithResource(
context.getResources(), R.drawable.stat_sys_phone_call))
diff --git a/testapps/src/com/android/server/telecom/testapps/RttChatbot.java b/testapps/src/com/android/server/telecom/testapps/RttChatbot.java
new file mode 100644
index 0000000..3b16bd4
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/RttChatbot.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.testapps;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.telecom.Connection;
+import android.telecom.Log;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.Random;
+
+public class RttChatbot {
+ private static final String LOG_TAG = RttChatbot.class.getSimpleName();
+ private static final long PER_CHARACTER_DELAY_MS = 100;
+ private static final long MSG_WAIT_DELAY_MS = 3999;
+ private static final double ONE_LINER_FREQUENCY = 0.1;
+ private static final String REPLY_PREFIX = "You said: ";
+
+ private static final int BEGIN_SEND_REPLY_MESSAGE = 1;
+ private static final int SEND_CHARACTER = 2;
+ private static final int APPEND_TO_INPUT_BUFFER = 3;
+
+ private final Connection.RttTextStream mRttTextStream;
+ private final Random mRandom = new Random();
+ private final String[] mOneLiners;
+ private Handler mHandler;
+
+ private final class ReplyHandler extends Handler {
+ private StringBuilder mInputSoFar;
+
+ public ReplyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BEGIN_SEND_REPLY_MESSAGE:
+ removeMessages(SEND_CHARACTER);
+ sendReplyMessage();
+ break;
+ case SEND_CHARACTER:
+ try {
+ mRttTextStream.write((String) msg.obj);
+ } catch (IOException e) {
+ }
+ break;
+ case APPEND_TO_INPUT_BUFFER:
+ removeMessages(BEGIN_SEND_REPLY_MESSAGE);
+ sendEmptyMessageDelayed(BEGIN_SEND_REPLY_MESSAGE, MSG_WAIT_DELAY_MS);
+ String toAppend = (String) msg.obj;
+ if (mInputSoFar == null) {
+ mInputSoFar = new StringBuilder(toAppend);
+ } else {
+ mInputSoFar.append(toAppend);
+ }
+ Log.d(LOG_TAG, "Got %s to append, total text now %s",
+ toAppend, mInputSoFar.toString());
+ break;
+ }
+ }
+
+ private void sendReplyMessage() {
+ String messageToSend;
+ if (mRandom.nextDouble() < ONE_LINER_FREQUENCY) {
+ messageToSend = mOneLiners[mRandom.nextInt(mOneLiners.length)];
+ } else {
+ messageToSend = REPLY_PREFIX + mInputSoFar.toString();
+ }
+ mInputSoFar = null;
+ Log.i(LOG_TAG, "Begin send reply message: %s", messageToSend);
+ int[] charsToSend = messageToSend.codePoints().toArray();
+ for (int i = 0; i < charsToSend.length; i++) {
+ Message msg = obtainMessage(SEND_CHARACTER,
+ new String(new int[] {charsToSend[i]}, 0, 1));
+ sendMessageDelayed(msg, PER_CHARACTER_DELAY_MS * i);
+ }
+ }
+ }
+
+ public RttChatbot(Context context, Connection.RttTextStream textStream) {
+ mOneLiners = context.getResources().getStringArray(R.array.rtt_reply_one_liners);
+ mRttTextStream = textStream;
+ }
+
+ public void start() {
+ Log.i(LOG_TAG, "Starting RTT chatbot.");
+ HandlerThread ht = new HandlerThread("RttChatbotSender");
+ ht.start();
+ mHandler = new ReplyHandler(ht.getLooper());
+ Thread receiveThread = new Thread(() -> {
+ while (true) {
+ String charsReceived = mRttTextStream.read();
+ if (charsReceived == null) {
+ if (Thread.currentThread().isInterrupted()) {
+ Log.w(LOG_TAG, "Thread interrupted");
+ break;
+ }
+ Log.w(LOG_TAG, "Stream closed");
+ break;
+ }
+ if (charsReceived.length() == 0) {
+ continue;
+ }
+ mHandler.obtainMessage(APPEND_TO_INPUT_BUFFER, charsReceived)
+ .sendToTarget();
+ }
+ }, "RttChatbotReceiver");
+ receiveThread.start();
+ }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java b/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
index 862ccf7..76f2058 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallActivity.java
@@ -52,6 +52,9 @@
public static final String ACTION_SEND_UPGRADE_REQUEST =
"android.telecom.testapps.ACTION_SEND_UPGRADE_REQUEST";
+ static final String ACTION_RTT_CALL =
+ "android.telecom.testapps.ACTION_RTT_CALL";
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -65,6 +68,9 @@
CallNotificationReceiver.addNewUnknownCall(this, data, intent.getExtras());
} else if (ACTION_HANGUP_CALLS.equals(action)) {
CallNotificationReceiver.hangupCalls(this);
+ } else if (ACTION_RTT_CALL.equals(action)) {
+ CallNotificationReceiver.sendIncomingRttCallIntent(
+ this, data, VideoProfile.STATE_AUDIO_ONLY);
} else if (ACTION_SEND_UPGRADE_REQUEST.equals(action)) {
CallNotificationReceiver.sendUpgradeRequest(this, data);
} else {
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallList.java b/testapps/src/com/android/server/telecom/testapps/TestCallList.java
index 391a588..4419b17 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallList.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallList.java
@@ -32,7 +32,7 @@
/**
* Maintains a list of calls received via the {@link TestInCallServiceImpl}.
*/
-public class TestCallList extends Call.Listener {
+public class TestCallList extends Call.Callback {
public static abstract class Listener {
public void onCallAdded(Call call) {}
@@ -126,7 +126,7 @@
}
Log.i(TAG, "addCall: " + call + " " + System.identityHashCode(this));
mCalls.add(call);
- call.addListener(this);
+ call.registerCallback(this);
for (Listener l : mListeners) {
l.onCallAdded(call);
@@ -140,7 +140,7 @@
}
Log.i(TAG, "removeCall: " + call);
mCalls.remove(call);
- call.removeListener(this);
+ call.unregisterCallback(this);
for (Listener l : mListeners) {
l.onCallRemoved(call);
@@ -214,4 +214,15 @@
}
}
}
+
+ @Override
+ public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) {
+ Log.v(TAG, "onRttStatusChanged: call = " + call + " " + System.identityHashCode(this));
+
+ if (call != null) {
+ // Did you have another call? Well too bad, this class isn't gonna handle it.
+ mCalls.clear();
+ mCalls.add(call);
+ }
+ }
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
index 5b0cf63..c2d8852 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
@@ -135,6 +135,7 @@
mRemote.registerCallback(mRemoteCallback);
setState(mRemote.getState());
setVideoState(mRemote.getVideoState());
+ setConnectionProperties(remote.getConnectionProperties());
}
@Override
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index 0150dbe..6c07073 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -25,6 +25,7 @@
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;
@@ -35,7 +36,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
-import android.util.Log;
+import android.telecom.Log;
import android.widget.Toast;
import com.android.server.telecom.testapps.R;
@@ -57,6 +58,7 @@
public static final String EXTRA_HANDLE = "extra_handle";
+ private static final String LOG_TAG = TestConnectionService.class.getSimpleName();
/**
* Random number generator used to generate phone numbers.
*/
@@ -271,6 +273,8 @@
/** 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) {
@@ -309,11 +313,22 @@
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 */);
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.
@@ -377,6 +392,13 @@
connectionExtras.putString(Connection.EXTRA_CALL_SUBJECT,
"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);
@@ -385,11 +407,6 @@
addCall(connection);
- ConnectionRequest newRequest = new ConnectionRequest(
- request.getAccountHandle(),
- address,
- request.getExtras(),
- videoState);
connection.setVideoState(videoState);
return connection;
} else {
diff --git a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
index 596d18d..c7eccf7 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
@@ -12,15 +12,15 @@
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
-import com.android.server.telecom.testapps.R;
-
public class TestDialerActivity extends Activity {
private static final int REQUEST_CODE_SET_DEFAULT_DIALER = 1;
private EditText mNumberView;
+ private CheckBox mRttCheckbox;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -55,7 +55,8 @@
});
mNumberView = (EditText) findViewById(R.id.number);
- updateEditTextWithNumber();
+ mRttCheckbox = (CheckBox) findViewById(R.id.call_with_rtt_checkbox);
+ updateMutableUi();
}
@Override
@@ -72,13 +73,15 @@
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
- updateEditTextWithNumber();
+ updateMutableUi();
}
- private void updateEditTextWithNumber() {
+ private void updateMutableUi() {
Intent intent = getIntent();
if (intent != null) {
mNumberView.setText(intent.getDataString());
+ mRttCheckbox.setChecked(
+ intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_RTT, false));
}
}
@@ -127,7 +130,10 @@
private Bundle createCallIntentExtras() {
Bundle extras = new Bundle();
- extras.putString("com.android.server.telecom.testapps.CALL_EXTRAS", "Yorke was here");
+ extras.putString("com.android.server.telecom.testapps.CALL_EXTRAS", "Hall was here");
+ if (mRttCheckbox.isChecked()) {
+ extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true);
+ }
Bundle intentExtras = new Bundle();
intentExtras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
index ce53709..809036c 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
@@ -17,9 +17,10 @@
package com.android.server.telecom.testapps;
import android.app.Activity;
-import android.graphics.Color;
+import android.content.Intent;
import android.os.Bundle;
import android.telecom.Call;
+import android.telecom.VideoProfile;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
@@ -46,7 +47,7 @@
@Override
public void onCallRemoved(Call call) {
if (mCallList.size() == 0) {
- Log.i("Santos", "Ending the incall UI");
+ Log.i(TestInCallUI.class.getSimpleName(), "Ending the incall UI");
finish();
}
}
@@ -55,6 +56,8 @@
View endCallButton = findViewById(R.id.end_call_button);
View holdButton = findViewById(R.id.hold_button);
View muteButton = findViewById(R.id.mute_button);
+ View rttIfaceButton = findViewById(R.id.rtt_iface_button);
+ View answerButton = findViewById(R.id.answer_button);
endCallButton.setOnClickListener(new OnClickListener() {
@Override
@@ -83,9 +86,24 @@
public void onClick(View view) {
Call call = mCallList.getCall(0);
if (call != null) {
+
}
}
});
+ rttIfaceButton.setOnClickListener((view) -> {
+ Call call = mCallList.getCall(0);
+ if (call.isRttActive()) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(this, TestRttActivity.class);
+ startActivity(intent);
+ }
+ });
+ answerButton.setOnClickListener(view -> {
+ Call call = mCallList.getCall(0);
+ if (call.getState() == Call.STATE_RINGING) {
+ call.answer(VideoProfile.STATE_AUDIO_ONLY);
+ }
+ });
}
/** ${inheritDoc} */
diff --git a/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java b/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java
new file mode 100644
index 0000000..ce962b4
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/TestRttActivity.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 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.testapps;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telecom.Call;
+import android.telecom.Log;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+public class TestRttActivity extends Activity {
+ private static final String LOG_TAG = TestRttActivity.class.getSimpleName();
+ private static final long NEWLINE_DELAY_MILLIS = 3000;
+
+ private static final int UPDATE_RECEIVED_TEXT = 1;
+ private static final int UPDATE_SENT_TEXT = 2;
+ private static final int RECEIVED_MESSAGE_GAP = 3;
+ private static final int SENT_MESSAGE_GAP = 4;
+
+ private TextView mReceivedText;
+ private TextView mSentText;
+ private EditText mTypingBox;
+
+ private TestCallList mCallList;
+
+ private Handler mTextDisplayHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ String text;
+ switch (msg.what) {
+ case UPDATE_RECEIVED_TEXT:
+ text = (String) msg.obj;
+ mReceivedText.append(text);
+ break;
+ case UPDATE_SENT_TEXT:
+ text = (String) msg.obj;
+ mSentText.append(text);
+ break;
+ case RECEIVED_MESSAGE_GAP:
+ mReceivedText.append("\n> ");
+ break;
+ case SENT_MESSAGE_GAP:
+ mSentText.append("\n> ");
+ mTypingBox.setText("");
+ break;
+ default:
+ Log.w(LOG_TAG, "Invalid message %d", msg.what);
+ }
+ }
+ };
+
+ private Thread mReceiveReader = new Thread() {
+ @Override
+ public void run() {
+ // outer loop
+ while (true) {
+ begin :
+ // sleep and wait if there are no calls
+ while (mCallList.size() > 0) {
+ Call.RttCall rttCall = mCallList.getCall(0).getRttCall();
+ if (rttCall == null) {
+ break;
+ }
+ // inner read loop
+ while (true) {
+ String receivedText = rttCall.read();
+ if (receivedText == null) {
+ if (Thread.currentThread().isInterrupted()) {
+ break begin;
+ }
+ break;
+ }
+ Log.d(LOG_TAG, "Received %s", receivedText);
+ mTextDisplayHandler.removeMessages(RECEIVED_MESSAGE_GAP);
+ mTextDisplayHandler.sendEmptyMessageDelayed(RECEIVED_MESSAGE_GAP,
+ NEWLINE_DELAY_MILLIS);
+ mTextDisplayHandler.obtainMessage(UPDATE_RECEIVED_TEXT, receivedText)
+ .sendToTarget();
+ }
+ }
+ if (Thread.currentThread().isInterrupted()) {
+ break;
+ }
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.rtt_incall_screen);
+
+ mReceivedText = (TextView) findViewById(R.id.received_messages_text);
+ mSentText = (TextView) findViewById(R.id.sent_messages_text);
+ mTypingBox = (EditText) findViewById(R.id.rtt_typing_box);
+
+ Button endRttButton = (Button) findViewById(R.id.end_rtt_button);
+ Spinner rttModeSelector = (Spinner) findViewById(R.id.rtt_mode_selection_spinner);
+
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+ R.array.rtt_mode_array, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ rttModeSelector.setAdapter(adapter);
+
+ mCallList = TestCallList.getInstance();
+ mCallList.addListener(new TestCallList.Listener() {
+ @Override
+ public void onCallRemoved(Call call) {
+ if (mCallList.size() == 0) {
+ Log.i(LOG_TAG, "Ending the RTT UI");
+ finish();
+ }
+ }
+ });
+
+ endRttButton.setOnClickListener((view) -> {
+ Call call = mCallList.getCall(0);
+ call.stopRtt();
+ });
+
+ rttModeSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ CharSequence selection = (CharSequence) parent.getItemAtPosition(position);
+ Call.RttCall call = mCallList.getCall(0).getRttCall();
+ switch (selection.toString()) {
+ case "Full":
+ call.setRttMode(Call.RttCall.RTT_MODE_FULL);
+ break;
+ case "HCO":
+ call.setRttMode(Call.RttCall.RTT_MODE_HCO);
+ break;
+ case "VCO":
+ call.setRttMode(Call.RttCall.RTT_MODE_VCO);
+ break;
+ default:
+ Log.w(LOG_TAG, "Bad name for rtt mode: %s", selection.toString());
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mTypingBox.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ if (count == 0 || count < before) {
+ // ignore deletions and clears
+ return;
+ }
+ // Only appending at the end is supported.
+ int numCharsInserted = count - before;
+ String toAppend =
+ s.subSequence(s.length() - numCharsInserted, s.length()).toString();
+
+ if (toAppend.isEmpty()) {
+ return;
+ }
+ try {
+ mCallList.getCall(0).getRttCall().write(toAppend);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Exception sending text %s: %s", toAppend, e);
+ }
+ mTextDisplayHandler.removeMessages(SENT_MESSAGE_GAP);
+ mTextDisplayHandler.sendEmptyMessageDelayed(SENT_MESSAGE_GAP, NEWLINE_DELAY_MILLIS);
+ mTextDisplayHandler.obtainMessage(UPDATE_SENT_TEXT, toAppend).sendToTarget();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mReceiveReader.start();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mReceiveReader.interrupt();
+ }
+
+}
diff --git a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
index bf12411..00760fe 100644
--- a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
@@ -121,6 +121,10 @@
}
@Override
+ public void onRttUpgradeRequest(String callId, int id) throws RemoteException {
+ }
+
+ @Override
public IBinder asBinder() {
return this;
}