Merge "Revert submission"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..75feeb6
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+  "presubmit": [
+    {
+      "name": "TelecomUnitTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "TeleServiceTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/res/values/config.xml b/res/values/config.xml
index 5b9636d..9cbbf46 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -53,11 +53,6 @@
          Devices should overlay this value based on the type of vibration hardware they employ. -->
     <bool name="use_simple_vibration_pattern">false</bool>
 
-    <!-- When true, if Telecom is playing the ringtone, it will attempt to pause for some time
-         between repeats of the ringtone.
-         When false, the ringtone will be looping with no pause. -->
-    <bool name="should_pause_between_ringtone_repeats">true</bool>
-
     <!-- Threshold for the X+Y component of gravity needed for the device orientation to be
          classified as being on a user's ear. -->
     <item name="device_on_ear_xy_gravity_threshold" format="float" type="dimen">5.5</item>
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 8f11882..1a7d0f7 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -43,10 +43,6 @@
     // Message codes used with the ringtone thread.
     private static final int EVENT_PLAY = 1;
     private static final int EVENT_STOP = 2;
-    private static final int EVENT_REPEAT = 3;
-
-    // The interval in which to restart the ringer.
-    private static final int RESTART_RINGER_MILLIS = 3000;
 
     /** Handler running on the ringtone thread. */
     private Handler mHandler;
@@ -60,26 +56,10 @@
      */
     private CompletableFuture<Boolean> mHapticsFuture = null;
 
-    /**
-     * Determines if the {@link AsyncRingtonePlayer} should pause between repeats of the ringtone.
-     * When {@code true}, the system will check if the ringtone has stopped every
-     * {@link #RESTART_RINGER_MILLIS} and restart the ringtone if it has stopped.  This does not
-     * guarantee that there is {@link #RESTART_RINGER_MILLIS} between each repeat of the ringtone,
-     * rather it ensures that for short ringtones, or ringtones which are not a multiple of
-     * {@link #RESTART_RINGER_MILLIS} in duration that there will be some pause between repetitions.
-     * When {@code false}, the ringtone will be looped continually with no attempt to pause between
-     * repeats.
-     */
-    private boolean mShouldPauseBetweenRepeat = true;
-
     public AsyncRingtonePlayer() {
         // Empty
     }
 
-    public AsyncRingtonePlayer(boolean shouldPauseBetweenRepeat) {
-        mShouldPauseBetweenRepeat = shouldPauseBetweenRepeat;
-    }
-
     /**
      * Plays the appropriate ringtone for the specified call.
      * If {@link VolumeShaper.Configuration} is specified, it is applied to the ringtone to change
@@ -156,9 +136,6 @@
                     case EVENT_PLAY:
                         handlePlay((SomeArgs) msg.obj);
                         break;
-                    case EVENT_REPEAT:
-                        handleRepeat();
-                        break;
                     case EVENT_STOP:
                         handleStop();
                         break;
@@ -241,43 +218,18 @@
                 }
             }
 
-            if (mShouldPauseBetweenRepeat) {
-                // We're trying to pause between repeats, so the ringtone will not intentionally loop.
-                // Instead, we'll use a handler message to perform repeats.
-                handleRepeat();
-            } else {
-                mRingtone.setLooping(true);
-                if (mRingtone.isPlaying()) {
-                    Log.d(this, "Ringtone already playing.");
-                    return;
-                }
-                mRingtone.play();
-                Log.i(this, "Play ringtone, looping.");
+            mRingtone.setLooping(true);
+            if (mRingtone.isPlaying()) {
+                Log.d(this, "Ringtone already playing.");
+                return;
             }
+            mRingtone.play();
+            Log.i(this, "Play ringtone, looping.");
         } finally {
             Log.cancelSubsession(session);
         }
     }
 
-    private void handleRepeat() {
-        if (mRingtone == null) {
-            return;
-        }
-        if (mRingtone.isPlaying()) {
-            Log.d(this, "Ringtone already playing.");
-        } else {
-            mRingtone.play();
-            Log.i(this, "Repeat ringtone.");
-        }
-
-        // Repost event to restart ringer in {@link RESTART_RINGER_MILLIS}.
-        synchronized(this) {
-            if (!mHandler.hasMessages(EVENT_REPEAT)) {
-                mHandler.sendEmptyMessageDelayed(EVENT_REPEAT, RESTART_RINGER_MILLIS);
-            }
-        }
-    }
-
     /**
      * Stops the playback of the ringtone. Executes on the ringtone-thread.
      */
@@ -292,10 +244,6 @@
         }
 
         synchronized(this) {
-            // At the time that STOP is handled, there should be no need for repeat messages in the
-            // queue.
-            mHandler.removeMessages(EVENT_REPEAT);
-
             if (mHandler.hasMessages(EVENT_PLAY)) {
                 Log.v(this, "Keeping alive ringtone thread for subsequent play request.");
             } else {
diff --git a/src/com/android/server/telecom/BluetoothAdapterProxy.java b/src/com/android/server/telecom/BluetoothAdapterProxy.java
index 72962b8..41b4faa 100644
--- a/src/com/android/server/telecom/BluetoothAdapterProxy.java
+++ b/src/com/android/server/telecom/BluetoothAdapterProxy.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
@@ -37,4 +38,11 @@
         }
         return mBluetoothAdapter.getProfileProxy(context, listener, profile);
     }
+
+    public boolean setActiveDevice(BluetoothDevice device, int profiles) {
+        if (mBluetoothAdapter == null) {
+            return false;
+        }
+        return mBluetoothAdapter.setActiveDevice(device, profiles);
+    }
 }
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 3d04e50..8773ea2 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -136,9 +136,11 @@
         void onPhoneAccountChanged(Call call);
         void onConferenceableCallsChanged(Call call);
         void onConferenceStateChanged(Call call, boolean isConference);
