Add ability to add sim-initiated MO call to UI (3/4)
Pipe an unknown call through CallsManager via various new APIs like
onSuccessfulUnknownCall, onFailedUnknownCall, etc. These are the unknown call's
equivalent of onSuccessfulOutgoingCall and onFailedOutgoingCall.
Only the TelephonyConnectionService is allowed to call this hidden new API
in Telecom.
Also add the ability for TestConnectionService to test this API (although the
check for TelephonyConnectionService must be disabled at build time first).
Bug: 16852844
Change-Id: I5dfdfc1bd4675f6b300a4a55a3098582f9d715a2
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index ca72ec8..9b6ece1 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -70,6 +70,8 @@
void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
void onSuccessfulIncomingCall(Call call);
void onFailedIncomingCall(Call call);
+ void onSuccessfulUnknownCall(Call call, int callState);
+ void onFailedUnknownCall(Call call);
void onRingbackRequested(Call call, boolean ringbackRequested);
void onPostDialWait(Call call, String remaining);
void onCallCapabilitiesChanged(Call call);
@@ -99,6 +101,10 @@
@Override
public void onFailedIncomingCall(Call call) {}
@Override
+ public void onSuccessfulUnknownCall(Call call, int callState) {}
+ @Override
+ public void onFailedUnknownCall(Call call) {}
+ @Override
public void onRingbackRequested(Call call, boolean ringbackRequested) {}
@Override
public void onPostDialWait(Call call, String remaining) {}
@@ -167,6 +173,11 @@
/** True if this is an incoming call. */
private final boolean mIsIncoming;
+ /** True if this is a currently unknown call that was not previously tracked by CallsManager,
+ * and did not originate via the regular incoming/outgoing call code paths.
+ */
+ private boolean mIsUnknown;
+
/**
* The time this call was created. Beyond logging and such, may also be used for bookkeeping
* and specifically for marking certain call attempts as failed attempts.
@@ -660,7 +671,11 @@
mConferenceableCalls.add(idMapper.getCall(id));
}
- if (mIsIncoming) {
+ if (mIsUnknown) {
+ for (Listener l : mListeners) {
+ l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
+ }
+ } else if (mIsIncoming) {
// We do not handle incoming calls immediately when they are verified by the connection
// service. We allow the caller-info-query code to execute first so that we can read the
// direct-to-voicemail property before deciding if we want to show the incoming call to
@@ -686,7 +701,11 @@
setDisconnectCause(disconnectCause);
CallsManager.getInstance().markCallAsDisconnected(this, disconnectCause);
- if (mIsIncoming) {
+ if (mIsUnknown) {
+ for (Listener listener : mListeners) {
+ listener.onFailedUnknownCall(this);
+ }
+ } else if (mIsIncoming) {
for (Listener listener : mListeners) {
listener.onFailedIncomingCall(this);
}
@@ -1255,6 +1274,14 @@
}
}
+ public boolean isUnknown() {
+ return mIsUnknown;
+ }
+
+ public void setIsUnknown(boolean isUnknown) {
+ mIsUnknown = isUnknown;
+ }
+
static int getStateFromConnectionState(int state) {
switch (state) {
case Connection.STATE_INITIALIZING:
diff --git a/src/com/android/server/telecom/CallReceiver.java b/src/com/android/server/telecom/CallReceiver.java
index c8fdeb4..17ca3e5 100644
--- a/src/com/android/server/telecom/CallReceiver.java
+++ b/src/com/android/server/telecom/CallReceiver.java
@@ -21,16 +21,21 @@
public class CallReceiver extends BroadcastReceiver {
private static final String TAG = CallReceiver.class.getName();
+ static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
static final String KEY_IS_DEFAULT_DIALER =
"is_default_dialer";
@Override
public void onReceive(Context context, Intent intent) {
+ final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false);
- Log.i(TAG, "onReceive - isIncomingCall: %s", isIncomingCall);
+ Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall,
+ isUnknownCall);
- if (isIncomingCall) {
+ if (isUnknownCall) {
+ processUnknownCallIntent(intent);
+ } else if (isIncomingCall) {
processIncomingCallIntent(intent);
} else {
processOutgoingCallIntent(context, intent);
@@ -111,6 +116,22 @@
getCallsManager().processIncomingCallIntent(phoneAccountHandle, clientExtras);
}
+ private void processUnknownCallIntent(Intent intent) {
+ PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+
+ if (phoneAccountHandle == null) {
+ Log.w(this, "Rejecting unknown call due to null phone account");
+ return;
+ }
+ if (phoneAccountHandle.getComponentName() == null) {
+ Log.w(this, "Rejecting unknown call due to null component name");
+ return;
+ }
+
+ getCallsManager().addNewUnknownCall(phoneAccountHandle, intent.getExtras());
+ }
+
static CallsManager getCallsManager() {
return CallsManager.getInstance();
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 1b0a9e3..52c0b44 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -32,6 +32,7 @@
import android.telephony.TelephonyManager;
import com.android.internal.util.IndentingPrintWriter;
+
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
@@ -226,6 +227,20 @@
}
@Override
+ public void onSuccessfulUnknownCall(Call call, int callState) {
+ setCallState(call, callState);
+ Log.i(this, "onSuccessfulUnknownCall for call %s", call);
+ addCall(call);
+ }
+
+ @Override
+ public void onFailedUnknownCall(Call call) {
+ Log.i(this, "onFailedUnknownCall for call %s", call);
+ setCallState(call, CallState.DISCONNECTED);
+ call.removeListener(this);
+ }
+
+ @Override
public void onRingbackRequested(Call call, boolean ringback) {
for (CallsManagerListener listener : mListeners) {
listener.onRingbackRequested(call, ringback);
@@ -336,6 +351,27 @@
call.startCreateConnection(mPhoneAccountRegistrar);
}
+ void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
+ Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
+ Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
+ Call call = new Call(
+ mContext,
+ mConnectionServiceRepository,
+ handle,
+ null /* gatewayInfo */,
+ null /* connectionManagerPhoneAccount */,
+ phoneAccountHandle,
+ // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
+ // to the existing connection instead of trying to create a new one.
+ true /* isIncoming */,
+ false /* isConference */);
+ call.setConnectTimeMillis(System.currentTimeMillis());
+ call.setIsUnknown(true);
+ call.setExtras(extras);
+ call.addListener(this);
+ call.startCreateConnection(mPhoneAccountRegistrar);
+ }
+
/**
* Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
*
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index cacdc81..32c6107 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -643,7 +643,8 @@
call.getHandle(),
extras,
call.getVideoState()),
- call.isIncoming());
+ call.isIncoming(),
+ call.isUnknown());
} catch (RemoteException e) {
Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
mPendingResponses.remove(callId).handleCreateConnectionFailure(
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 6aae49b..0c6e25d 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -31,7 +31,7 @@
import java.util.Objects;
/**
- * This class creates connections to place new outgoing calls to attached to an existing incoming
+ * This class creates connections to place new outgoing calls or to attach to an existing incoming
* call. In either case, this class cycles through a set of connection services until:
* - a connection service returns a newly created connection in which case the call is displayed
* to the user
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 299ca24..fbbe1c9 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -37,6 +37,7 @@
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
+
// TODO: Needed for move to system service: import com.android.internal.R;
import com.android.internal.telecom.ITelecomService;
import com.android.internal.util.IndentingPrintWriter;
@@ -472,6 +473,29 @@
}
}
+ /**
+ * @see android.telecom.TelecomManager#addNewUnknownCall
+ */
+ @Override
+ public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
+ if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null &&
+ TelephonyUtil.isPstnComponentName(phoneAccountHandle.getComponentName())) {
+ mAppOpsManager.checkPackage(
+ Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
+
+ Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
+ intent.setClass(mContext, CallReceiver.class);
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtras(extras);
+ intent.putExtra(CallReceiver.KEY_IS_UNKNOWN_CALL, true);
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ } else {
+ Log.i(this, "Null phoneAccountHandle or not initiated by Telephony. Ignoring request"
+ + " to add new unknown call.");
+ }
+ }
+
//
// Supporting methods for the ITelecomService interface implementation.
//
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index cd9b77f..9c09a40 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -69,6 +69,7 @@
</intent-filter>
<intent-filter>
<action android:name="android.telecom.testapps.ACTION_START_INCOMING_CALL" />
+ <action android:name="android.telecom.testapps.ACTION_NEW_UNKNOWN_CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
diff --git a/tests/src/com/android/server/telecom/testapps/CallNotificationReceiver.java b/tests/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
index 6105f3e..a835bf1 100644
--- a/tests/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
+++ b/tests/src/com/android/server/telecom/testapps/CallNotificationReceiver.java
@@ -22,14 +22,18 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.telecom.CallState;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.util.Log;
/**
* This class receives the notification callback intents used to update call states for
* {@link TestConnectionService}.
*/
public class CallNotificationReceiver extends BroadcastReceiver {
+
+ static final String TAG = CallNotificationReceiver.class.getSimpleName();
/**
* Exit intent action is sent when the user clicks the "exit" action of the
* TestConnectionService notification. Used to cancel (remove) the notification.
@@ -83,4 +87,22 @@
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(
+ new ComponentName(context, TestConnectionService.class),
+ CallServiceNotifier.SIM_SUBSCRIPTION_ID);
+
+ if (extras == null) {
+ extras = new Bundle();
+ }
+
+ if (handle != null) {
+ extras.putParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE, handle);
+ extras.putParcelable(TestConnectionService.EXTRA_HANDLE, handle);
+ }
+
+ TelecomManager.from(context).addNewUnknownCall(phoneAccount, extras);
+ }
}
diff --git a/tests/src/com/android/server/telecom/testapps/TestCallActivity.java b/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
index f5e24ec..9c9a40b 100644
--- a/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
+++ b/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
@@ -36,21 +36,25 @@
public static final String ACTION_NEW_INCOMING_CALL =
"android.telecom.testapps.ACTION_START_INCOMING_CALL";
+ /*
+ * Action to exercise TelecomManager.addNewUnknownCall().
+ */
+ public static final String ACTION_NEW_UNKNOWN_CALL =
+ "android.telecom.testapps.ACTION_NEW_UNKNOWN_CALL";
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Intent intent = getIntent();
- if (intent != null && intent.getData() != null) {
- startIncomingCallBroadcast(intent.getData());
+ final String action = intent != null ? intent.getAction() : null;
+ final Uri data = intent != null ? intent.getData() : null;
+ if (ACTION_NEW_INCOMING_CALL.equals(action) && data != null) {
+ CallNotificationReceiver.sendIncomingCallIntent(this, data, false);
+ } if (ACTION_NEW_UNKNOWN_CALL.equals(action) && data != null) {
+ CallNotificationReceiver.addNewUnknownCall(this, data, intent.getExtras());
+ } else {
+ CallServiceNotifier.getInstance().updateNotification(this);
}
- CallServiceNotifier.getInstance().updateNotification(this);
finish();
}
-
- /**
- * Bypass the notification and start the test incoming call directly.
- */
- private void startIncomingCallBroadcast(Uri handle) {
- CallNotificationReceiver.sendIncomingCallIntent(this, handle, false);
- }
}
diff --git a/tests/src/com/android/server/telecom/testapps/TestConnectionService.java b/tests/src/com/android/server/telecom/testapps/TestConnectionService.java
index b327462..389373f 100644
--- a/tests/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/tests/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -267,6 +267,7 @@
originalHandle + "]");
final TestConnection connection = new TestConnection(false /* isIncoming */);
+ connection.setAddress(handle, TelecomManager.PRESENTATION_ALLOWED);
// If the number starts with 555, then we handle it ourselves. If not, then we
// use a remote connection service.
@@ -341,6 +342,31 @@
}
}
+ @Override
+ public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
+ final ConnectionRequest request) {
+ 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 Bundle extras = request.getExtras();
+ final Uri providedHandle = extras.getParcelable(EXTRA_HANDLE);
+
+ Uri handle = providedHandle == null ?
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, getDummyNumber(false), null)
+ : providedHandle;
+
+ connection.setAddress(handle, TelecomManager.PRESENTATION_ALLOWED);
+ connection.setDialing();
+
+ addCall(connection);
+ return connection;
+ } else {
+ return Connection.createFailedConnection(new DisconnectCause(DisconnectCause.ERROR,
+ "Invalid inputs: " + accountHandle + " " + componentName));
+ }
+ }
+
private void activateCall(TestConnection connection) {
if (mMediaPlayer == null) {
mMediaPlayer = createMediaPlayer();