Merge "Clarify InCallController Log message"
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index 9e1b5c7..86d2aaa 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -357,7 +357,11 @@
             if (mCallAudioManager.startRinging()) {
                 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-                mAudioManager.setMode(AudioManager.MODE_RINGTONE);
+                // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode -- this
+                // trips up the audio system.
+                if (mAudioManager.getMode() != NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING) {
+                    mAudioManager.setMode(AudioManager.MODE_RINGTONE);
+                }
                 mCallAudioManager.setCallAudioRouteFocusState(
                         CallAudioRouteStateMachine.RINGING_FOCUS);
             } else {
diff --git a/src/com/android/server/telecom/CallIdMapper.java b/src/com/android/server/telecom/CallIdMapper.java
index da2c199..2cd5c79 100644
--- a/src/com/android/server/telecom/CallIdMapper.java
+++ b/src/com/android/server/telecom/CallIdMapper.java
@@ -20,6 +20,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.Collection;
 import java.util.Map;
 
 /** Utility to map {@link Call} objects to unique IDs. IDs are generated when a call is added. */
@@ -71,6 +72,10 @@
             return mSecondaryMap.get(value);
         }
 
+        public Collection<V> getValues() {
+            return mPrimaryMap.values();
+        }
+
         public void clear() {
             mPrimaryMap.clear();
             mSecondaryMap.clear();
@@ -132,6 +137,10 @@
         return mCalls.getValue(callId);
     }
 
+    Collection<Call> getCalls() {
+        return mCalls.getValues();
+    }
+
     void clear() {
         mCalls.clear();
     }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 02df33a..158c7e1 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -20,9 +20,11 @@
 import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -44,6 +46,7 @@
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 // TODO: Needed for move to system service: import com.android.internal.R;
@@ -59,6 +62,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -323,7 +327,10 @@
                 String packageName = mInCallServiceInfo.getComponentName().getPackageName();
                 mContext.unbindService(mServiceConnection);
                 mIsConnected = false;
-                if (mIsNullBinding) {
+                if (mIsNullBinding && mInCallServiceInfo.getType() != IN_CALL_SERVICE_TYPE_NON_UI) {
+                    // Non-UI InCallServices are allowed to return null from onBind if they don't
+                    // want to handle calls at the moment, so don't report them to the user as
+                    // crashed.
                     sendCrashedInCallServiceNotification(packageName);
                 }
                 if (mCall != null) {
@@ -722,6 +729,20 @@
             }
             pw.decreaseIndent();
         }
+
+        public void addConnections(List<InCallServiceBindingConnection> newConnections) {
+            // connect() needs to be called with a Call object. Since we're in the middle of any
+            // possible number of calls right now, choose an arbitrary one from the ones that
+            // InCallController is tracking.
+            if (mCallIdMapper.getCalls().isEmpty()) {
+                Log.w(InCallController.this, "No calls tracked while adding new NonUi incall");
+                return;
+            }
+            Call callToConnectWith = mCallIdMapper.getCalls().iterator().next();
+            for (InCallServiceBindingConnection newConnection : newConnections) {
+                newConnection.connect(callToConnectWith);
+            }
+        }
     }
 
     private final Call.Listener mCallListener = new Call.ListenerBase() {
@@ -848,6 +869,38 @@
         }
     };
 
+    private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.startSession("ICC.pCR");
+            try {
+                if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
+                    synchronized (mLock) {
+                        String changedPackage = intent.getData().getSchemeSpecificPart();
+                        List<InCallServiceBindingConnection> componentsToBind =
+                                Arrays.stream(intent.getStringArrayExtra(
+                                        Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))
+                                        .map((className) ->
+                                                ComponentName.createRelative(changedPackage,
+                                                        className))
+                                        .filter(mKnownNonUiInCallServices::contains)
+                                        .flatMap(componentName -> getInCallServiceComponents(
+                                                componentName,
+                                                IN_CALL_SERVICE_TYPE_NON_UI).stream())
+                                        .map(InCallServiceBindingConnection::new)
+                                        .collect(Collectors.toList());
+
+                        if (mNonUIInCallServiceConnections != null) {
+                            mNonUIInCallServiceConnections.addConnections(componentsToBind);
+                        }
+                    }
+                }
+            } finally {
+                Log.endSession();
+            }
+        }
+    };
+
     private final SystemStateListener mSystemStateListener =
             (priority, packageName, isCarMode) -> InCallController.this.handleCarModeChange(
                     priority, packageName, isCarMode);