+        void onCdmaConferenceSwap(Call call);
         boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout);
         void onHoldToneRequested(Call call);
         void onCallHoldFailed(Call call);
+        void onCallSwitchFailed(Call call);
         void onConnectionEvent(Call call, String event, Bundle extras);
         void onExternalCallChanged(Call call, boolean isExternalCall);
         void onRttInitiationFailure(Call call, int reason);
@@ -207,6 +209,8 @@
         @Override
         public void onConferenceStateChanged(Call call, boolean isConference) {}
         @Override
+        public void onCdmaConferenceSwap(Call call) {}
+        @Override
         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
             return false;
         }
@@ -215,6 +219,8 @@
         @Override
         public void onCallHoldFailed(Call call) {}
         @Override
+        public void onCallSwitchFailed(Call call) {}
+        @Override
         public void onConnectionEvent(Call call, String event, Bundle extras) {}
         @Override
         public void onExternalCallChanged(Call call, boolean isExternalCall) {}
@@ -325,6 +331,11 @@
      */
     private int mHandlePresentation;
 
+    /**
+     * The verification status for an incoming call's number.
+     */
+    private @Connection.VerificationStatus int mCallerNumberVerificationStatus;
+
     /** The caller display name (CNAP) set by the connection service. */
     private String mCallerDisplayName;
 
@@ -575,6 +586,13 @@
     private boolean mHasGoneActiveBefore = false;
 
     /**
+     * Indicates the package name of the {@link android.telecom.CallScreeningService} which should
+     * be sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} intent upon disconnection
+     * of a call.
+     */
+    private String mPostCallPackageName;
+
+    /**
      * Persists the specified parameters and initializes the new instance.
      * @param context The context.
      * @param repository The connection service repository.
@@ -1085,6 +1103,14 @@
         return mHandlePresentation;
     }
 
+    public void setCallerNumberVerificationStatus(
+            @Connection.VerificationStatus int callerNumberVerificationStatus) {
+        mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+    }
+
+    public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+        return mCallerNumberVerificationStatus;
+    }
 
     void setHandle(Uri handle) {
         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
@@ -1590,14 +1616,6 @@
         Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
                 connectionCapabilities));
         if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
-            // If the phone account does not support video calling, and the connection capabilities
-            // passed in indicate that the call supports video, remove those video capabilities.
-            if (!isVideoCallingSupportedByPhoneAccount()
-                    && doesCallSupportVideo(connectionCapabilities)) {
-                Log.w(this, "setConnectionCapabilities: attempt to set connection as video " +
-                        "capable when not supported by the phone account.");
-                connectionCapabilities = removeVideoCapabilities(connectionCapabilities);
-            }
             int previousCapabilities = mConnectionCapabilities;
             mConnectionCapabilities = connectionCapabilities;
             for (Listener l : mListeners) {
@@ -1836,6 +1854,8 @@
 
         switch (mCallDirection) {
             case CALL_DIRECTION_INCOMING:
+                setCallerNumberVerificationStatus(connection.getCallerNumberVerificationStatus());
+
                 // Listeners (just CallsManager for now) will be responsible for checking whether
                 // the call should be blocked.
                 for (Listener l : mListeners) {
@@ -2396,6 +2416,9 @@
                     mConferenceLevelActiveCall = null;
                     break;
             }
+            for (Listener l : mListeners) {
+                l.onCdmaConferenceSwap(this);
+            }
         }
     }
 
@@ -3207,6 +3230,10 @@
             for (Listener l : mListeners) {
                 l.onCallHoldFailed(this);
             }
+        } else if (Connection.EVENT_CALL_SWITCH_FAILED.equals(event)) {
+            for (Listener l : mListeners) {
+                l.onCallSwitchFailed(this);
+            }
         } else {
             for (Listener l : mListeners) {
                 l.onConnectionEvent(this, event, extras);
@@ -3398,4 +3425,23 @@
         }
         return CALL_DIRECTION_UNDEFINED;
     }
+
+    /**
+     * Set the package name of the {@link android.telecom.CallScreeningService} which should be sent
+     * the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a call.
+     * @param packageName post call screen service package name.
+     */
+    public void setPostCallPackageName(String packageName) {
+        mPostCallPackageName = packageName;
+    }
+
+    /**
+     * Return the package name of the {@link android.telecom.CallScreeningService} which should be
+     * sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a
+     * call.
+     * @return post call screen service package name.
+     */
+    public String getPostCallPackageName() {
+        return mPostCallPackageName;
+    }
 }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e825a0e..9bd1fe5 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -16,6 +16,18 @@
 
 package com.android.server.telecom;
 
+import static android.telecom.TelecomManager.ACTION_POST_CALL;
+import static android.telecom.TelecomManager.DURATION_LONG;
+import static android.telecom.TelecomManager.DURATION_MEDIUM;
+import static android.telecom.TelecomManager.DURATION_SHORT;
+import static android.telecom.TelecomManager.DURATION_VERY_SHORT;
+import static android.telecom.TelecomManager.EXTRA_CALL_DURATION;
+import static android.telecom.TelecomManager.EXTRA_DISCONNECT_CAUSE;
+import static android.telecom.TelecomManager.EXTRA_HANDLE;
+import static android.telecom.TelecomManager.MEDIUM_CALL_TIME_MS;
+import static android.telecom.TelecomManager.SHORT_CALL_TIME_MS;
+import static android.telecom.TelecomManager.VERY_SHORT_CALL_TIME_MS;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -154,6 +166,7 @@
         void onDisconnectedTonePlaying(boolean isTonePlaying);
         void onConnectionTimeChanged(Call call);
         void onConferenceStateChanged(Call call, boolean isConference);
