Merge "Remove BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS for ConnectionService bind." into qt-dev am: f44200a565 am: 43be434335 am: 8fcb7fa2f4 am: 2d2dc88e8a am: a9d89f3ca5 am: 946f77ccd0 am: 6bfe5a3221

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/16576060

Change-Id: Ifc23d879953e9efc9210058f7acbbecbb3ea6449
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c38fa4e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.idea
+*.iml
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index bf2472f..d7786e1 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -77,8 +77,10 @@
      *         {@code False} indicates that a haptic track is NOT present on the ringtone;
      *         in this case the default vibration in {@link Ringer} should be trigger if needed.
      */
-    public @NonNull CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall,
-            @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isVibrationEnabled) {
+    public @NonNull
+    CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall,
+            @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isRingerAudible,
+            boolean isVibrationEnabled) {
         Log.d(this, "Posting play.");
         if (mHapticsFuture == null) {
             mHapticsFuture = new CompletableFuture<>();
@@ -88,7 +90,8 @@
         args.arg2 = incomingCall;
         args.arg3 = volumeShaperConfig;
         args.arg4 = isVibrationEnabled;
-        args.arg5 = Log.createSubsession();
+        args.arg5 = isRingerAudible;
+        args.arg6 = Log.createSubsession();
         postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
         return mHapticsFuture;
     }
@@ -152,30 +155,33 @@
         Call incomingCall = (Call) args.arg2;
         VolumeShaper.Configuration volumeShaperConfig = (VolumeShaper.Configuration) args.arg3;
         boolean isVibrationEnabled = (boolean) args.arg4;
-        Session session = (Session) args.arg5;
+        boolean isRingerAudible = (boolean) args.arg5;
+        Session session = (Session) args.arg6;
         args.recycle();
 
         Log.continueSession(session, "ARP.hP");
         try {
             // don't bother with any of this if there is an EVENT_STOP waiting.
             if (mHandler.hasMessages(EVENT_STOP)) {
-                if (mHapticsFuture != null) {
-                    mHapticsFuture.complete(false /* ringtoneHasHaptics */);
-                    mHapticsFuture = null;
-                }
+                completeHapticFuture(false /* ringtoneHasHaptics */);
                 return;
             }
 
-            // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. Do not play
-            // anything.
-            if (Uri.EMPTY.equals(incomingCall.getRingtone())) {
-                mRingtone = null;
-                if (mHapticsFuture != null) {
-                    mHapticsFuture.complete(false /* ringtoneHasHaptics */);
-                    mHapticsFuture = null;
+            // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected.
+            // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
+            // Use haptic-only ringtone or do not play anything.
+            if (!isRingerAudible || Uri.EMPTY.equals(incomingCall.getRingtone())) {
+                if (isVibrationEnabled) {
+                    mRingtone = factory.getHapticOnlyRingtone();
+                    if (mRingtone == null) {
+                        completeHapticFuture(false /* ringtoneHasHaptics */);
+                        return;
+                    }
+                } else {
+                    mRingtone = null;
+                    completeHapticFuture(false /* ringtoneHasHaptics */);
+                    return;
                 }
-
-                return;
             }
 
             ThreadUtil.checkNotOnMainThread();
@@ -189,34 +195,30 @@
                             ringtoneUri.toSafeString();
                     Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " +
                             "factory. Skipping ringing. Uri was: " + ringtoneUriString);
-                    if (mHapticsFuture != null) {
-                        mHapticsFuture.complete(false /* ringtoneHasHaptics */);
-                        mHapticsFuture = null;
-                    }
+                    completeHapticFuture(false /* ringtoneHasHaptics */);
                     return;
                 }
+            }
 
-                // With the ringtone to play now known, we can determine if it has haptic channels or
-                // not; we will complete the haptics future so the default vibration code in Ringer
-                // can know whether to trigger the vibrator.
-                if (mHapticsFuture != null && !mHapticsFuture.isDone()) {
-                    boolean hasHaptics = factory.hasHapticChannels(mRingtone);
-                    Log.i(this, "handlePlay: hasHaptics=%b, isVibrationEnabled=%b", hasHaptics,
-                            isVibrationEnabled);
-                    SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
-                    if (hasHaptics && (volumeShaperConfig == null
-                            || systemSettingsUtil.enableAudioCoupledVibrationForRampingRinger())) {
-                        AudioAttributes attributes = mRingtone.getAudioAttributes();
-                        Log.d(this, "handlePlay: %s haptic channel",
-                                (isVibrationEnabled ? "unmuting" : "muting"));
-                        mRingtone.setAudioAttributes(
-                                new AudioAttributes.Builder(attributes)
-                                        .setHapticChannelsMuted(!isVibrationEnabled)
-                                        .build());
-                    }
-                    mHapticsFuture.complete(hasHaptics);
-                    mHapticsFuture = null;
+            // With the ringtone to play now known, we can determine if it has haptic channels or
+            // not; we will complete the haptics future so the default vibration code in Ringer can
+            // know whether to trigger the vibrator.
+            if (mHapticsFuture != null && !mHapticsFuture.isDone()) {
+                boolean hasHaptics = factory.hasHapticChannels(mRingtone);
+                Log.i(this, "handlePlay: hasHaptics=%b, isVibrationEnabled=%b", hasHaptics,
+                        isVibrationEnabled);
+                SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
+                if (hasHaptics && (volumeShaperConfig == null
+                        || systemSettingsUtil.enableAudioCoupledVibrationForRampingRinger())) {
+                    AudioAttributes attributes = mRingtone.getAudioAttributes();
+                    Log.d(this, "handlePlay: %s haptic channel",
+                            (isVibrationEnabled ? "unmuting" : "muting"));
+                    mRingtone.setAudioAttributes(
+                            new AudioAttributes.Builder(attributes)
+                                    .setHapticChannelsMuted(!isVibrationEnabled)
+                                    .build());
                 }
+                completeHapticFuture(hasHaptics);
             }
 
             mRingtone.setLooping(true);
@@ -259,4 +261,11 @@
     public boolean isPlaying() {
         return mRingtone != null;
     }
+
+    private void completeHapticFuture(boolean ringtoneHasHaptics) {
+        if (mHapticsFuture != null) {
+            mHapticsFuture.complete(ringtoneHasHaptics);
+            mHapticsFuture = null;
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 91276de..6c9b1cc 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -280,25 +280,33 @@
                 }
                 if (mVolumeShaperConfig == null) {
                     float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION)
-                        / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
+                            / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
                     mVolumeShaperConfig = new VolumeShaper.Configuration.Builder()
-                        .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION)
-                        .setCurve(new float[] {0.f, silencePoint + EPSILON /*keep monotonicity*/,
-                            1.f}, new float[] {0.f, 0.f, 1.f})
-                        .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
-                        .build();
+                            .setDuration(
+                                    RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION)
+                            .setCurve(new float[]{0.f, silencePoint + EPSILON /*keep monotonicity*/,
+                                    1.f}, new float[]{0.f, 0.f, 1.f})
+                            .setInterpolatorType(
+                                    VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
+                            .build();
                 }
                 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall,
-                        mVolumeShaperConfig, isVibratorEnabled);
+                        mVolumeShaperConfig, attributes.isRingerAudible(), isVibratorEnabled);
             } else {
                 // Ramping ringtone is not enabled.
                 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null,
-                        isVibratorEnabled);
+                        attributes.isRingerAudible(), isVibratorEnabled);
                 effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
             }
         } else {
             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: "
                     + attributes.getInaudibleReason());