@@ -876,6 +929,11 @@
     private NonUIInCallServiceConnectionCollection mNonUIInCallServiceConnections;
     private final ClockProxy mClockProxy;
 
+    // A set of known non-UI in call services on the device, including those that are disabled.
+    // We track this so that we can efficiently bind to them when we're notified that a new
+    // component has been enabled.
+    private Set<ComponentName> mKnownNonUiInCallServices = new ArraySet<>();
+
     // Future that's in a completed state unless we're in the middle of binding to a service.
     // The future will complete with true if binding succeeds, false if it timed out.
     private CompletableFuture<Boolean> mBindingFuture = CompletableFuture.completedFuture(true);
@@ -1231,6 +1289,7 @@
      * Unbinds an existing bound connection to the in-call app.
      */
     private void unbindFromServices() {
+        mContext.unregisterReceiver(mPackageChangedReceiver);
         if (mInCallServiceConnection != null) {
             mInCallServiceConnection.disconnect();
             mInCallServiceConnection = null;
@@ -1316,6 +1375,10 @@
         }
         mNonUIInCallServiceConnections = new NonUIInCallServiceConnectionCollection(nonUIInCalls);
         mNonUIInCallServiceConnections.connect(call);
+
+        IntentFilter packageChangedFilter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
+        packageChangedFilter.addDataScheme("package");
+        mContext.registerReceiver(mPackageChangedReceiver, packageChangedFilter);
     }
 
     private InCallServiceInfo getDefaultDialerComponent() {
@@ -1389,7 +1452,7 @@
         PackageManager packageManager = mContext.getPackageManager();
         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
                 serviceIntent,
-                PackageManager.GET_META_DATA,
+                PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS,
                 mCallsManager.getCurrentUserHandle().getIdentifier())) {
             ServiceInfo serviceInfo = entry.serviceInfo;
             if (serviceInfo != null) {
@@ -1402,14 +1465,13 @@
 
                 int currentType = getInCallServiceType(entry.serviceInfo, packageManager,
                         packageName);
-                if (requestedType == 0 || requestedType == currentType) {
-                    if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
-                        // We enforce the rule that self-managed calls are not supported by non-ui
-                        // InCallServices.
-                        isSelfManageCallsSupported = false;
-                    }
-                    retval.add(new InCallServiceInfo(
-                            new ComponentName(serviceInfo.packageName, serviceInfo.name),
+                ComponentName foundComponentName =
+                        new ComponentName(serviceInfo.packageName, serviceInfo.name);
+                if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
+                    mKnownNonUiInCallServices.add(foundComponentName);
+                }
+                if (serviceInfo.enabled && (requestedType == 0 || requestedType == currentType)) {
+                    retval.add(new InCallServiceInfo(foundComponentName,
                             isExternalCallsSupported, isSelfManageCallsSupported, requestedType));
                 }
             }
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 5952c25..77a412e 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -842,7 +842,7 @@
                     // current state as tracked by PhoneStateBroadcaster, any failure to properly
                     // track the current call state there could result in the wrong ringing state
                     // being reported by this API.
-                    return mCallsManager.hasRingingCall();
+                    return mCallsManager.hasRingingOrSimulatedRingingCall();
                 }
             } finally {
                 Log.endSession();
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
index 5ab204e..c7e5aa9 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
@@ -137,6 +137,11 @@
 
         resetMocks();
         when(mCallAudioManager.startRinging()).thenReturn(true);
+        if (mParams.initialAudioState
+                == CallAudioModeStateMachine.ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING) {
+            when(mAudioManager.getMode())
+                    .thenReturn(CallAudioModeStateMachine.NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING);
+        }
 
         sm.sendMessage(mParams.messageType, mParams.externalState);
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -815,7 +820,7 @@
                         .build(),
                 CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
                 FOCUS_RING, // expectedFocus
-                AudioManager.MODE_RINGTONE, // expectedMode
+                NO_CHANGE, // expectedMode
                 ON, // expectedRingingInteraction
                 // We expect a call to stopCallWaiting because it happens whenever the ringer starts
                 OFF // expectedCallWaitingInteraction
@@ -836,7 +841,7 @@
                         .build(),
                 CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
                 FOCUS_RING, // expectedFocus
-                AudioManager.MODE_RINGTONE, // expectedMode
+                NO_CHANGE, // expectedMode
                 ON, // expectedRingingInteraction
                 // We expect a call to stopCallWaiting because it happens whenever the ringer starts
                 OFF // expectedCallWaitingInteraction
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index e16b598..c31761f 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -291,7 +291,8 @@
         ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
                 queryIntentCaptor.capture(),
-                eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
+                eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+                eq(CURRENT_USER_ID));
 
         // Verify call for default dialer InCallService
         assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