+        void onCdmaConferenceSwap(Call call);
     }
 
     /** Interface used to define the action which is executed delay under some condition. */
@@ -600,6 +613,7 @@
     @Override
     public void onSuccessfulOutgoingCall(Call call, int callState) {
         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
+        call.setPostCallPackageName(getRoleManagerAdapter().getDefaultCallScreeningApp());
 
         setCallState(call, callState, "successful outgoing call");
         if (!mCalls.contains(call)) {
@@ -739,6 +753,9 @@
         }
 
         if (result.shouldAllowCall) {
+            incomingCall.setPostCallPackageName(
+                    getRoleManagerAdapter().getDefaultCallScreeningApp());
+
             if (hasMaximumManagedRingingCalls(incomingCall)) {
                 if (shouldSilenceInsteadOfReject(incomingCall)) {
                     incomingCall.silence();
@@ -913,6 +930,14 @@
     }
 
     @Override
+    public void onCdmaConferenceSwap(Call call) {
+        // SWAP was executed on a CDMA conference
+        for (CallsManagerListener listener : mListeners) {
+            listener.onCdmaConferenceSwap(call);
+        }
+    }
+
+    @Override
     public void onIsVoipAudioModeChanged(Call call) {
         for (CallsManagerListener listener : mListeners) {
             listener.onIsVoipAudioModeChanged(call);
@@ -1030,12 +1055,22 @@
 
     @Override
     public void onCallHoldFailed(Call call) {
-        // Normally, we don't care whether a call hold has failed. However, if a call was held in
-        // order to answer an incoming call, that incoming call needs to be brought out of the
-        // ANSWERED state so that the user can try the operation again.
+        markAllAnsweredCallAsRinging(call, "hold");
+    }
+
+    @Override
+    public void onCallSwitchFailed(Call call) {
+        markAllAnsweredCallAsRinging(call, "switch");
+    }
+
+    private void markAllAnsweredCallAsRinging(Call call, String actionName) {
+        // Normally, we don't care whether a call hold or switch has failed.
+        // However, if a call was held or switched in order to answer an incoming call, that
+        // incoming call needs to be brought out of the ANSWERED state so that the user can
+        // try the operation again.
         for (Call call1 : mCalls) {
             if (call1 != call && call1.getState() == CallState.ANSWERED) {
-                setCallState(call1, CallState.RINGING, "hold failed on other call");
+                setCallState(call1, CallState.RINGING, actionName + " failed on other call");
             }
         }
     }
@@ -3247,6 +3282,10 @@
             // TODO: Define expected state transitions here, and log when an
             // unexpected transition occurs.
             if (call.setState(newState, tag)) {
+                if ((oldState != CallState.AUDIO_PROCESSING) &&
+                        (newState == CallState.DISCONNECTED)) {
+                    maybeSendPostCallScreenIntent(call);
+                }
                 maybeShowErrorDialogOnDisconnect(call);
 
                 Trace.beginSection("onCallStateChanged");
@@ -4826,4 +4865,28 @@
     public LinkedList<HandlerThread> getGraphHandlerThreads() {
         return mGraphHandlerThreads;
     }
+
+    private void maybeSendPostCallScreenIntent(Call call) {
+        if (call.isEmergencyCall() || (call.isNetworkIdentifiedEmergencyCall()) ||
+                (call.getPostCallPackageName() == null)) {
+            return;
+        }
+
+        Intent intent = new Intent(ACTION_POST_CALL);
+        intent.setPackage(call.getPostCallPackageName());
+        intent.putExtra(EXTRA_HANDLE, call.getHandle());
+        intent.putExtra(EXTRA_DISCONNECT_CAUSE, call.getDisconnectCause().getCode());
+        long duration = call.getAgeMillis();
+        int durationCode = DURATION_VERY_SHORT;
+        if ((duration >= VERY_SHORT_CALL_TIME_MS) && (duration < SHORT_CALL_TIME_MS)) {
+            durationCode = DURATION_SHORT;
+        } else if ((duration >= SHORT_CALL_TIME_MS) && (duration < MEDIUM_CALL_TIME_MS)) {
+            durationCode = DURATION_MEDIUM;
+        } else if (duration >= MEDIUM_CALL_TIME_MS) {
+            durationCode = DURATION_LONG;
+        }
+        intent.putExtra(EXTRA_CALL_DURATION, durationCode);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivityAsUser(intent, mCurrentUserHandle);
+    }
 }
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index 4fa2ee5..e0d2831 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -100,4 +100,8 @@
     @Override
     public void onConferenceStateChanged(Call call, boolean isConference) {
     }
+
+    @Override
+    public void onCdmaConferenceSwap(Call call) {
+    }
 }
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index d608852..dd50318 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -813,8 +813,11 @@
                     // ParcelableConnection is in fact registered to Telecom and is being called
                     // from the correct user.
                     List<PhoneAccountHandle> accountHandles =
+                    // Include CAPABILITY_EMERGENCY_CALLS_ONLY in this list in case we are adding
+                    // an emergency call.
                             mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null /*uriScheme*/,
-                                    false /*includeDisabledAccounts*/, userHandle);
+                            false /*includeDisabledAccounts*/, userHandle, 0 /*capabilities*/,
+                            0 /*excludedCapabilities*/);
                     PhoneAccountHandle phoneAccountHandle = null;
                     for (PhoneAccountHandle accountHandle : accountHandles) {
                         if(accountHandle.equals(callingPhoneAccountHandle)) {
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 9fc06e4..b18c6ff 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -865,7 +865,7 @@
                         info.isExternalCallsSupported(), includeRttCall,
                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 try {
-                    inCallService.addCall(parcelableCall);
+                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -929,7 +929,7 @@
                         info.isExternalCallsSupported(), includeRttCall,
                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 try {
-                    inCallService.addCall(parcelableCall);
+                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -961,7 +961,8 @@
                         );
 
                 try {
-                    inCallService.updateCall(parcelableCall);
+                    inCallService.updateCall(
+                            sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -1046,6 +1047,12 @@
         updateCall(call);
     }
 
+    @Override
+    public void onCdmaConferenceSwap(Call call) {
+        Log.d(this, "onCdmaConferenceSwap %s", call);
+        updateCall(call);
+    }
+
     void bringToForeground(boolean showDialpad) {
         if (!mInCallServices.isEmpty()) {
             for (IInCallService inCallService : mInCallServices.values()) {
@@ -1446,13 +1453,14 @@
                 // Track the call if we don't already know about it.
                 addCall(call);
                 numCallsSent += 1;
-                inCallService.addCall(ParcelableCallUtils.toParcelableCall(
+                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
                         call,
                         true /* includeVideoProvider */,
                         mCallsManager.getPhoneAccountRegistrar(),
                         info.isExternalCallsSupported(),
                         includeRttCall,
-                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI));
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
+                inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
             } catch (RemoteException ignored) {
             }
         }
@@ -1522,7 +1530,8 @@
                 componentsUpdated.add(componentName);
 
                 try {
-                    inCallService.updateCall(parcelableCall);
+                    inCallService.updateCall(
+                            sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -1654,6 +1663,21 @@
         return childCalls;
     }
 
+    private ParcelableCall sanitizeParcelableCallForService(
+            InCallServiceInfo info, ParcelableCall parcelableCall) {
+        ParcelableCall.ParcelableCallBuilder builder =
+                ParcelableCall.ParcelableCallBuilder.fromParcelableCall(parcelableCall);
+        // Check for contacts permission. If it's not there, remove the contactsDisplayName.
+        PackageManager pm = mContext.getPackageManager();
+        if (pm.checkPermission(Manifest.permission.READ_CONTACTS,
+                info.getComponentName().getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+            builder.setContactDisplayName(null);
+        }
+
+        // TODO: move all the other service-specific sanitizations in here
+        return builder.createParcelableCall();
+    }
+
     @VisibleForTesting
     public Handler getHandler() {
         return mHandler;
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index 1abd6fb..7f71ad2 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -519,7 +519,13 @@
     private boolean isPotentialEmergencyNumber(String number) {
         Log.v(this, "Checking restrictions for number : %s", Log.pii(number));
         if (number == null) return false;
-        return mContext.getSystemService(TelephonyManager.class).isPotentialEmergencyNumber(number);
+        try {
+            return mContext.getSystemService(TelephonyManager.class).isPotentialEmergencyNumber(
+                    number);
+        } catch (Exception e) {
+            Log.e(this, e, "isPotentialEmergencyNumber: Telephony threw an exception.");
+            return false;
+        }
     }
 
     /**
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index be44131..69d9c5e 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -220,6 +220,11 @@
             callDirection = DIRECTION_OUTGOING;
         }
 
+        String activeChildCallId = null;
+        if (call.getConferenceLevelActiveCall() != null) {
+            activeChildCallId = call.getConferenceLevelActiveCall().getId();
+        }
+
         Bundle extras;
         if (isForSystemDialer) {
             extras = call.getExtras();
@@ -227,34 +232,38 @@
             extras = sanitizeExtras(call.getExtras());
         }
 
-        return new ParcelableCall(
-                call.getId(),
-                state,
-                call.getDisconnectCause(),
-                call.getCannedSmsResponses(),
-                capabilities,
-                properties,
-                supportedAudioRoutes,
-                connectTimeMillis,
-                handle,
-                call.getHandlePresentation(),
-                callerDisplayName,
-                call.getCallerDisplayNamePresentation(),
-                call.getGatewayInfo(),
-                call.getTargetPhoneAccount(),
-                includeVideoProvider,
-                includeVideoProvider ? call.getVideoProvider() : null,
-                includeRttCall,
-                rttCall,
-                parentCallId,
-                childCallIds,
-                call.getStatusHints(),
-                call.getVideoState(),
-                conferenceableCallIds,
-                call.getIntentExtras(),
-                extras,
-                call.getCreationTimeMillis(),
-                callDirection);
+        return new ParcelableCall.ParcelableCallBuilder()
+                .setId(call.getId())
+                .setState(state)
+                .setDisconnectCause(call.getDisconnectCause())
+                .setCannedSmsResponses(call.getCannedSmsResponses())
+                .setCapabilities(capabilities)
+                .setProperties(properties)
+                .setSupportedAudioRoutes(supportedAudioRoutes)
+                .setConnectTimeMillis(connectTimeMillis)
+                .setHandle(handle)
+                .setHandlePresentation(call.getHandlePresentation())
+                .setCallerDisplayName(callerDisplayName)
+                .setCallerDisplayNamePresentation(call.getCallerDisplayNamePresentation())
+                .setGatewayInfo(call.getGatewayInfo())
+                .setAccountHandle(call.getTargetPhoneAccount())
+                .setIsVideoCallProviderChanged(includeVideoProvider)
+                .setVideoCallProvider(includeVideoProvider ? call.getVideoProvider() : null)
+                .setIsRttCallChanged(includeRttCall)
+                .setRttCall(rttCall)
+                .setParentCallId(parentCallId)
+                .setChildCallIds(childCallIds)
+                .setStatusHints(call.getStatusHints())
+                .setVideoState(call.getVideoState())
+                .setConferenceableCallIds(conferenceableCallIds)
+                .setIntentExtras(call.getIntentExtras())
+                .setExtras(extras)
+                .setCreationTimeMillis(call.getCreationTimeMillis())
+                .setCallDirection(callDirection)
+                .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
+                .setContactDisplayName(call.getName())
+                .setActiveChildCallId(activeChildCallId)
+                .createParcelableCall();
     }
 
     /**
@@ -267,6 +276,7 @@
      *     <li>Connection time</li>
      *     <li>Handle (phone number)</li>
      *     <li>Handle (phone number) presentation</li>
+     *     <li>Caller number verification status (verstat)</li>
      * </ul>
      * All other fields are nulled or set to 0 values.
      * Where the call screening service is part of the system dialer, the
@@ -297,34 +307,38 @@
             callExtras = new Bundle();
         }
 
-        return new ParcelableCall(
-                call.getId(),
-                getParcelableState(call, false /* supportsExternalCalls */),
-                new DisconnectCause(DisconnectCause.UNKNOWN),
-                null, /* cannedSmsResponses */
-                0, /* capabilities */
-                0, /* properties */
-                0, /* supportedAudioRoutes */
-                call.getConnectTimeMillis(),
-                handle,
-                call.getHandlePresentation(),
-                null, /* callerDisplayName */
-                0 /* callerDisplayNamePresentation */,
-                null, /* gatewayInfo */
-                null, /* targetPhoneAccount */
-                false, /* includeVideoProvider */
-                null, /* videoProvider */
-                false, /* includeRttCall */
-                null, /* rttCall */
-                null, /* parentCallId */
-                null, /* childCallIds */
-                null, /* statusHints */
-                0, /* videoState */
-                Collections.emptyList(), /* conferenceableCallIds */
-                null, /* intentExtras */
-                callExtras, /* callExtras */
-                call.getCreationTimeMillis(),
-                callDirection);
+        return new ParcelableCall.ParcelableCallBuilder()
+                .setId(call.getId())
+                .setState(getParcelableState(call, false /* supportsExternalCalls */))
+                .setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN))
+                .setCannedSmsResponses(null)
+                .setCapabilities(0)
+                .setProperties(0)
+                .setSupportedAudioRoutes(0)
+                .setConnectTimeMillis(call.getConnectTimeMillis())
+                .setHandle(handle)
+                .setHandlePresentation(call.getHandlePresentation())
+                .setCallerDisplayName(null)
+                .setCallerDisplayNamePresentation(0)
+                .setGatewayInfo(null)
+                .setAccountHandle(null)
+                .setIsVideoCallProviderChanged(false)
+                .setVideoCallProvider(null)
+                .setIsRttCallChanged(false)
+                .setRttCall(null)
+                .setParentCallId(null)
+                .setChildCallIds(null)
+                .setStatusHints(null)
+                .setVideoState(0)
+                .setConferenceableCallIds(Collections.emptyList())
+                .setIntentExtras(null)
+                .setExtras(callExtras)
+                .setCreationTimeMillis(call.getCreationTimeMillis())
+                .setCallDirection(callDirection)
+                .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
+                .setContactDisplayName(null)
+                .setActiveChildCallId(null)
+                .createParcelableCall();
     }
 
     /**
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index c1af159..3daa452 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -34,6 +34,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -449,8 +450,8 @@
             try {
                 Log.startSession("TSI.rPA");
                 synchronized (mLock) {
-                    if (!mContext.getApplicationContext().getResources().getBoolean(
-                            com.android.internal.R.bool.config_voice_capable)) {
+                    if (!((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                                .isVoiceCapable()) {
                         Log.w(this,
                                 "registerPhoneAccount not allowed on non-voice capable device.");
                         return;
@@ -1466,6 +1467,24 @@
             return BlockedNumbersActivity.getIntentForStartingActivity();
         }
 
+
+        @Override
+        public Intent createLaunchEmergencyDialerIntent(String number) {
+            String packageName = mContext.getApplicationContext().getString(
+                    com.android.internal.R.string.config_emergency_dialer_package);
+            Intent intent = new Intent(Intent.ACTION_DIAL_EMERGENCY)
+                    .setPackage(packageName);
+            ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0 /* flags*/);
+            if (resolveInfo == null) {
+                // No matching activity from config, fallback to default platform implementation
+                intent.setPackage(null);
+            }
+            if (!TextUtils.isEmpty(number) && TextUtils.isDigitsOnly(number)) {
+                intent.setData(Uri.parse("tel:" + number));
+            }
+            return intent;
+        }
+
         /**
          * @see android.telecom.TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)
          */
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 92de536..7fd600c 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom.bluetooth;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
@@ -119,9 +120,11 @@
     private BluetoothHeadsetProxy mBluetoothHeadsetService;
     private BluetoothHearingAid mBluetoothHearingAidService;
     private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;
+    private BluetoothAdapterProxy mBluetoothAdapterProxy;
 
     public BluetoothDeviceManager(Context context, BluetoothAdapterProxy bluetoothAdapter) {
         if (bluetoothAdapter != null) {
+            mBluetoothAdapterProxy = bluetoothAdapter;
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                     BluetoothProfile.HEADSET);
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
@@ -246,7 +249,8 @@
         } else {
             for (BluetoothDevice device : mBluetoothHearingAidService.getActiveDevices()) {
                 if (device != null) {
-                    mBluetoothHearingAidService.setActiveDevice(null);
+                    mBluetoothAdapterProxy.setActiveDevice(null,
+                        BluetoothAdapter.ACTIVE_DEVICE_ALL);
                 }
             }
         }
@@ -269,15 +273,17 @@
                 Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
                 return false;
             }
-            return mBluetoothHearingAidService.setActiveDevice(
-                    mHearingAidDevicesByAddress.get(address));
+            return mBluetoothAdapterProxy.setActiveDevice(
+                    mHearingAidDevicesByAddress.get(address),
+                    BluetoothAdapter.ACTIVE_DEVICE_ALL);
         } else if (mHfpDevicesByAddress.containsKey(address)) {
             BluetoothDevice device = mHfpDevicesByAddress.get(address);
             if (mBluetoothHeadsetService == null) {
                 Log.w(this, "Attempting to turn on audio when the headset service is null");
                 return false;
             }
-            boolean success = mBluetoothHeadsetService.setActiveDevice(device);
+            boolean success = mBluetoothAdapterProxy.setActiveDevice(device,
+                BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
             if (!success) {
                 Log.w(this, "Couldn't set active device to %s", address);
                 return false;
@@ -304,7 +310,9 @@
 
     public void restoreHearingAidDevice() {
         if (mBluetoothHearingAidActiveDeviceCache != null && mBluetoothHearingAidService != null) {
-            mBluetoothHearingAidService.setActiveDevice(mBluetoothHearingAidActiveDeviceCache);
+            mBluetoothAdapterProxy.setActiveDevice(
+                mBluetoothHearingAidActiveDeviceCache,
+                BluetoothAdapter.ACTIVE_DEVICE_ALL);
             mBluetoothHearingAidActiveDeviceCache = null;
         }
     }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 7671abd..8a14cbd 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -146,6 +146,10 @@
             if (device == null) {
                 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args);
             } else {
+                if (!mIsInCall) {
+                    Log.i(LOG_TAG, "Ignoring hearing aid audio on since we're not in a call");
+                    return;
+                }
                 args.arg2 = device.getAddress();
                 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args);
             }
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 2acc548..e20da80 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -90,8 +90,6 @@
                     new NotificationChannelManager();
             notificationChannelManager.createChannels(context);
 
-            boolean shouldPauseBetweenRingtoneRepeat = context.getResources().getBoolean(
-                    R.bool.should_pause_between_ringtone_repeats);
             TelecomSystem.setInstance(
                     new TelecomSystem(
                             context,
@@ -173,7 +171,7 @@
                             },
                             ConnectionServiceFocusManager::new,
                             new Timeouts.Adapter(),
-                            new AsyncRingtonePlayer(shouldPauseBetweenRingtoneRepeat),
+                            new AsyncRingtonePlayer(),
                             new PhoneNumberUtilsAdapterImpl(),
                             new IncomingCallNotifier(context),
                             ToneGenerator::new,
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index 134db80..75c1996 100644
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -27,6 +27,7 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
 
 import com.android.server.telecom.CallIntentProcessor;
 import com.android.server.telecom.R;
@@ -163,8 +164,8 @@
      * @return {@code True} if the device is voice-capable.
      */
     private boolean isVoiceCapable() {
-        return mContext.getApplicationContext().getResources().getBoolean(
-                com.android.internal.R.bool.config_voice_capable);
+        return ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .isVoiceCapable();
     }
 
     /**
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index f19c13e..4238191 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -273,5 +273,13 @@
                   android:excludeFromRecents="true"
                   android:launchMode="singleInstance">
         </activity>
+
+        <activity android:name=".PostCallActivity"
+                  android:label="@string/postCallActivityLabel">
+            <intent-filter>
+                <action android:name="android.telecom.action.POST_CALL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index aa34070..fcd6eff 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -96,6 +96,8 @@
 
     <string name="rttUiLabel">Test RTT UI</string>
 
+    <string name="postCallActivityLabel">Test Post Call Screen</string>
+
     <string-array name="rtt_mode_array">
         <item>Full</item>
         <item>HCO</item>
diff --git a/testapps/src/com/android/server/telecom/testapps/PostCallActivity.java b/testapps/src/com/android/server/telecom/testapps/PostCallActivity.java
new file mode 100644
index 0000000..101a68e
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/PostCallActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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 static android.telecom.TelecomManager.EXTRA_CALL_DURATION;
+import static android.telecom.TelecomManager.EXTRA_DISCONNECT_CAUSE;
+import static android.telecom.TelecomManager.EXTRA_HANDLE;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telecom.Log;
+
+public class PostCallActivity extends Activity {
+
+    public static final String ACTION_POST_CALL = "android.telecom.action.POST_CALL";
+    public static final int DEFAULT_DISCONNECT_CAUSE = -1;
+    public static final int DEFAULT_DURATION = -1;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        final String action = intent != null ? intent.getAction() : null;
+        Log.i(this, "action: %s", action);
+        if (ACTION_POST_CALL.equals(action)) {
+            Log.i(this, "extra handle: " +
+                    intent.getParcelableExtra(EXTRA_HANDLE));
+            Log.i(this, "extra disconnect cause: " +
+                    intent.getIntExtra(EXTRA_DISCONNECT_CAUSE, DEFAULT_DISCONNECT_CAUSE));
+            Log.i(this, "extra duration: " +
+                    intent.getIntExtra(EXTRA_CALL_DURATION, DEFAULT_DURATION));
+        }
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 423e069..d536cbd 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -811,7 +811,7 @@
         when(getBlockedNumberProvider().call(
                 anyString(),
                 anyString(),
-                eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
+                eq(BlockedNumberContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
                 eq(phoneNumber),
                 nullable(Bundle.class))).thenAnswer(answer);
     }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 63ff962..bfa7a75 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom.tests;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
@@ -59,7 +60,7 @@
 
     BluetoothDeviceManager mBluetoothDeviceManager;
     BluetoothProfile.ServiceListener serviceListenerUnderTest;
-    BroadcastReceiver receiverUnderTest;
+    BluetoothStateReceiver receiverUnderTest;
 
     private BluetoothDevice device1;
     private BluetoothDevice device2;
@@ -90,8 +91,7 @@
                 serviceCaptor.capture(), eq(BluetoothProfile.HEADSET));
         serviceListenerUnderTest = serviceCaptor.getValue();
 
-        receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager,
-                null /* route mgr not needed here */);
+        receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager, mRouteManager);
 
         mBluetoothDeviceManager.setHeadsetServiceForTesting(mHeadsetProxy);
         mBluetoothDeviceManager.setHearingAidServiceForTesting(mBluetoothHearingAid);
@@ -198,15 +198,28 @@
 
     @SmallTest
     @Test
+    public void testHearingAidChangesIgnoredWhenNotInCall() {
+        receiverUnderTest.setIsInCall(false);
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+        Intent activeDeviceChangedIntent =
+                new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
+        activeDeviceChangedIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device2);
+        receiverUnderTest.onReceive(mContext, activeDeviceChangedIntent);
+
+        verify(mRouteManager).onActiveDeviceChanged(device2, true);
+        verify(mRouteManager, never()).sendMessage(BluetoothRouteManager.BT_AUDIO_IS_ON);
+    }
+
+    @SmallTest
+    @Test
     public void testConnectDisconnectAudioHeadset() {
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
-        when(mHeadsetProxy.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true);
+        when(mAdapterProxy.setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
         mBluetoothDeviceManager.connectAudio(device1.getAddress());
-        verify(mHeadsetProxy).setActiveDevice(device1);
-        verify(mHeadsetProxy).connectAudio();
-        verify(mBluetoothHearingAid, never()).setActiveDevice(nullable(BluetoothDevice.class));
-
+        verify(mAdapterProxy).setActiveDevice(device1, BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
+        verify(mAdapterProxy, never()).setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_ALL));
         mBluetoothDeviceManager.disconnectAudio();
         verify(mHeadsetProxy).disconnectAudio();
     }