+            if (isVibratorEnabled && mIsHapticPlaybackSupportedByDevice) {
+                // Attempt to run the attentional haptic ringtone first and fallback to the default
+                // vibration effect if hapticFuture is completed with false.
+                hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null,
+                        attributes.isRingerAudible(), isVibratorEnabled);
+            }
             effect = mDefaultVibrationEffect;
         }
 
diff --git a/src/com/android/server/telecom/RingtoneFactory.java b/src/com/android/server/telecom/RingtoneFactory.java
index 6a121f7..b1846fe 100644
--- a/src/com/android/server/telecom/RingtoneFactory.java
+++ b/src/com/android/server/telecom/RingtoneFactory.java
@@ -109,6 +109,27 @@
                 Log.e(this, npe, "getRingtone: NPE while getting ringtone.");
             }
         }
+        return setRingtoneAudioAttributes(ringtone);
+    }
+
+    public Ringtone getRingtone(Call incomingCall) {
+        return getRingtone(incomingCall, null);
+    }
+
+    /** Returns a ringtone to be used when ringer is not audible for the incoming call. */
+    @Nullable
+    public Ringtone getHapticOnlyRingtone() {
+        Uri ringtoneUri = Uri.parse("file://" + mContext.getString(
+                com.android.internal.R.string.config_defaultRingtoneVibrationSound));
+        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri, null);
+        if (ringtone != null) {
+            // Make sure the sound is muted.
+            ringtone.setVolume(0);
+        }
+        return setRingtoneAudioAttributes(ringtone);
+    }
+
+    private Ringtone setRingtoneAudioAttributes(Ringtone ringtone) {
         if (ringtone != null) {
             ringtone.setAudioAttributes(new AudioAttributes.Builder()
                     .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
@@ -118,10 +139,6 @@
         return ringtone;
     }
 
-    public Ringtone getRingtone(Call incomingCall) {
-        return getRingtone(incomingCall, null);
-    }
-
     private Context getWorkProfileContextForUser(UserHandle userHandle) {
         // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle
         // itself (so we must filter out the user).
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 8d6e324..620378b 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -284,7 +284,7 @@
         public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
             //TODO: Deprecate this in S
             try {
-                enforceCallingPackage(packageName);
+                enforceCallingPackage(packageName, "getPhoneAccountsForPackage");
             } catch (SecurityException se1) {
                 EventLog.writeEvent(0x534e4554, "153995334", Binder.getCallingUid(),
                         "getPhoneAccountsForPackage: invalid calling package");
@@ -319,6 +319,13 @@
         @Override
         public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle,
                 String callingPackage) {
+            try {
+                enforceCallingPackage(callingPackage, "getPhoneAccount");
+            } catch (SecurityException se) {
+                EventLog.writeEvent(0x534e4554, "196406138", Binder.getCallingUid(),
+                        "getPhoneAccount: invalid calling package");
+                throw se;
+            }
             synchronized (mLock) {
                 final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                 if (CompatChanges.isChangeEnabled(
@@ -852,7 +859,7 @@
         public boolean hasManageOngoingCallsPermission(String callingPackage) {
             try {
                 Log.startSession("TSI.hMOCP");
-                enforceCallingPackage(callingPackage);
+                enforceCallingPackage(callingPackage, "hasManageOngoingCallsPermission");
                 return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(
                         mContext, Manifest.permission.MANAGE_ONGOING_CALLS,
                         Binder.getCallingPid(),
@@ -1464,7 +1471,7 @@
                 String callingFeatureId) {
             try {
                 Log.startSession("TSI.pC", Log.getPackageAbbreviation(callingPackage));
-                enforceCallingPackage(callingPackage);
+                enforceCallingPackage(callingPackage, "placeCall");
 
                 PhoneAccountHandle phoneAccountHandle = null;
                 boolean clearPhoneAccountHandleExtra = false;
@@ -2229,7 +2236,7 @@
             // feature is enabled ...
             enforceConnectionServiceFeature();
             // ... and the PhoneAccounts they refer to are for their own package.
-            enforceCallingPackage(packageName);
+            enforceCallingPackage(packageName, "enforcePhoneAccountModificationForPackage");
         }
     }
 
@@ -2245,8 +2252,22 @@
         }
     }
 
-    private void enforceCallingPackage(String packageName) {
-        mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
+    private void enforceCallingPackage(String packageName, String message) {
+        int packageUid = -1;
+        int callingUid = Binder.getCallingUid();
+        PackageManager pm = mContext.createContextAsUser(
+            UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+        if (pm != null) {
+            try {
+                packageUid = pm.getPackageUid(packageName, 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                // packageUid is -1
+            }
+        }
+        if (packageUid != callingUid && callingUid != Process.ROOT_UID) {
+            throw new SecurityException(message + ": Package " + packageName
+                + " does not belong to " + callingUid);
+        }
     }
 
     private void enforceConnectionServiceFeature() {
diff --git a/src/com/android/server/telecom/TtyManager.java b/src/com/android/server/telecom/TtyManager.java
index f1b74a4..10e3348 100644
--- a/src/com/android/server/telecom/TtyManager.java
+++ b/src/com/android/server/telecom/TtyManager.java
@@ -84,7 +84,8 @@
             mCurrentTtyMode = newTtyMode;
             Intent ttyModeChanged = new Intent(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
             ttyModeChanged.putExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE, mCurrentTtyMode);
-            mContext.sendBroadcastAsUser(ttyModeChanged, UserHandle.ALL);
+            mContext.sendBroadcastAsUser(ttyModeChanged, UserHandle.ALL,
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
 
             updateAudioTtyMode();
         }
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
index 4a308e0..0c30a16 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
@@ -313,11 +313,15 @@
 
     private void bindCallScreeningService(
             CompletableFuture<CallFilteringResult> resultFuture) {
-        mConnection = new CallScreeningServiceConnection(resultFuture);
+        CallScreeningServiceConnection connection = new CallScreeningServiceConnection(
+                resultFuture);
         if (!CallScreeningServiceHelper.bindCallScreeningService(mContext,
-                mCallsManager.getCurrentUserHandle(), mPackageName, mConnection)) {
+                mCallsManager.getCurrentUserHandle(), mPackageName, connection)) {
             Log.i(this, "Call screening service binding failed.");
             resultFuture.complete(mPriorStageResult);
+            mConnection = null;
+        } else {
+            mConnection = connection;
         }
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index a3b8654..058eb12 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -26,6 +26,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
@@ -35,10 +36,13 @@
 
 import android.content.Context;
 import android.content.IContentProvider;
+import android.content.pm.PackageManager;
 import android.media.AudioManager;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Process;
+import android.os.UserHandle;
 import android.provider.BlockedNumberContract;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
@@ -85,10 +89,15 @@
     private static final String TEST_BUNDLE_KEY = "android.telecom.extra.TEST";
     private static final String TEST_EVENT = "android.telecom.event.TEST";
 
+    private PackageManager mPackageManager;
+
     @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
+        mPackageManager = mContext.getPackageManager();
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
     }
 
     @Override
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
index 68caf67..9ff9986 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
@@ -62,6 +62,7 @@
 import org.mockito.Mock;
 
 import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 import java.util.concurrent.TimeUnit;
 
@@ -229,12 +230,30 @@
         CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, PKG_NAME,
                 CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, mCallsManager,
                 mAppLabelProxy, mParcelableCallUtilsConverter);
-        filter.startFilterLookup(inputResult);
+        CompletableFuture<CallFilteringResult> result = filter.startFilterLookup(inputResult)
+                .toCompletableFuture();
+
+        assertEquals(result.isDone(), false);
+
         filter.unbindCallScreeningService();
     }
 
     @SmallTest
     @Test
+    public void testBindingFailed() {
+        // Use an empty package name here, which fails in the bindCallScreeningService.
+        CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, "",
+                CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, mCallsManager,
+                mAppLabelProxy, mParcelableCallUtilsConverter);
+
+        CompletableFuture<CallFilteringResult> result = filter.startFilterLookup(inputResult)
+                .toCompletableFuture();
+
+        assertEquals(result.isDone(), true);
+    }
+
+    @SmallTest
+    @Test
     public void testAllowCall() throws Exception {
         CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, PKG_NAME,
                 CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, mCallsManager,
diff --git a/tests/src/com/android/server/telecom/tests/MissedInformationTest.java b/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
index a8e1c5f..e6cd986 100644
--- a/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
@@ -26,6 +26,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
@@ -36,8 +39,11 @@
 import android.content.ContentValues;
 import android.content.IContentProvider;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.provider.CallLog;
 import android.telecom.DisconnectCause;
 import android.telecom.TelecomManager;
@@ -73,6 +79,7 @@
     @Mock Call mIncomingCall;
     private CallsManager mCallsManager;
     private CallIntentProcessor.AdapterImpl mAdapter;
+    private PackageManager mPackageManager;
 
     @Override
     @Before
@@ -85,6 +92,9 @@
         when(mContentProvider.call(any(String.class), any(String.class),
                 any(String.class), any(Bundle.class))).thenReturn(new Bundle());
         doReturn(mContentResolver).when(mSpyContext).getContentResolver();
+        doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
+        mPackageManager = mContext.getPackageManager();
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
     }
 
     @Override
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 0e93481..00456e2 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -149,7 +149,8 @@
         when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
         when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
         when(mockRingtonePlayer.play(any(RingtoneFactory.class), any(Call.class),
-                nullable(VolumeShaper.Configuration.class), anyBoolean())).thenReturn(mFuture);
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()))
+                .thenReturn(mFuture);
         mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
                 mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
                 mockInCallController);
@@ -174,7 +175,7 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                eq(null), eq(false));
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -192,7 +193,7 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -208,7 +209,7 @@
         mRingCompletionFuture.get();
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -225,7 +226,7 @@
         mRingCompletionFuture.get();
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -241,7 +242,7 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -260,7 +261,7 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -276,8 +277,8 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer).play(
-                any(RingtoneFactory.class), any(Call.class), isNull(), eq(true));
+        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
+                eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
         verify(mockVibrator, never()).vibrate(any(VibrationEffect.class),
                 any(AudioAttributes.class));
     }
@@ -293,8 +294,10 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+        // Try to play a silent haptics ringtone
+        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
+                eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
+        // Play default vibration when future completes with no audio coupled haptics
         verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
                 any(AudioAttributes.class));
     }
