Merge "Add support for CALL_WAITING supp service notification." am: fc0a5cae29 am: c2390cf0f6
am: 8b96af723e
Change-Id: Ib4db31e2839e8c02f78030ea48f15c96da8f59b7
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2fb1ef7..ff84f29 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1608,7 +1608,6 @@
<string name="clh_callFailed_protocol_Error_unspecified_txt">Protocol error, unspecified</string>
<!-- In-call screen: call failure reason (Cause Number 127) -->
<string name="clh_callFailed_interworking_unspecified_txt">Interworking, unspecified</string>
-
<!-- Call settings screen, setting option name -->
<string name="labelCallBarring">Call barring</string>
<!-- Call barring settings screen, setting summary text when a call barring option is activated -->
@@ -1675,4 +1674,57 @@
<string name="call_barring_settings">Call barring settings</string>
<!-- Call barring settings screen, deactivate all call barring settings -->
<string name="call_barring_deactivate_all_no_password">Deactivate all call barring settings?</string>
+ <!-- Message displayed to the user when an outgoing call is deflected. This means that the
+ party the user is calling has chosen to send the call to another phone number. -->
+ <string name="supp_service_notification_call_deflected">Call deflected.</string>
+ <!-- Message displayed to the user when an outgoing call is forwarded to another number.
+ This happens because the party the user is calling has call forwarding active. -->
+ <string name="supp_service_notification_call_forwarded">Call forwarded.</string>
+ <!-- Message displayed to the user when an outgoing call is waiting. This happens when the
+ party the user is calling is already in another call. -->
+ <string name="supp_service_notification_call_waiting">Call is waiting.</string>
+ <!-- Message displayed to the user when they have chosen to block their phone number for an
+ outgoing call, but the network has rejected that request. -->
+ <string name="supp_service_clir_suppression_rejected">Number blocking is rejected.</string>
+ <!-- Message displayed to the user to inform them that the call is to or from a number which is
+ part of a closed user group. A closed user group is a network feature which restricts
+ calls on a device to members of the closed user group. -->
+ <string name="supp_service_closed_user_group_call">Closed user group call.</string>
+ <!-- Message displayed to the user when incoming call barring is active. This means that the
+ user has enabled the network feature which prevents all incoming calls. -->
+ <string name="supp_service_incoming_calls_barred">Incoming calls barred.</string>
+ <!-- Message displayed to the user when outgoing call barring is active. This means that the
+ user has enabled the network feature which prevents all outgoing calls. -->
+ <string name="supp_service_outgoing_calls_barred">Outgoing calls barred.</string>
+ <!-- Message displayed to the user to indicate that call forwarding is active. -->
+ <string name="supp_service_call_forwarding_active">Call forwarding active.</string>
+ <!-- Message displayed to the user when they receive multiple incoming calls at the same time
+ and one of them is forwarded to the network. Phones can't handle multiple incoming calls
+ so the network will typically forward one of the calls to voicemail or another number
+ defined by the user. -->
+ <string name="supp_service_additional_call_forwarded">Additional call forwarded.</string>
+ <!-- Message displayed to the user to indicate that a call has been successfully transferred
+ to another phone number. -->
+ <string name="supp_service_additional_ect_connected">Explicit call transfer complete.</string>
+ <!-- Message displayed to the user to indicate that the call is in the process of being
+ transferred to another phone number.-->
+ <string name="supp_service_additional_ect_connecting">Explicit call transfer in progress.</string>
+ <!-- Message displayed to the user to indicate that the remote party has put the user
+ on hold. -->
+ <string name="supp_service_call_on_hold">Call on hold.</string>
+ <!-- Message displayed to the user to indicate that the remote party has taken the user
+ off hold. -->
+ <string name="supp_service_call_resumed">Call resumed.</string>
+ <!-- Message displayed to the user to indicate that an incoming call was deflected from another
+ number. This means that the call originated as a result of the original caller choosing
+ to forward the call to the current user rather than answering it themselves. -->
+ <string name="supp_service_deflected_call">Call was deflected.</string>
+ <!-- Message displayed to the user to indicate that an incoming call was forwarded from another
+ number. -->
+ <string name="supp_service_forwarded_call">Forwarded call.</string>
+ <!-- Message displayed to the user to indicate that they are joining a conference call. -->
+ <string name="supp_service_conference_call">Joining conference call.</string>
+ <!-- Message displayed to the user to indicate that a held call has been released /
+ disconnected. -->
+ <string name="supp_service_held_call_released">Held call has been released.</string>
</resources>
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 9ed7689..92c3d2c 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -22,6 +22,7 @@
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.telecom.CallAudioState;
@@ -96,7 +97,7 @@
private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
private static final int MSG_HANGUP = 17;
- private final Handler mHandler = new Handler() {
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -170,10 +171,7 @@
new ArrayList(Arrays.asList(mSsNotification.history)));
putExtras(lastForwardedNumber);
}
- if (mSsNotification.code
- == SuppServiceNotification.MO_CODE_CALL_FORWARDED) {
- sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
- }
+ handleSuppServiceNotification(mSsNotification);
}
}
break;
@@ -256,6 +254,115 @@
};
/**
+ * Handles {@link SuppServiceNotification}s pertinent to Telephony.
+ * @param ssn the notification.
+ */
+ private void handleSuppServiceNotification(SuppServiceNotification ssn) {
+ Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType,
+ ssn.code);
+ if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
+ && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
+ sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
+ }
+ sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
+ }
+
+ /**
+ * Sends a supplementary service notification connection event.
+ * This connection event includes the type and code, as well as a human readable message which
+ * is suitable for display to the user if the UI chooses to do so.
+ * @param type the {@link SuppServiceNotification#type}.
+ * @param code the {@link SuppServiceNotification#code}.
+ */
+ private void sendSuppServiceNotificationEvent(int type, int code) {
+ Bundle extras = new Bundle();
+ extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
+ extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
+ extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
+ getSuppServiceMessage(type, code));
+ sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
+ }
+
+ /**
+ * Retrieves a human-readable message for a supplementary service notification.
+ * This message is suitable for display to the user.
+ * @param type the code group.
+ * @param code the code.
+ * @return A {@link CharSequence} containing the message, or {@code null} if none defined.
+ */
+ private CharSequence getSuppServiceMessage(int type, int code) {
+ int messageId = -1;
+ if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) {
+ switch (code) {
+ case SuppServiceNotification.CODE_1_CALL_DEFLECTED:
+ messageId = R.string.supp_service_notification_call_deflected;
+ break;
+ case SuppServiceNotification.CODE_1_CALL_FORWARDED:
+ messageId = R.string.supp_service_notification_call_forwarded;
+ break;
+ case SuppServiceNotification.CODE_1_CALL_IS_WAITING:
+ messageId = R.string.supp_service_notification_call_waiting;
+ break;
+ case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED:
+ messageId = R.string.supp_service_clir_suppression_rejected;
+ break;
+ case SuppServiceNotification.CODE_1_CUG_CALL:
+ messageId = R.string.supp_service_closed_user_group_call;
+ break;
+ case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED:
+ messageId = R.string.supp_service_incoming_calls_barred;
+ break;
+ case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED:
+ messageId = R.string.supp_service_outgoing_calls_barred;
+ break;
+ case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE:
+ // Intentional fall through.
+ case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE:
+ messageId = R.string.supp_service_call_forwarding_active;
+ break;
+ }
+ } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) {
+ switch (code) {
+ case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED:
+ messageId = R.string.supp_service_additional_call_forwarded;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT:
+ messageId = R.string.supp_service_additional_ect_connected;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT:
+ messageId = R.string.supp_service_additional_ect_connecting;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_ON_HOLD:
+ messageId = R.string.supp_service_call_on_hold;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_RETRIEVED:
+ messageId = R.string.supp_service_call_resumed;
+ break;
+ case SuppServiceNotification.CODE_2_CUG_CALL:
+ messageId = R.string.supp_service_closed_user_group_call;
+ break;
+ case SuppServiceNotification.CODE_2_DEFLECTED_CALL:
+ messageId = R.string.supp_service_deflected_call;
+ break;
+ case SuppServiceNotification.CODE_2_FORWARDED_CALL:
+ messageId = R.string.supp_service_forwarded_call;
+ break;
+ case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL:
+ messageId = R.string.supp_service_conference_call;
+ break;
+ case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED:
+ messageId = R.string.supp_service_held_call_released;
+ break;
+ }
+ }
+ if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
+ return getPhone().getContext().getText(messageId);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
*/
public boolean isCarrierVideoConferencingSupported() {
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 700b626..3bd2716 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,13 +16,29 @@
package com.android.services.telephony;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
import android.telecom.DisconnectCause;
import android.telecom.TelecomManager;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
@@ -30,29 +46,18 @@
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.List;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
/**
* Unit tests for TelephonyConnectionService.
*/
@@ -60,6 +65,7 @@
@RunWith(AndroidJUnit4.class)
public class TelephonyConnectionServiceTest extends TelephonyTestBase {
+ private static final long TIMEOUT_MS = 100;
private static final int SLOT_0_PHONE_ID = 0;
private static final int SLOT_1_PHONE_ID = 1;
@@ -752,6 +758,58 @@
}
}
+ @Test
+ @SmallTest
+ public void testSuppServiceNotification() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+
+ // We need to set the original connection to cause the supp service notification
+ // registration to occur.
+ Phone phone = c.getPhone();
+ c.setOriginalConnection(c.getOriginalConnection());
+
+ // When the registration occurs, we'll capture the handler and message so we can post our
+ // own messages to it.
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> messageCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(phone).registerForSuppServiceNotification(handlerCaptor.capture(),
+ messageCaptor.capture(), any());
+ Handler handler = handlerCaptor.getValue();
+ int message = messageCaptor.getValue();
+
+ // With the handler and message now known, we'll post a supp service notification.
+ AsyncResult result = getSuppServiceNotification(
+ SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+ SuppServiceNotification.CODE_1_CALL_FORWARDED);
+ handler.obtainMessage(message, result).sendToTarget();
+ waitForHandlerAction(handler, TIMEOUT_MS);
+
+ assertTrue(c.getLastConnectionEvents().contains(TelephonyManager.EVENT_CALL_FORWARDED));
+
+ // With the handler and message now known, we'll post a supp service notification.
+ result = getSuppServiceNotification(
+ SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+ SuppServiceNotification.CODE_1_CALL_IS_WAITING);
+ handler.obtainMessage(message, result).sendToTarget();
+ waitForHandlerAction(handler, TIMEOUT_MS);
+
+ // We we want the 3rd event since the forwarding one above sends 2.
+ assertEquals(c.getLastConnectionEvents().get(2),
+ TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION);
+ Bundle extras = c.getLastConnectionEventExtras().get(2);
+ assertEquals(SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+ extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE));
+ assertEquals(SuppServiceNotification.CODE_1_CALL_IS_WAITING,
+ extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
+ }
+
+ private AsyncResult getSuppServiceNotification(int notificationType, int code) {
+ SuppServiceNotification notification = new SuppServiceNotification();
+ notification.notificationType = notificationType;
+ notification.code = code;
+ return new AsyncResult(null, notification, null);
+ }
+
private Phone makeTestPhone(int phoneId, int serviceState, boolean isEmergencyOnly) {
Phone phone = mock(Phone.class);
ServiceState testServiceState = new ServiceState();
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index ea0f965..9040257 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -16,17 +16,25 @@
package com.android.services.telephony;
+import android.content.Context;
+import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Mock Telephony Connection used in TelephonyConferenceController.java for testing purpose
*/
@@ -39,8 +47,13 @@
@Mock
Call mMockCall;
+ @Mock
+ Context mMockContext;
+
private Phone mMockPhone;
private int mNotifyPhoneAccountChangedCount = 0;
+ private List<String> mLastConnectionEvents = new ArrayList<>();
+ private List<Bundle> mLastConnectionEventExtras = new ArrayList<>();
@Override
public com.android.internal.telephony.Connection getOriginalConnection() {
@@ -52,11 +65,17 @@
MockitoAnnotations.initMocks(this);
mMockPhone = mock(Phone.class);
+ mMockContext = mock(Context.class);
// Set up mMockRadioConnection and mMockPhone to contain an active call
when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
+ doNothing().when(mMockRadioConnection).addListener(any(Connection.Listener.class));
+ doNothing().when(mMockRadioConnection).addPostDialListener(
+ any(Connection.PostDialListener.class));
when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
+ when(mMockPhone.getContext()).thenReturn(null);
when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
+ when(mMockCall.getPhone()).thenReturn(mMockPhone);
}
@Override
@@ -82,7 +101,21 @@
mNotifyPhoneAccountChangedCount++;
}
+ @Override
+ public void sendConnectionEvent(String event, Bundle extras) {
+ mLastConnectionEvents.add(event);
+ mLastConnectionEventExtras.add(extras);
+ }
+
public int getNotifyPhoneAccountChangedCount() {
return mNotifyPhoneAccountChangedCount;
}
+
+ public List<String> getLastConnectionEvents() {
+ return mLastConnectionEvents;
+ }
+
+ public List<Bundle> getLastConnectionEventExtras() {
+ return mLastConnectionEventExtras;
+ }
}