DO NOT MERGE - Merge Android R QPR1

Bug: 172690556
Merged-In: Ie748edbcc64e5f2eaef5081fd507eceb63ce127a
Change-Id: I1b9ff95da40f5038a83af5c4f3e7529d47ea2b55
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 25fd43e..6b99633 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -177,6 +177,7 @@
         void onConnectionTimeChanged(Call call);
         void onConferenceStateChanged(Call call, boolean isConference);
         void onCdmaConferenceSwap(Call call);
+        void onSetCamera(Call call, String cameraId);
     }
 
     /** Interface used to define the action which is executed delay under some condition. */
@@ -1017,6 +1018,18 @@
         }
     }
 
+    /**
+     * Handles a change to the currently active camera for a call by notifying listeners.
+     * @param call The call.
+     * @param cameraId The ID of the camera in use, or {@code null} if no camera is in use.
+     */
+    @Override
+    public void onSetCamera(Call call, String cameraId) {
+        for (CallsManagerListener listener : mListeners) {
+            listener.onSetCamera(call, cameraId);
+        }
+    }
+
     public Collection<Call> getCalls() {
         return Collections.unmodifiableCollection(mCalls);
     }
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index e0d2831..55c7b53 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -104,4 +104,8 @@
     @Override
     public void onCdmaConferenceSwap(Call call) {
     }
+
+    @Override
+    public void onSetCamera(Call call, String cameraId) {
+    }
 }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 1fa914a..954aa44 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -222,7 +222,7 @@
                 Log.startSession("ICSBC.oSD", Log.getPackageAbbreviation(name));
                 synchronized (mLock) {
                     try {
-                        Log.d(this, "onDisconnected: %s", name);
+                        Log.d(this, "onServiceDisconnected: %s", name);
                         mIsBound = false;
                         onDisconnected();
                     } finally {
@@ -343,6 +343,8 @@
                             mInCallServiceInfo.getDisconnectTime()
                                     - mInCallServiceInfo.getBindingStartTime(), mIsNullBinding);
                 }
+
+                InCallController.this.onDisconnected(mInCallServiceInfo);
             } else {
                 Log.i(InCallController.this, "ICSBC#disconnect: already disconnected; %s",
                         mInCallServiceInfo);
@@ -953,6 +955,17 @@
 
     private final CarModeTracker mCarModeTracker;
 
+    /**
+     * The package name of the app which is showing the calling UX.
+     */
+    private String mCurrentUserInterfacePackageName = null;
+
+    /**
+     * {@code true} if InCallController is tracking a managed, not external call which is using the
+     * microphone, {@code false} otherwise.
+     */
+    private boolean mIsCallUsingMicrophone = false;
+
     public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
             SystemStateHelper systemStateHelper,
             DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
@@ -1046,6 +1059,7 @@
         }
         call.removeListener(mCallListener);
         mCallIdMapper.removeCall(call);
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     @Override
@@ -1121,10 +1135,12 @@
             }
             Log.i(this, "External call removed from components: %s", componentsUpdated);
         }
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     @Override
     public void onCallStateChanged(Call call, int oldState, int newState) {
+        maybeTrackMicrophoneUse(isMuted());
         updateCall(call);
     }
 
