Merge "remove setCallIsSuppressedByDoNotDisturb in Ringer#shouldRingForContact" into udc-dev
diff --git a/Android.bp b/Android.bp
index c5141ca..501b438 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
],
static_libs: [
"androidx.annotation_annotation",
+ "androidx.core_core",
],
libs: [
"services",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d42dcff..ab067d9 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -63,6 +63,8 @@
<uses-permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"/>
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
+ <uses-permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"/>
+ <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
<uses-permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"/>
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
diff --git a/res/drawable/gm_phonelink.xml b/res/drawable/gm_phonelink.xml
new file mode 100644
index 0000000..2ffba0e
--- /dev/null
+++ b/res/drawable/gm_phonelink.xml
@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,6h16L21,4L5,4c-1.1,0 -2,0.9 -2,2v11L1,17v3h11v-3L5,17L5,6zM21,8h-6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1L22,9c0,-0.55 -0.45,-1 -1,-1zM20,17h-4v-7h4v7z"/>
+</vector>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 4af6351..883ce52 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -25,7 +25,7 @@
<string name="notification_missedCallsMsg" msgid="5055782736170916682">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ચૂકી ગયેલા કૉલ"</string>
<string name="notification_missedCallTicker" msgid="6731461957487087769">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> નો કૉલ ચૂકી ગયાં"</string>
<string name="notification_missedCall_call_back" msgid="7900333283939789732">"કૉલ બેક"</string>
- <string name="notification_missedCall_message" msgid="4054698824390076431">"સંદેશ"</string>
+ <string name="notification_missedCall_message" msgid="4054698824390076431">"મેસેજ"</string>
<string name="notification_disconnectedCall_title" msgid="1790131923692416928">"ડિસ્કનેક્ટ કરેલો કૉલ"</string>
<string name="notification_disconnectedCall_body" msgid="600491714584417536">"ઇમર્જન્સી કૉલને કારણે <xliff:g id="CALLER">%s</xliff:g>નો કૉલ ડિસ્કનેક્ટ કરવામાં આવ્યો છે."</string>
<string name="notification_disconnectedCall_generic_body" msgid="5282765206349184853">"ઇમર્જન્સી કૉલને કારણે તમારો કૉલ ડિસ્કનેક્ટ કરવામાં આવ્યો છે."</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 8109de2..68d8078 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -53,7 +53,7 @@
<string name="no_vm_number" msgid="2179959110602180844">"ಧ್ವನಿಮೇಲ್ ಸಂಖ್ಯೆಯು ಕಾಣೆಯಾಗಿದೆ"</string>
<string name="no_vm_number_msg" msgid="1339245731058529388">"ಸಿಮ್ ಕಾರ್ಡ್ನಲ್ಲಿ ಯಾವುದೇ ಧ್ವನಿಮೇಲ್ ಸಂಖ್ಯೆಯನ್ನು ಸಂಗ್ರಹಿಸಿಲ್ಲ."</string>
<string name="add_vm_number_str" msgid="5179510133063168998">"ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಿ"</string>
- <string name="change_default_dialer_dialog_title" msgid="5861469279421508060">"<xliff:g id="NEW_APP">%s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಡಿಫಾಲ್ಟ್ ಫೋನ್ ಅಪ್ಲಿಕೇಶನ್ ಆಗಿ ಮಾಡುವುದೇ?"</string>
+ <string name="change_default_dialer_dialog_title" msgid="5861469279421508060">"<xliff:g id="NEW_APP">%s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಡಿಫಾಲ್ಟ್ ಫೋನ್ ಆ್ಯಪ್ ಆಗಿ ಮಾಡಬೇಕೆ?"</string>
<string name="change_default_dialer_dialog_affirmative" msgid="8604665314757739550">"ಡಿಫಾಲ್ಟ್ ಹೊಂದಿಸಿ"</string>
<string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"ರದ್ದುಮಾಡಿ"</string>
<string name="change_default_dialer_warning_message" msgid="8461963987376916114">"<xliff:g id="NEW_APP">%s</xliff:g> ಗೆ ನಿಮ್ಮ ಕರೆಗಳ ಎಲ್ಲಾ ಅಂಶಗಳನ್ನು ನಿಯಂತ್ರಿಸಲು ಮತ್ತು ಕರೆಗಳನ್ನು ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ. ನೀವು ವಿಶ್ವಾಸವಿರಿಸಿರುವಂತಹ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಮಾತ್ರ ನಿಮ್ಮ ಡಿಫಾಲ್ಟ್ ಅಪ್ಲಿಕೇಶನ್ ಆಗಿ ಹೊಂದಿಸಬೇಕು."</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d67df4b..ec278f0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -321,6 +321,10 @@
<string name="notification_channel_disconnected_calls">Disconnected calls</string>
<!-- Notification channel name for a channel containing crashed phone apps service notifications. -->
<string name="notification_channel_in_call_service_crash">Crashed phone apps</string>
+ <!-- Notification channel name for a channel containing notifications related to call streaming.
+ Call streaming is a feature where an app can use another device like a tablet to see and
+ control a call taking place on their phone. -->
+ <string name="notification_channel_call_streaming">Call streaming</string>
<!-- Alert dialog content used to inform the user that placing a new outgoing call will end the
ongoing call in the app "other_app". -->
@@ -395,4 +399,20 @@
<string name="callendpoint_name_streaming">External</string>
<!-- The user-visible name of the unknown new type CallEndpoint -->
<string name="callendpoint_name_unknown">Unknown</string>
+
+ <!-- The content of a notification shown when a call is being streamed to another device.
+ Call streaming is a feature where a user can see and interact with a call from another
+ device like a tablet while the call takes place on their phone. -->
+ <string name="call_streaming_notification_body">Streaming audio to other device</string>
+ <!-- A notification action which is shown when a call is being streamed to another device.
+ Tapping the action will hang up the call.
+ Call streaming is a feature where a user can see and interact with a call from another
+ device like a tablet while the call takes place on their phone. -->
+ <string name="call_streaming_notification_action_hang_up">Hang up</string>
+ <!-- A notification action which is shown when a call is being streamed to another device.
+ Tapping the action will move the call back to the phone from the device it is being
+ streamed to.
+ Call streaming is a feature where a user can see and interact with a call from another
+ device like a tablet while the call takes place on their phone. -->
+ <string name="call_streaming_notification_action_switch_here">Switch here</string>
</resources>
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 6ae8834..42f02fb 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -999,6 +999,9 @@
s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis())));
s.append("]");
s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
+ s.append("(User=");
+ s.append(getInitiatingUser());
+ s.append(")");
s.append("\n\t");
PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount();
@@ -4116,6 +4119,15 @@
* @param extras The extras.
*/
public void onConnectionEvent(String event, Bundle extras) {
+ if (mIsTransactionalCall) {
+ // send the Event directly to the ICS via the InCallController listener
+ for (Listener l : mListeners) {
+ l.onConnectionEvent(this, event, extras);
+ }
+ // Don't run the below block since it applies to Calls that are attached to a
+ // ConnectionService
+ return;
+ }
// Don't log call quality reports; they're quite frequent and will clog the log.
if (!Connection.EVENT_CALL_QUALITY_REPORT.equals(event)) {
Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 6276a7d..d90524d 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -87,6 +87,14 @@
mStreamingCall = null;
mTransactionalServiceWrapper = null;
if (mConnection != null) {
+ // Notify service streaming stopped and then unbind.
+ try {
+ mService.onCallStreamingStopped();
+ } catch (RemoteException e) {
+ // Could not notify stop streaming; we're about to just unbind so this is
+ // unfortunate but not the end of the world.
+ Log.e(this, e, "resetController: failed to notify stop streaming.");
+ }
mContext.unbindService(mConnection);
mConnection = null;
}
@@ -140,7 +148,7 @@
@Override
public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(this, "processTransaction");
+ Log.i(this, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
if (mEnterInterception) {
@@ -178,9 +186,8 @@
@SuppressLint("LongLogTag")
@Override
public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(this, "processTransaction");
+ Log.i(this, "processTransaction");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
-
RoleManager roleManager = mContext.getSystemService(RoleManager.class);
PackageManager packageManager = mContext.getPackageManager();
if (roleManager == null || packageManager == null) {
@@ -198,7 +205,7 @@
VoipCallTransactionResult.RESULT_FAILED, MESSAGE));
return future;
}
-
+ Log.i(this, "processTransaction: servicePackage=%s", holders.get(0));
Intent serviceIntent = new Intent(CallStreamingService.SERVICE_INTERFACE);
serviceIntent.setPackage(holders.get(0));
List<ResolveInfo> infos = packageManager.queryIntentServicesAsUser(serviceIntent,
@@ -223,7 +230,7 @@
Intent intent = new Intent(CallStreamingService.SERVICE_INTERFACE);
intent.setComponent(serviceInfo.getComponentName());
- mConnection = new CallStreamingServiceConnection(mCall, mWrapper, future);
+ mConnection = new CallStreamingServiceConnection(mCall, mWrapper, future);
if (!mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE
| Context.BIND_SCHEDULE_LIKE_TOP_APP, mUserHandle)) {
@@ -232,7 +239,6 @@
VoipCallTransactionResult.RESULT_FAILED,
"STREAMING_FAILED_SENDER_BINDING_ERROR"));
}
-
return future;
}
}
@@ -249,7 +255,7 @@
@SuppressLint("LongLogTag")
@Override
public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(this, "processTransaction");
+ Log.i(this, "processTransaction (unbindStreaming");
CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
resetController();
@@ -280,11 +286,13 @@
case CallState.ON_HOLD:
transaction = new CallStreamingStateChangeTransaction(
StreamingCall.STATE_HOLDING);
+ break;
case CallState.DISCONNECTING:
case CallState.DISCONNECTED:
Log.addEvent(call, LogUtils.Events.STOP_STREAMING);
transaction = new CallStreamingStateChangeTransaction(
StreamingCall.STATE_DISCONNECTED);
+ break;
default:
// ignore
}
@@ -374,13 +382,6 @@
}
private void clearBinding() {
- try {
- if (mService != null) {
- mService.onCallStreamingStopped();
- }
- } catch (RemoteException e) {
- Log.e(this, e, "Exception when stop call streaming");
- }
resetController();
if (!mFuture.isDone()) {
mFuture.complete(new VoipCallTransactionResult(
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index d457cc8..13a965c 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -41,6 +41,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.KeyguardManager;
@@ -132,6 +133,7 @@
import com.android.server.telecom.stats.CallFailureCause;
import com.android.server.telecom.ui.AudioProcessingNotification;
import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
+import com.android.server.telecom.ui.CallStreamingNotification;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
import com.android.server.telecom.ui.IncomingCallNotifier;
@@ -448,6 +450,7 @@
private final BlockedNumbersAdapter mBlockedNumbersAdapter;
private final TransactionManager mTransactionManager;
private final UserManager mUserManager;
+ private final CallStreamingNotification mCallStreamingNotification;
private final ConnectionServiceFocusManager.CallsManagerRequester mRequester =
new ConnectionServiceFocusManager.CallsManagerRequester() {
@@ -559,7 +562,8 @@
Executor asyncTaskExecutor,
BlockedNumbersAdapter blockedNumbersAdapter,
TransactionManager transactionManager,
- EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger) {
+ EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger,
+ CallStreamingNotification callStreamingNotification) {
mContext = context;
mLock = lock;
@@ -648,6 +652,7 @@
mBlockedNumbersAdapter = blockedNumbersAdapter;
mCallStreamingController = new CallStreamingController(mContext, mLock);
mVoipCallMonitor = new VoipCallMonitor(mContext, mLock);
+ mCallStreamingNotification = callStreamingNotification;
mListeners.add(mInCallWakeLockController);
mListeners.add(statusBarNotifier);
@@ -669,6 +674,7 @@
// this needs to be after the mCallAudioManager
mListeners.add(mPhoneStateBroadcaster);
mListeners.add(mVoipCallMonitor);
+ mListeners.add(mCallStreamingNotification);
mVoipCallMonitor.startMonitor();
@@ -1414,6 +1420,15 @@
extras.getInt(CallAttributes.CALL_CAPABILITIES_KEY,
CallAttributes.SUPPORTS_SET_INACTIVE), true);
call.setTargetPhoneAccount(phoneAccountHandle);
+ if (extras.containsKey(CallAttributes.DISPLAY_NAME_KEY)) {
+ CharSequence displayName = extras.getCharSequence(CallAttributes.DISPLAY_NAME_KEY);
+ if (!TextUtils.isEmpty(displayName)) {
+ call.setCallerDisplayName(displayName.toString(),
+ TelecomManager.PRESENTATION_ALLOWED);
+ }
+ }
+ // Incoming address was set via EXTRA_INCOMING_CALL_ADDRESS above.
+ call.setInitiatingUser(phoneAccountHandle.getUserHandle());
}
// Ensure new calls related to self-managed calls/connections are set as such. This will
@@ -1698,7 +1713,6 @@
boolean isReusedCall;
Uri handle = isConference ? Uri.parse("tel:conf-factory") : participants.get(0);
Call call = reuseOutgoingCall(handle);
-
PhoneAccount account =
mPhoneAccountRegistrar.getPhoneAccount(requestedAccountHandle, initiatingUser);
Bundle phoneAccountExtra = account != null ? account.getExtras() : null;
@@ -1739,6 +1753,14 @@
call.setConnectionCapabilities(
extras.getInt(CallAttributes.CALL_CAPABILITIES_KEY,
CallAttributes.SUPPORTS_SET_INACTIVE), true);
+ if (extras.containsKey(CallAttributes.DISPLAY_NAME_KEY)) {
+ CharSequence displayName = extras.getCharSequence(
+ CallAttributes.DISPLAY_NAME_KEY);
+ if (!TextUtils.isEmpty(displayName)) {
+ call.setCallerDisplayName(displayName.toString(),
+ TelecomManager.PRESENTATION_ALLOWED);
+ }
+ }
call.setTargetPhoneAccount(requestedAccountHandle);
}
@@ -6365,4 +6387,30 @@
public CallStreamingController getCallStreamingController() {
return mCallStreamingController;
}
+
+ /**
+ * Given a call identified by call id, get the instance from the list of calls.
+ * @param callId the call id.
+ * @return the call, or null if not found.
+ */
+ public @Nullable Call getCall(@NonNull String callId) {
+ Optional<Call> foundCall = mCalls.stream().filter(
+ c -> c.getId().equals(callId)).findFirst();
+ if (foundCall.isPresent()) {
+ return foundCall.get();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Triggers stopping of call streaming for a call by launching a stop streaming transaction.
+ * @param call the call.
+ */
+ public void stopCallStreaming(@NonNull Call call) {
+ if (call.getTransactionServiceWrapper() == null) {
+ return;
+ }
+ call.getTransactionServiceWrapper().stopCallStreaming(call);
+ }
}
diff --git a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
index 0be90e0..523b841 100644
--- a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
+++ b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
@@ -101,6 +101,10 @@
public static final String ACTION_CANCEL_REDIRECTED_CALL =
"com.android.server.telecom.CANCEL_REDIRECTED_CALL";
+ public static final String ACTION_HANGUP_CALL = "com.android.server.telecom.HANGUP_CALL";
+ public static final String ACTION_STOP_STREAMING =
+ "com.android.server.telecom.ACTION_STOP_STREAMING";
+
public static final String EXTRA_USERHANDLE = "userhandle";
public static final String EXTRA_REDIRECTION_OUTGOING_CALL_ID =
"android.telecom.extra.REDIRECTION_OUTGOING_CALL_ID";
@@ -242,6 +246,26 @@
} finally {
Log.endSession();
}
+ } else if (ACTION_HANGUP_CALL.equals(action)) {
+ Log.startSession("TBIP.aHC", "streamingDialog");
+ try {
+ Call call = mCallsManager.getCall(intent.getData().getSchemeSpecificPart());
+ if (call != null) {
+ mCallsManager.disconnectCall(call);
+ }
+ } finally {
+ Log.endSession();
+ }
+ } else if (ACTION_STOP_STREAMING.equals(action)) {
+ Log.startSession("TBIP.aSS", "streamingDialog");
+ try {
+ Call call = mCallsManager.getCall(intent.getData().getSchemeSpecificPart());
+ if (call != null) {
+ mCallsManager.stopCallStreaming(call);
+ }
+ } finally {
+ Log.endSession();
+ }
}
}
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 8477d49..d3ca0b7 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -48,6 +48,7 @@
import com.android.server.telecom.components.UserCallIntentProcessor;
import com.android.server.telecom.components.UserCallIntentProcessorFactory;
import com.android.server.telecom.ui.AudioProcessingNotification;
+import com.android.server.telecom.ui.CallStreamingNotification;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
import com.android.server.telecom.ui.IncomingCallNotifier;
import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
@@ -350,6 +351,12 @@
mLock, timeoutsAdapter, clockProxy, emergencyCallDiagnosticLogger);
TransactionManager transactionManager = TransactionManager.getInstance();
+
+ CallStreamingNotification callStreamingNotification =
+ new CallStreamingNotification(mContext,
+ packageName -> AppLabelProxy.Util.getAppLabel(
+ mContext.getPackageManager(), packageName), asyncTaskExecutor);
+
mCallsManager = new CallsManager(
mContext,
mLock,
@@ -386,7 +393,8 @@
asyncTaskExecutor,
blockedNumbersAdapter,
transactionManager,
- emergencyCallDiagnosticLogger);
+ emergencyCallDiagnosticLogger,
+ callStreamingNotification);
mIncomingCallNotifier = incomingCallNotifier;
incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index d83e551..ec95f39 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -682,6 +682,7 @@
public void stopCallStreaming(Call call) {
+ Log.i(this, "stopCallStreaming; callid=%s", call.getId());
if (call != null && call.isStreaming()) {
VoipCallTransaction stopStreamingTransaction = createStopStreamingTransaction(call);
addTransactionsToManager(stopStreamingTransaction, new ResultReceiver(null));
diff --git a/src/com/android/server/telecom/ui/CallStreamingNotification.java b/src/com/android/server/telecom/ui/CallStreamingNotification.java
new file mode 100644
index 0000000..752d8c8
--- /dev/null
+++ b/src/com/android/server/telecom/ui/CallStreamingNotification.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.ui;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.telecom.Log;
+import android.telecom.PhoneAccount;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.telecom.AppLabelProxy;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManagerListenerBase;
+import com.android.server.telecom.R;
+import com.android.server.telecom.TelecomBroadcastIntentProcessor;
+import com.android.server.telecom.components.TelecomBroadcastReceiver;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Class responsible for tracking if there is a call which is being streamed and posting a
+ * notification which informs the user that a call is streaming. The user has two possible actions:
+ * disconnect the call, bring the call back to the current device (stop streaming).
+ */
+public class CallStreamingNotification extends CallsManagerListenerBase implements Call.Listener {
+ // URI scheme used for data related to the notification actions.
+ public static final String CALL_ID_SCHEME = "callid";
+ // The default streaming notification ID.
+ private static final int STREAMING_NOTIFICATION_ID = 90210;
+ // Tag for streaming notification.
+ private static final String NOTIFICATION_TAG =
+ CallStreamingNotification.class.getSimpleName();
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ // Used to get the app name for the notification.
+ private final AppLabelProxy mAppLabelProxy;
+ // An executor that can be used to fire off async tasks that do not block Telecom in any manner.
+ private final Executor mAsyncTaskExecutor;
+ // The call which is treaming.
+ private Call mStreamingCall;
+ // Lock for notification post/remove -- these happen outside the Telecom sync lock.
+ private final Object mNotificationLock = new Object();
+
+ // Whether the notification is showing.
+ @GuardedBy("mNotificationLock")
+ private boolean mIsNotificationShowing = false;
+ @GuardedBy("mNotificationLock")
+ private UserHandle mNotificationUserHandle;
+
+ public CallStreamingNotification(@NonNull Context context,
+ @NonNull AppLabelProxy appLabelProxy,
+ @NonNull Executor asyncTaskExecutor) {
+ mContext = context;
+ mNotificationManager = context.getSystemService(NotificationManager.class);
+ mAppLabelProxy = appLabelProxy;
+ mAsyncTaskExecutor = asyncTaskExecutor;
+ }
+
+ @Override
+ public void onCallAdded(Call call) {
+ if (call.isStreaming()) {
+ trackStreamingCall(call);
+ enqueueStreamingNotification(call);
+ }
+ }
+
+ @Override
+ public void onCallRemoved(Call call) {
+ if (call == mStreamingCall) {
+ trackStreamingCall(null);
+ dequeueStreamingNotification();
+ }
+ }
+
+ /**
+ * Handles streaming state changes for a call.
+ * @param call the call
+ * @param isStreaming whether it is streaming or not
+ */
+ @Override
+ public void onCallStreamingStateChanged(Call call, boolean isStreaming) {
+ Log.i(this, "onCallStreamingStateChanged: call=%s, isStreaming=%b", call.getId(),
+ isStreaming);
+
+ if (isStreaming) {
+ trackStreamingCall(call);
+ enqueueStreamingNotification(call);
+ } else {
+ trackStreamingCall(null);
+ dequeueStreamingNotification();
+ }
+ }
+
+ /**
+ * Handles changes to the caller info for a call. Used to ensure we can update the photo uri
+ * if one was found.
+ * @param call the call which the caller info changed on.
+ */
+ @Override
+ public void onCallerInfoChanged(Call call) {
+ if (call == mStreamingCall) {
+ Log.i(this, "onCallerInfoChanged: call=%s, photoUri=%b", call.getId(),
+ call.getContactPhotoUri());
+ enqueueStreamingNotification(call);
+ }
+ }
+
+ /**
+ * Change the streaming call we are tracking.
+ * @param call the call.
+ */
+ private void trackStreamingCall(Call call) {
+ if (mStreamingCall != null) {
+ mStreamingCall.removeListener(this);
+ }
+ mStreamingCall = call;
+ if (mStreamingCall != null) {
+ mStreamingCall.addListener(this);
+ }
+ }
+
+ /**
+ * Enqueue an async task to post/repost the streaming notification.
+ * Note: This happens INSIDE the telecom lock.
+ * @param call the call to post notification for.
+ */
+ private void enqueueStreamingNotification(Call call) {
+ final Bitmap contactPhotoBitmap = call.getPhotoIcon();
+ mAsyncTaskExecutor.execute(() -> {
+ Icon contactPhotoIcon = null;
+ try {
+ if (contactPhotoBitmap != null) {
+ // Make the icon rounded... because there has to be hoops to jump through.
+ RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
+ mContext.getResources(), contactPhotoBitmap);
+ roundedDrawable.setCornerRadius(Math.max(contactPhotoBitmap.getWidth(),
+ contactPhotoBitmap.getHeight()) / 2.0f);
+ contactPhotoIcon = Icon.createWithBitmap(drawableToBitmap(roundedDrawable,
+ contactPhotoBitmap.getWidth(), contactPhotoBitmap.getHeight()));
+ }
+ } catch (Exception e) {
+ // All loads of things can do wrong when working with bitmaps and images, so to
+ // ensure Telecom doesn't crash, lets try/catch to be sure.
+ Log.e(this, e, "enqueueStreamingNotification: Couldn't build rounded icon");
+ }
+ showStreamingNotification(call.getId(),
+ call.getUserHandleFromTargetPhoneAccount(), call.getCallerDisplayName(),
+ call.getHandle(), contactPhotoIcon,
+ call.getTargetPhoneAccount().getComponentName().getPackageName(),
+ call.getConnectTimeMillis());
+ });
+ }
+
+ /**
+ * Dequeues the call streaming notification.
+ * Note: This is yo be called within the Telecom sync lock to launch the task to remove the call
+ * streaming notification.
+ */
+ private void dequeueStreamingNotification() {
+ mAsyncTaskExecutor.execute(() -> hideStreamingNotification());
+ }
+
+ /**
+ * Show the call streaming notification. This is intended to run outside the Telecom sync lock.
+ *
+ * @param callId the call ID we're streaming.
+ * @param userHandle the userhandle for the call.
+ * @param callerName the name of the caller/callee associated with the call
+ * @param callerAddress the address associated with the caller/callee
+ * @param photoIcon the contact photo icon if available
+ * @param appPackageName the package name for the app to post the notification for
+ * @param connectTimeMillis when the call connected (for chronometer in the notification)
+ */
+ private void showStreamingNotification(final String callId, final UserHandle userHandle,
+ String callerName, Uri callerAddress, Icon photoIcon, String appPackageName,
+ long connectTimeMillis) {
+ Log.i(this, "showStreamingNotification; callid=%s, hasPhoto=%b", callId, photoIcon != null);
+
+ // Use the caller name for the label if available, default to app name if none.
+ if (TextUtils.isEmpty(callerName)) {
+ // App did not provide a caller name, so default to app's name.
+ callerName = mAppLabelProxy.getAppLabel(appPackageName).toString();
+ }
+
+ // Action to hangup; this can use the default hangup action from the call style
+ // notification.
+ Intent hangupIntent = new Intent(TelecomBroadcastIntentProcessor.ACTION_HANGUP_CALL,
+ Uri.fromParts(CALL_ID_SCHEME, callId, null),
+ mContext, TelecomBroadcastReceiver.class);
+ PendingIntent hangupPendingIntent = PendingIntent.getBroadcast(mContext, 0, hangupIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ // Action to switch here.
+ Intent switchHereIntent = new Intent(TelecomBroadcastIntentProcessor.ACTION_STOP_STREAMING,
+ Uri.fromParts(CALL_ID_SCHEME, callId, null),
+ mContext, TelecomBroadcastReceiver.class);
+ PendingIntent switchHerePendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ switchHereIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ // Apply a span to the string to colorize it using the "answer" color.
+ Spannable spannable = new SpannableString(
+ mContext.getString(R.string.call_streaming_notification_action_switch_here));
+ spannable.setSpan(new ForegroundColorSpan(
+ com.android.internal.R.color.call_notification_answer_color), 0, spannable.length(),
+ Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+
+ // Use the "phone link" icon per mock.
+ Icon switchHereIcon = Icon.createWithResource(mContext, R.drawable.gm_phonelink);
+ Notification.Action.Builder switchHereBuilder = new Notification.Action.Builder(
+ switchHereIcon,
+ spannable,
+ switchHerePendingIntent);
+ Notification.Action switchHereAction = switchHereBuilder.build();
+
+ // Notifications use a "person" entity to identify caller/callee.
+ Person.Builder personBuilder = new Person.Builder()
+ .setName(callerName);
+
+ // Some apps use phone numbers to identify; these are something the notification framework
+ // can lookup in contacts to provide more data
+ if (callerAddress != null && PhoneAccount.SCHEME_TEL.equals(callerAddress)) {
+ personBuilder.setUri(callerAddress.toString());
+ }
+ if (photoIcon != null) {
+ personBuilder.setIcon(photoIcon);
+ }
+ Person person = personBuilder.build();
+
+ // Call Style notification requires a full screen intent, so we'll just link in a null
+ // pending intent
+ Intent nullIntent = new Intent();
+ PendingIntent nullPendingIntent = PendingIntent.getBroadcast(mContext, 0, nullIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ Notification.Builder builder = new Notification.Builder(mContext,
+ NotificationChannelManager.CHANNEL_ID_CALL_STREAMING)
+ // Use call style to get the general look and feel for the notification; it provides
+ // a hangup action with the right action already so we can leverage that. The
+ // "switch here" action will be a custom action defined later.
+ .setStyle(Notification.CallStyle.forOngoingCall(person, hangupPendingIntent))
+ .setSmallIcon(R.drawable.ic_phone)
+ .setContentText(mContext.getString(
+ R.string.call_streaming_notification_body))
+ // Report call time
+ .setWhen(connectTimeMillis)
+ .setShowWhen(true)
+ .setUsesChronometer(true)
+ // Set the full screen intent; this is just tricking notification manager into
+ // letting us use this style. Sssh.
+ .setFullScreenIntent(nullPendingIntent, true)
+ .setColorized(true)
+ .addAction(switchHereAction);
+ Notification notification = builder.build();
+
+ synchronized(mNotificationLock) {
+ mIsNotificationShowing = true;
+ mNotificationUserHandle = userHandle;
+ try {
+ mNotificationManager.notifyAsUser(NOTIFICATION_TAG, STREAMING_NOTIFICATION_ID,
+ notification, userHandle);
+ } catch (Exception e) {
+ // We don't want to crash Telecom if something changes with the requirements for the
+ // notification.
+ Log.e(this, e, "Notification post failed.");
+ }
+ }
+ }
+
+ /**
+ * Removes the posted streaming notification. Intended to run outside the telecom lock.
+ */
+ private void hideStreamingNotification() {
+ Log.i(this, "hideStreamingNotification");
+ synchronized(mNotificationLock) {
+ if (mIsNotificationShowing) {
+ mIsNotificationShowing = false;
+ mNotificationManager.cancelAsUser(NOTIFICATION_TAG,
+ STREAMING_NOTIFICATION_ID, mNotificationUserHandle);
+ }
+ }
+ }
+
+ public static Bitmap drawableToBitmap(@Nullable Drawable drawable, int width, int height) {
+ if (drawable == null) {
+ return null;
+ }
+
+ Bitmap bitmap;
+ if (drawable instanceof BitmapDrawable) {
+ bitmap = ((BitmapDrawable) drawable).getBitmap();
+ } else {
+ if (width > 0 || height > 0) {
+ bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ } else if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+ // Needed for drawables that are just a colour.
+ bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ } else {
+ bitmap =
+ Bitmap.createBitmap(
+ drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ }
+
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ }
+ return bitmap;
+ }
+}
diff --git a/src/com/android/server/telecom/ui/NotificationChannelManager.java b/src/com/android/server/telecom/ui/NotificationChannelManager.java
index 58794a6..a0baa03 100644
--- a/src/com/android/server/telecom/ui/NotificationChannelManager.java
+++ b/src/com/android/server/telecom/ui/NotificationChannelManager.java
@@ -40,6 +40,7 @@
public static final String CHANNEL_ID_AUDIO_PROCESSING = "TelecomBackgroundAudioProcessing";
public static final String CHANNEL_ID_DISCONNECTED_CALLS = "TelecomDisconnectedCalls";
public static final String CHANNEL_ID_IN_CALL_SERVICE_CRASH = "TelecomInCallServiceCrash";
+ public static final String CHANNEL_ID_CALL_STREAMING = "TelecomCallStreaming";
private BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
@Override
@@ -63,6 +64,7 @@
createOrUpdateChannel(context, CHANNEL_ID_AUDIO_PROCESSING);
createOrUpdateChannel(context, CHANNEL_ID_DISCONNECTED_CALLS);
createOrUpdateChannel(context, CHANNEL_ID_IN_CALL_SERVICE_CRASH);
+ createOrUpdateChannel(context, CHANNEL_ID_CALL_STREAMING);
}
private void createOrUpdateChannel(Context context, String channelId) {
@@ -127,6 +129,14 @@
lights = true;
vibration = true;
sound = null;
+ case CHANNEL_ID_CALL_STREAMING:
+ name = context.getText(R.string.notification_channel_call_streaming);
+ importance = NotificationManager.IMPORTANCE_DEFAULT;
+ canShowBadge = false;
+ lights = false;
+ vibration = false;
+ sound = null;
+ break;
}
NotificationChannel channel = new NotificationChannel(channelId, name, importance);
diff --git a/src/com/android/server/telecom/voip/IncomingCallTransaction.java b/src/com/android/server/telecom/voip/IncomingCallTransaction.java
index c0bb93d..d35030c 100644
--- a/src/com/android/server/telecom/voip/IncomingCallTransaction.java
+++ b/src/com/android/server/telecom/voip/IncomingCallTransaction.java
@@ -17,6 +17,7 @@
package com.android.server.telecom.voip;
import static android.telecom.CallAttributes.CALL_CAPABILITIES_KEY;
+import static android.telecom.CallAttributes.DISPLAY_NAME_KEY;
import android.os.Bundle;
import android.telecom.CallAttributes;
@@ -80,6 +81,9 @@
mExtras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
mExtras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
mExtras.putInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE, callAttributes.getCallType());
+ mExtras.putCharSequence(DISPLAY_NAME_KEY, callAttributes.getDisplayName());
+ mExtras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ callAttributes.getAddress());
return mExtras;
}
}
diff --git a/src/com/android/server/telecom/voip/OutgoingCallTransaction.java b/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
index 0b17da2..b2625e6 100644
--- a/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
+++ b/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.CALL_PRIVILEGED;
import static android.telecom.CallAttributes.CALL_CAPABILITIES_KEY;
+import static android.telecom.CallAttributes.DISPLAY_NAME_KEY;
import static android.telecom.CallException.CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME;
import android.content.Context;
@@ -126,6 +127,7 @@
mExtras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
mExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
callAttributes.getCallType());
+ mExtras.putCharSequence(DISPLAY_NAME_KEY, callAttributes.getDisplayName());
return mExtras;
}
}
diff --git a/src/com/android/server/telecom/voip/VoipCallTransaction.java b/src/com/android/server/telecom/voip/VoipCallTransaction.java
index a1cc13c..38f1d54 100644
--- a/src/com/android/server/telecom/voip/VoipCallTransaction.java
+++ b/src/com/android/server/telecom/voip/VoipCallTransaction.java
@@ -18,6 +18,7 @@
import android.os.Handler;
import android.os.HandlerThread;
+import android.telecom.Log;
import com.android.server.telecom.LoggedHandlerExecutor;
import com.android.server.telecom.TelecomSystem;
@@ -80,7 +81,11 @@
}
finish();
return null;
- });
+ })
+ .exceptionally((throwable) -> {
+ Log.e(this, throwable, "Error while executing transaction.");
+ return null;
+ });;
}
public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
diff --git a/testapps/streamingtest/Android.bp b/testapps/streamingtest/Android.bp
new file mode 100644
index 0000000..bd0a582
--- /dev/null
+++ b/testapps/streamingtest/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "streamingTestApp",
+ static_libs: [
+ "androidx.legacy_legacy-support-v4",
+ "guava",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ privileged: true,
+}
diff --git a/testapps/streamingtest/AndroidManifest.xml b/testapps/streamingtest/AndroidManifest.xml
new file mode 100644
index 0000000..47e4abc
--- /dev/null
+++ b/testapps/streamingtest/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ coreApp="true"
+ package="com.android.server.telecom.streamingtest">
+
+ <uses-sdk android:minSdkVersion="28"
+ android:targetSdkVersion="33"/>
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+ <uses-permission android:name="android.permission.CALL_AUDIO_INTERCEPTION"/>
+
+ <application android:label="Streaming Test App">
+ <uses-library android:name="android.test.runner"/>
+
+ <service android:name="com.android.server.telecom.streamingtest.StreamingService"
+ android:exported="true"
+ android:permission="android.permission.BIND_CALL_STREAMING_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecom.CallStreamingService"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/testapps/streamingtest/src/com/android/server/telecom/streamingtest/StreamingService.java b/testapps/streamingtest/src/com/android/server/telecom/streamingtest/StreamingService.java
new file mode 100644
index 0000000..c76b349
--- /dev/null
+++ b/testapps/streamingtest/src/com/android/server/telecom/streamingtest/StreamingService.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.streamingtest;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.telecom.CallStreamingService;
+import android.telecom.StreamingCall;
+import android.telecom.Log;
+
+public class StreamingService extends CallStreamingService {
+ @Override
+ public void onCallStreamingStarted(@NonNull StreamingCall call) {
+ Log.i(this, "onCallStreamingStarted: call %s", call);
+ }
+
+ @Override
+ public void onCallStreamingStopped() {
+ Log.i(this, "onCallStreamingStopped");
+ }
+
+ @Override
+ public void onCallStreamingStateChanged(int state) {
+ Log.i(this, "onCallStreamingStateChanged; state=%d", state);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i(this, "onUnbind");
+ return false;
+ }
+}
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
index b868b70..3e53800 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
@@ -57,6 +57,11 @@
setContentView(R.layout.in_call_activity);
Bundle extras = getIntent().getExtras();
+ // Copy the extras with properties like call direction into the extras so the below
+ // code can access them.
+ if (extras != null && extras.containsKey(Utils.sEXTRAS_KEY)) {
+ extras.putAll(extras.getBundle(Utils.sEXTRAS_KEY));
+ }
if (extras != null) {
mCallDirection = extras.getInt(Utils.sCALL_DIRECTION_KEY, DIRECTION_INCOMING);
}
@@ -211,7 +216,7 @@
Utils.PHONE_ACCOUNT_HANDLE,
mCallDirection,
"Alan Turing",
- Uri.parse("tel:6506959001")).build();
+ Uri.parse("tel:+16506959001")).build();
mTelecomManager.addCall(callAttributes, Runnable::run,
new OutcomeReceiver<CallControl, CallException>() {
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 22a850f..a9e5fac 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -125,6 +125,7 @@
import com.android.server.telecom.callfiltering.BlockedNumbersAdapter;
import com.android.server.telecom.callfiltering.CallFilteringResult;
import com.android.server.telecom.ui.AudioProcessingNotification;
+import com.android.server.telecom.ui.CallStreamingNotification;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
import com.android.server.telecom.ui.ToastFactory;
import com.android.server.telecom.voip.TransactionManager;
@@ -257,6 +258,7 @@
@Mock private Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
@Mock private BlockedNumbersAdapter mBlockedNumbersAdapter;
@Mock private PhoneCapability mPhoneCapability;
+ @Mock private CallStreamingNotification mCallStreamingNotification;
private CallsManager mCallsManager;
@@ -327,7 +329,8 @@
command -> command.run(),
mBlockedNumbersAdapter,
TransactionManager.getTestInstance(),
- mEmergencyCallDiagnosticLogger);
+ mEmergencyCallDiagnosticLogger,
+ mCallStreamingNotification);
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);