@@ -305,21 +308,44 @@
         mRingerUnderTest.startCallWaiting(mockCall1);
         Ringtone mockRingtone = mock(Ringtone.class);
         when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
-        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
         mFuture.complete(false); // not using audio coupled haptics
         enableVibrationWhenRinging();
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+        // Try to play a silent haptics ringtone
+        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
+                eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
+        // Play default vibration when future completes with no audio coupled haptics
         verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
                 any(AudioAttributes.class));
     }
 
     @SmallTest
     @Test
+    public void testAudioCoupledHapticsForSilentRingtone() throws Exception {
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        Ringtone mockRingtone = mock(Ringtone.class);
+        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
+        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
+        mFuture.complete(true); // using audio coupled haptics
+        enableVibrationWhenRinging();
+        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+        mRingCompletionFuture.get();
+        verify(mockTonePlayer).stopTone();
+        // Try to play a silent haptics ringtone
+        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
+                eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
+        // Skip vibration for audio coupled haptics
+        verify(mockVibrator, never()).vibrate(any(VibrationEffect.class),
+                any(AudioAttributes.class));
+    }
+
+    @SmallTest
+    @Test
     public void testStopRingingBeforeHapticsLookupComplete() throws Exception {
         enableVibrationWhenRinging();
         Ringtone mockRingtone = mock(Ringtone.class);
@@ -329,7 +355,7 @@
         mRingerUnderTest.startRinging(mockCall1, false);
         // Make sure we haven't started the vibrator yet, but have started ringing.
         verify(mockRingtonePlayer).play(nullable(RingtoneFactory.class), nullable(Call.class),
-                nullable(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never()).vibrate(nullable(VibrationEffect.class),
                 nullable(AudioAttributes.class));
         // Simulate something stopping the ringer
@@ -357,7 +383,7 @@
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null),
-                eq(true));
+                eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
         verify(mockVibrator).vibrate(eq(spyVibrationEffectProxy.get(FAKE_RINGTONE_URI, mContext)),
                 any(AudioAttributes.class));
     }