@@ -350,7 +351,8 @@
         ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
                 queryIntentCaptor.capture(),
-                eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
+                eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+                eq(CURRENT_USER_ID));
 
         // Verify call for default dialer InCallService
         assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
@@ -428,7 +430,8 @@
         ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
                 queryIntentCaptor.capture(),
-                eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
+                eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+                eq(CURRENT_USER_ID));
 
         // Verify call for default dialer InCallService
         assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
@@ -510,7 +513,8 @@
         ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
                 queryIntentCaptor.capture(),
-                eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
+                eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+                eq(CURRENT_USER_ID));
 
         // Verify call for default dialer InCallService
         assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
@@ -634,7 +638,8 @@
         ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
                 queryIntentCaptor.capture(),
-                eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
+                eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+                eq(CURRENT_USER_ID));
 
         // Verify call for default dialer InCallService
         assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
@@ -1038,6 +1043,7 @@
             serviceInfo.applicationInfo = new ApplicationInfo();
             serviceInfo.applicationInfo.uid = DEF_UID;
             serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
+            serviceInfo.enabled = true;
             serviceInfo.metaData = new Bundle();
             serviceInfo.metaData.putBoolean(
                     TelecomManager.METADATA_IN_CALL_SERVICE_UI, true);
@@ -1065,6 +1071,7 @@
                 serviceInfo.applicationInfo.uid = CAR2_UID;
             }
             serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
+            serviceInfo.enabled = true;
             serviceInfo.metaData = new Bundle();
             serviceInfo.metaData.putBoolean(
                     TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI, true);
@@ -1086,6 +1093,7 @@
             serviceInfo.name = SYS_CLASS;
             serviceInfo.applicationInfo = new ApplicationInfo();
             serviceInfo.applicationInfo.uid = SYS_UID;
+            serviceInfo.enabled = true;
             serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
         }};
     }
@@ -1097,6 +1105,7 @@
             serviceInfo.name = COMPANION_CLASS;
             serviceInfo.applicationInfo = new ApplicationInfo();
             serviceInfo.applicationInfo.uid = COMPANION_UID;
+            serviceInfo.enabled = true;
             serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
         }};
     }
@@ -1149,7 +1158,7 @@
                 return resolveInfo;
             }
         }).when(mMockPackageManager).queryIntentServicesAsUser(
-                any(Intent.class), eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
+                any(Intent.class), anyInt(), eq(CURRENT_USER_ID));
     }
 
     private void setupMockPackageManagerLocationPermission(final String pkg,