@@ -1142,6 +1158,7 @@
         if (!mInCallServices.isEmpty()) {
             Log.i(this, "Calling onAudioStateChanged, audioState: %s -> %s", oldCallAudioState,
                     newCallAudioState);
+            maybeTrackMicrophoneUse(newCallAudioState.isMuted());
             for (IInCallService inCallService : mInCallServices.values()) {
                 try {
                     inCallService.onCallAudioStateChanged(newCallAudioState);
@@ -1206,6 +1223,23 @@
         updateCall(call);
     }
 
+    /**
+     * Track changes to camera usage for a call.
+     * @param call The call.
+     * @param cameraId The id of the camera to use, or {@code null} if camera is off.
+     */
+    @Override
+    public void onSetCamera(Call call, String cameraId) {
+        Log.i(this, "onSetCamera callId=%s, cameraId=%s", call.getId(), cameraId);
+        if (cameraId != null) {
+            mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
+                    mContext.getOpPackageName(), false, null, null);
+        } else {
+            mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
+                    mContext.getOpPackageName(), null);
+        }
+    }
+
     void bringToForeground(boolean showDialpad) {
         if (!mInCallServices.isEmpty()) {
             for (IInCallService inCallService : mInCallServices.values()) {
@@ -1600,6 +1634,11 @@
     private boolean onConnected(InCallServiceInfo info, IBinder service) {
         Log.i(this, "onConnected to %s", info.getComponentName());
 
+        if (info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
+                || info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+                || info.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
+            trackCallingUserInterfaceStarted(info);
+        }
         IInCallService inCallService = IInCallService.Stub.asInterface(service);
         mInCallServices.put(info, inCallService);
 
@@ -1667,7 +1706,11 @@
      */
     private void onDisconnected(InCallServiceInfo disconnectedInfo) {
         Log.i(this, "onDisconnected from %s", disconnectedInfo.getComponentName());
-
+        if (disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
+                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
+            trackCallingUserInterfaceStopped(disconnectedInfo);
+        }
         mInCallServices.remove(disconnectedInfo);
     }
 
@@ -1734,6 +1777,7 @@
             mCallIdMapper.addCall(call);
             call.addListener(mCallListener);
         }
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     /**
@@ -1913,6 +1957,69 @@
         }
     }
 
+    /**
+     * Tracks start of microphone use on binding to the current calling UX.
+     * @param info
+     */
+    private void trackCallingUserInterfaceStarted(InCallServiceInfo info) {
+        String packageName = info.getComponentName().getPackageName();
+        if (!Objects.equals(mCurrentUserInterfacePackageName, packageName)) {
+            Log.i(this, "trackCallingUserInterfaceStarted: %s is now calling UX.", packageName);
+            mCurrentUserInterfacePackageName = packageName;
+        }
+        maybeTrackMicrophoneUse(isMuted());
+    }
+
+    /**
+     * Tracks stop of microphone use on unbind from the current calling UX.
+     * @param info
+     */
+    private void trackCallingUserInterfaceStopped(InCallServiceInfo info) {
+        maybeTrackMicrophoneUse(isMuted());
+        mCurrentUserInterfacePackageName = null;
+        String packageName = info.getComponentName().getPackageName();
+        Log.i(this, "trackCallingUserInterfaceStopped: %s is no longer calling UX", packageName);
+    }
+
+    /**
+     * As calls are added, removed and change between external and non-external status, track
+     * whether the current active calling UX is using the microphone.  We assume if there is a
+     * managed call present and the mic is not muted that the microphone is in use.
+     */
+    private void maybeTrackMicrophoneUse(boolean isMuted) {
+        boolean wasTrackingManagedCall = mIsCallUsingMicrophone;
+        mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted;
+        if (wasTrackingManagedCall != mIsCallUsingMicrophone) {
+            if (mIsCallUsingMicrophone) {
+                mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
+                        mContext.getOpPackageName(), false, null, null);
+            } else {
+                mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
+                        mContext.getOpPackageName(), null);
+            }
+        }
+    }
+
+    /**
+     * @return {@code true} if InCallController is tracking a managed call (i.e. not self managed
+     * and not external) that is active.
+     */
+    private boolean isTrackingManagedAliveCall() {
+        return mCallIdMapper.getCalls().stream().anyMatch(c -> !c.isExternalCall()
+            && !c.isSelfManaged() && c.isAlive() && c.getState() != CallState.ON_HOLD
+                && c.getState() != CallState.AUDIO_PROCESSING);
+    }
+
+    /**
+     * @return {@code true} if the audio is currently muted, {@code false} otherwise.
+     */
+    private boolean isMuted() {
+        if (mCallsManager.getAudioState() == null) {
+            return false;
+        }
+        return mCallsManager.getAudioState().isMuted();
+    }
+
     private boolean isAppOpsPermittedManageOngoingCalls(int uid, String callingPackage) {
         return PermissionChecker.checkPermissionForPreflight(mContext,
                 Manifest.permission.MANAGE_ONGOING_CALLS, PermissionChecker.PID_UNKNOWN, uid,
@@ -1922,6 +2029,10 @@
     private void sendCrashedInCallServiceNotification(String packageName) {
         PackageManager packageManager = mContext.getPackageManager();
         CharSequence appName;
+        String systemDialer = mDefaultDialerCache.getSystemDialerApplication();
+        if ((systemDialer != null) && systemDialer.equals(packageName)) {
+            return;
+        }
         try {
             appName = packageManager.getApplicationLabel(
                     packageManager.getApplicationInfo(packageName, 0));
diff --git a/src/com/android/server/telecom/VideoProviderProxy.java b/src/com/android/server/telecom/VideoProviderProxy.java
index 364e0f4..df11403 100644
--- a/src/com/android/server/telecom/VideoProviderProxy.java
+++ b/src/com/android/server/telecom/VideoProviderProxy.java
@@ -55,6 +55,7 @@
      */
     public interface Listener {
         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
+        void onSetCamera(Call call, String cameraId);
     }
 
     /**
@@ -346,6 +347,12 @@
                     return;
                 }
             }
+
+            // Inform other Telecom components of the change in camera status.
+            for (Listener listener : mListeners) {
+                listener.onSetCamera(mCall, cameraId);
+            }
+
             try {
                 mConectionServiceVideoProvider.setCamera(cameraId, callingPackage,
                         targetSdkVersion);
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 5eecfae..6a6b9f3 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -62,6 +62,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.telecom.CallAudioState;
 import android.telecom.InCallService;
 import android.telecom.ParcelableCall;
 import android.telecom.PhoneAccountHandle;
@@ -175,6 +176,8 @@
         mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, mDefaultDialerCache,
                 mTimeoutsAdapter);
         when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter);
+        when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
+                .thenReturn(mNotificationManager);
         when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn(
                 mMockPermissionInfo);
         mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
@@ -186,8 +189,6 @@
         verify(mMockSystemStateHelper).addListener(systemStateListenerArgumentCaptor.capture());
         mSystemStateListener = systemStateListenerArgumentCaptor.getValue();
 
-        when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
-                .thenReturn(mNotificationManager);
         // Companion Apps don't have CONTROL_INCALL_EXPERIENCE permission.
         doAnswer(invocation -> {
             int uid = invocation.getArgument(0);
@@ -224,6 +225,7 @@
         when(mMockPackageManager.checkPermission(
                 matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
                 matches(APPOP_NONUI_PKG))).thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
     }
 
     @Override
@@ -638,6 +640,7 @@
                 any(Intent.class), any(ServiceConnection.class), anyInt(), any(UserHandle.class)))
                 .thenReturn(true);
         when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo);
+        when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
 
         setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
         mInCallController.bindToServices(mMockCall);
@@ -664,7 +667,7 @@
 
         verify(mNotificationManager).notify(eq(NOTIFICATION_TAG),
                 eq(IN_CALL_SERVICE_NOTIFICATION_ID), any(Notification.class));
-        verify(mCallInfo).addInCallService(eq(sysDialerComponentName.flattenToShortString()),
+        verify(mCallInfo).addInCallService(eq(defDialerComponentName.flattenToShortString()),
                 anyInt(), anyLong(), eq(true));
 
         ArgumentCaptor<Intent> bindIntentCaptor2 = ArgumentCaptor.forClass(Intent.class);
@@ -674,6 +677,7 @@
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
                 eq(UserHandle.CURRENT));
+        assertEquals(sysDialerComponentName, bindIntentCaptor2.getValue().getComponent());
     }
 
     /**