@@ -217,14 +230,14 @@
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
         mBluetoothDeviceManager.connectAudio(device2.getAddress());
-        verify(mBluetoothHearingAid).setActiveDevice(device2);
+        verify(mAdapterProxy).setActiveDevice(device2, BluetoothAdapter.ACTIVE_DEVICE_ALL);
         verify(mHeadsetProxy, never()).connectAudio();
-        verify(mHeadsetProxy, never()).setActiveDevice(nullable(BluetoothDevice.class));
+        verify(mAdapterProxy, never()).setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
 
         when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(device2, null));
 
         mBluetoothDeviceManager.disconnectAudio();
-        verify(mBluetoothHearingAid).setActiveDevice(null);
+        verify(mAdapterProxy).setActiveDevice(null, BluetoothAdapter.ACTIVE_DEVICE_ALL);
         verify(mHeadsetProxy).disconnectAudio();
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 1b63a42..ef294bd 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -1193,28 +1193,9 @@
         assertTrue((newCapabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
                 == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
         assertTrue(ongoingCall.isVideoCallingSupportedByPhoneAccount());
-
-        // Fire a changed event for the phone account making it not capable.
-        mCallsManager.getPhoneAccountListener().onPhoneAccountChanged(mPhoneAccountRegistrar,
-                SIM_2_ACCOUNT);
-        newCapabilities = capabilitiesQueue.poll(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
-        assertFalse((newCapabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
-                == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
-        assertFalse(ongoingCall.isVideoCallingSupportedByPhoneAccount());
-
-        // Fire a change for an unrelated phone account.
-        PhoneAccount anotherVideoCapableAcct = new PhoneAccount.Builder(SIM_1_ACCOUNT)
-                .setCapabilities(SIM_2_ACCOUNT.getCapabilities()
-                        | PhoneAccount.CAPABILITY_VIDEO_CALLING)
-                .build();
-        mCallsManager.getPhoneAccountListener().onPhoneAccountChanged(mPhoneAccountRegistrar,
-                anotherVideoCapableAcct);
-        // Call still should not be video capable
-        assertFalse((ongoingCall.getConnectionCapabilities()
-                & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
-                == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
     }
 
+
     private Call addSpyCall() {
         return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
     }
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 3718419..131e591 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -424,6 +424,7 @@
         boolean isVoipAudioMode;
         Bundle extras;
         boolean isConferenceCreated;
+        int callerNumberVerificationStatus;
     }
 
     public class ConferenceInfo {
@@ -712,6 +713,7 @@
                 c.statusHints,
                 c.disconnectCause,
                 c.conferenceableConnectionIds,
-                c.extras);
+                c.extras,
+                c.callerNumberVerificationStatus);
     }
 }
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index ed36d29..23d22e9 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -625,6 +625,44 @@
         verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS);
     }
 