@@ -373,7 +399,7 @@
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null),
-                eq(false));
+                eq(true) /* isRingerAudible */, eq(false) /* isVibrationEnabled */);
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -391,7 +417,7 @@
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer).play(
             any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class),
-                eq(true));
+                eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
     }
 
     @SmallTest
@@ -408,7 +434,7 @@
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -426,7 +452,7 @@
         mRingCompletionFuture.get();
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
-                any(VolumeShaper.Configuration.class), anyBoolean());
+                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 3cec50b..5c131c8 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -102,6 +102,7 @@
 public class TelecomServiceImplTest extends TelecomTestCase {
 
     public static final String TEST_PACKAGE = "com.test";
+    public static final String PACKAGE_NAME = "test";
 
     public static class CallIntentProcessAdapterFake implements CallIntentProcessor.Adapter {
         @Override
@@ -180,15 +181,17 @@
     private static final UserHandle USER_HANDLE_16 = new UserHandle(16);
     private static final UserHandle USER_HANDLE_17 = new UserHandle(17);
     private static final PhoneAccountHandle TEL_PA_HANDLE_16 = new PhoneAccountHandle(
-            new ComponentName("test", "telComponentName"), "0", USER_HANDLE_16);
+            new ComponentName(PACKAGE_NAME, "telComponentName"), "0", USER_HANDLE_16);
     private static final PhoneAccountHandle SIP_PA_HANDLE_17 = new PhoneAccountHandle(
-            new ComponentName("test", "sipComponentName"), "1", USER_HANDLE_17);
+            new ComponentName(PACKAGE_NAME, "sipComponentName"), "1", USER_HANDLE_17);
     private static final PhoneAccountHandle TEL_PA_HANDLE_CURRENT = new PhoneAccountHandle(
-            new ComponentName("test", "telComponentName"), "2", Binder.getCallingUserHandle());
+            new ComponentName(PACKAGE_NAME, "telComponentName"), "2",
+                    Binder.getCallingUserHandle());
     private static final PhoneAccountHandle SIP_PA_HANDLE_CURRENT = new PhoneAccountHandle(
-            new ComponentName("test", "sipComponentName"), "3", Binder.getCallingUserHandle());
-    private static final ComponentName THIRD_PARTY_CALL_SCREENING = new ComponentName("com.android" +
-            ".thirdparty", "com.android.thirdparty.callscreeningserviceimpl");
+            new ComponentName(PACKAGE_NAME, "sipComponentName"), "3",
+                    Binder.getCallingUserHandle());
+    private static final ComponentName THIRD_PARTY_CALL_SCREENING = new ComponentName(
+            "com.android.thirdparty", "com.android.thirdparty.callscreeningserviceimpl");
 
     @Override
     @Before
@@ -201,6 +204,7 @@
         when(mockTelephonyManager.isVoiceCapable()).thenReturn(true);
 
         doReturn(mContext).when(mContext).getApplicationContext();
+        doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
         doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
                 anyString());
         doAnswer(invocation -> {
@@ -237,6 +241,7 @@
                 .thenReturn(true);
 
         mPackageManager = mContext.getPackageManager();
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
     }
 
     @Override
@@ -458,12 +463,18 @@
 
     @SmallTest
     @Test
-    public void testGetPhoneAccount() throws RemoteException {
+    public void testGetPhoneAccount() throws Exception {
         makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
         assertEquals(TEL_PA_HANDLE_16, mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16,
                 mContext.getPackageName()).getAccountHandle());
         assertEquals(SIP_PA_HANDLE_17, mTSIBinder.getPhoneAccount(SIP_PA_HANDLE_17,
                 mContext.getPackageName()).getAccountHandle());
+        try {
+            // Try to call the method without using the caller's package name
+            mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16, null);
+            fail("Should have thrown a SecurityException");
+        } catch (SecurityException expected) {
+        }
     }
 
     @SmallTest