+    @MediumTest
+    @Test
+    public void testSanitizeContactName() throws Exception {
+        setupMocks(false /* isExternalCall */);
+        setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
+        when(mMockPackageManager.checkPermission(
+                matches(Manifest.permission.READ_CONTACTS),
+                matches(DEF_PKG))).thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mMockCall.getName()).thenReturn("evil");
+
+        mInCallController.bindToServices(mMockCall);
+
+        // Bind InCallServices
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                serviceConnectionCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(UserHandle.CURRENT));
+        assertEquals(1, bindIntentCaptor.getAllValues().size());
+        verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS);
+
+        IInCallService.Stub mockInCallServiceStub = mock(IInCallService.Stub.class);
+        IInCallService mockInCallService = mock(IInCallService.class);
+        when(mockInCallServiceStub.queryLocalInterface(anyString())).thenReturn(mockInCallService);
+        serviceConnectionCaptor.getValue().onServiceConnected(new ComponentName(DEF_PKG, DEF_CLASS),
+                mockInCallServiceStub);
+
+        mInCallController.onCallAdded(mMockCall);
+        ArgumentCaptor<ParcelableCall> parcelableCallCaptor =
+                ArgumentCaptor.forClass(ParcelableCall.class);
+        verify(mockInCallService).addCall(parcelableCallCaptor.capture());
+        assertTrue(TextUtils.isEmpty(parcelableCallCaptor.getValue().getContactDisplayName()));
+    }
+
     /**
      * Ensures that the {@link InCallController} will bind to a higher priority car mode service
      * when one becomes available.
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index 3438802..a966ffc 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Matchers.isNotNull;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.DisconnectCause;
+import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.Call;
@@ -429,6 +431,18 @@
         verify(mCall).disconnect(eq(0L));
     }
 
+    /**
+     * Ensure if {@link TelephonyManager#isPotentialEmergencyNumber(String)} throws an exception of
+     * any sort that we don't crash Telecom.
+     */
+    @SmallTest
+    @Test
+    public void testThrowOnIsPotentialEmergencyNumber() {
+        doThrow(new IllegalStateException()).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(anyString());
+        testUnmodifiedRegularCall();
+    }
+
     private ReceiverIntentPair regularCallTestHelper(Intent intent,
             Bundle expectedAdditionalExtras) {
         Uri handle = intent.getData();
diff --git a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
index 30b870e..6c941fe 100644
--- a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
+++ b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
@@ -2,22 +2,19 @@
 
 import static com.android.server.telecom.TelecomSystem.*;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.telecom.Connection;
-import android.telecom.GatewayInfo;
 import android.telecom.ParcelableCall;
 import android.telecom.PhoneAccountHandle;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -26,7 +23,6 @@
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ClockProxy;
-import com.android.server.telecom.ConnectionServiceRepository;
 import com.android.server.telecom.ParcelableCallUtils;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
@@ -149,6 +145,48 @@
         assertFalse(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
     }
 
+    @SmallTest
+    @Test
+    public void testVerificationStatusParcelingForScreening() {
+        checkVerStatParcelingForCallScreening(Connection.VERIFICATION_STATUS_NOT_VERIFIED, false);
+        checkVerStatParcelingForCallScreening(Connection.VERIFICATION_STATUS_NOT_VERIFIED, true);
+        checkVerStatParcelingForCallScreening(Connection.VERIFICATION_STATUS_PASSED, false);
+        checkVerStatParcelingForCallScreening(Connection.VERIFICATION_STATUS_PASSED, true);
+        checkVerStatParcelingForCallScreening(Connection.VERIFICATION_STATUS_FAILED, false);
+        checkVerStatParcelingForCallScreening(Connection.VERIFICATION_STATUS_FAILED, true);
+    }
+
+    @SmallTest
+    @Test
+    public void testVerificationStatusParcelingForDialer() {
+        checkVerStatParcelingForDialer(Connection.VERIFICATION_STATUS_NOT_VERIFIED, false);
+        checkVerStatParcelingForDialer(Connection.VERIFICATION_STATUS_NOT_VERIFIED, true);
+        checkVerStatParcelingForDialer(Connection.VERIFICATION_STATUS_PASSED, false);
+        checkVerStatParcelingForDialer(Connection.VERIFICATION_STATUS_PASSED, true);
+        checkVerStatParcelingForDialer(Connection.VERIFICATION_STATUS_FAILED, false);
+        checkVerStatParcelingForDialer(Connection.VERIFICATION_STATUS_FAILED, true);
+    }
+
+    private void checkVerStatParcelingForCallScreening(int connectionVerificationStatus,
+            boolean isForSystemDialer) {
+        mCall.setCallerNumberVerificationStatus(connectionVerificationStatus);
+        ParcelableCall call = ParcelableCallUtils.toParcelableCallForScreening(mCall,
+                isForSystemDialer /* isPartOfSystemDialer */);
+        assertEquals(connectionVerificationStatus, call.getCallerNumberVerificationStatus());
+    }
+
+    private void checkVerStatParcelingForDialer(int connectionVerificationStatus,
+            boolean isForSystemDialer) {
+        mCall.setCallerNumberVerificationStatus(connectionVerificationStatus);
+        ParcelableCall call = ParcelableCallUtils.toParcelableCall(mCall,
+                false /* includevideoProvider */,
+                null /* phoneAccountRegistrar */,
+                false /* supportsExternalCalls */,
+                false /* includeRttCall */,
+                isForSystemDialer /* isForSystemDialer */);
+        assertEquals(connectionVerificationStatus, call.getCallerNumberVerificationStatus());
+    }
+
     private Bundle getSomeExtras() {
         Bundle extras = new Bundle();
         extras.putString(Connection.EXTRA_SIP_INVITE, "scary data");
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 890fa6f..324bca2 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -190,8 +190,10 @@
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-        mComponentContextFixture.putBooleanResource(
-                com.android.internal.R.bool.config_voice_capable, true);
+
+        TelephonyManager mockTelephonyManager =
+                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        when(mockTelephonyManager.isVoiceCapable()).thenReturn(true);
 
         doReturn(mContext).when(mContext).getApplicationContext();
         doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index a4aba3d..442c310 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -581,8 +581,9 @@
         mComponentContextFixture.putResource(
                 com.android.server.telecom.R.string.incall_default_class,
                 mInCallServiceComponentNameX.getClassName());
-        mComponentContextFixture.putBooleanResource(
-                com.android.internal.R.bool.config_voice_capable, true);
+
+        doReturn(true).when(mComponentContextFixture.getTelephonyManager())
+                .isVoiceCapable();
 
         mInCallServiceFixtureX = new InCallServiceFixture();
         mInCallServiceFixtureY = new InCallServiceFixture();