Ensure TeleComm tracks carrier privileged apps using mic

Test: atest InCallControllerTests
Bug: 191800570
Change-Id: I9c909795d7873de1ad1f5c14b875b8ba5d1dd700
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 7403170..fb7deb7 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom;
 
+import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
 import static android.os.Process.myUid;
 
 import android.Manifest;
@@ -79,7 +80,8 @@
  * can send updates to the in-call app. This class is created and owned by CallsManager and retains
  * a binding to the {@link IInCallService} (implemented by the in-call app).
  */
-public class InCallController extends CallsManagerListenerBase {
+public class InCallController extends CallsManagerListenerBase implements
+        AppOpsManager.OnOpActiveChangedListener {
     public static final int IN_CALL_SERVICE_NOTIFICATION_ID = 3;
     public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName();
 
@@ -1004,6 +1006,9 @@
      */
     private ArrayList<String> mCallsUsingCamera = new ArrayList<>();
 
+    private ArraySet<String> mAllCarrierPrivilegedApps = new ArraySet<>();
+    private ArraySet<String> mActiveCarrierPrivilegedApps = new ArraySet<>();
+
     public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
             SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache,
             Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper,
@@ -1033,6 +1038,65 @@
     }
 
     @Override
+    public void onOpActiveChanged(@androidx.annotation.NonNull String op, int uid,
+            @androidx.annotation.NonNull String packageName, boolean active) {
+        synchronized (mLock) {
+            if (!mAllCarrierPrivilegedApps.contains(packageName)) {
+                return;
+            }
+
+            if (active) {
+                mActiveCarrierPrivilegedApps.add(packageName);
+            } else {
+                mActiveCarrierPrivilegedApps.remove(packageName);
+            }
+            maybeTrackMicrophoneUse(isMuted());
+        }
+    }
+
+    private void updateAllCarrierPrivilegedUsingMic() {
+        mActiveCarrierPrivilegedApps.clear();
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        PackageManager pkgManager = mContext.getPackageManager();
+        for (String pkg : mAllCarrierPrivilegedApps) {
+            boolean isActive = mActiveCarrierPrivilegedApps.contains(pkg);
+            List<UserHandle> users = userManager.getUserHandles(true);
+            for (UserHandle user : users) {
+                if (isActive) {
+                    break;
+                }
+
+                int uid;
+                try {
+                    uid = pkgManager.getPackageUidAsUser(pkg, user.getIdentifier());
+                } catch (PackageManager.NameNotFoundException e) {
+                    continue;
+                }
+                List<AppOpsManager.PackageOps> pkgOps = mAppOpsManager.getOpsForPackage(
+                        uid, pkg, OPSTR_RECORD_AUDIO);
+                for (int j = 0; j < pkgOps.size(); j++) {
+                    List<AppOpsManager.OpEntry> opEntries = pkgOps.get(j).getOps();
+                    for (int k = 0; k < opEntries.size(); k++) {
+                        AppOpsManager.OpEntry entry = opEntries.get(k);
+                        if (entry.isRunning()) {
+                            mActiveCarrierPrivilegedApps.add(pkg);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void updateAllCarrierPrivileged() {
+        mAllCarrierPrivilegedApps.clear();
+        for (Call call : mCallIdMapper.getCalls()) {
+            mAllCarrierPrivilegedApps.add(call.getConnectionManagerPhoneAccount()
+                    .getComponentName().getPackageName());
+        }
+    }
+
+    @Override
     public void onCallAdded(Call call) {
         if (!isBoundAndConnectedToServices()) {
             Log.i(this, "onCallAdded: %s; not bound or connected.", call);
@@ -1112,6 +1176,10 @@
         }
         call.removeListener(mCallListener);
         mCallIdMapper.removeCall(call);
+        if (mCallIdMapper.getCalls().isEmpty()) {
+            mActiveCarrierPrivilegedApps.clear();
+            mAppOpsManager.stopWatchingActive(this);
+        }
         maybeTrackMicrophoneUse(isMuted());
         onSetCamera(call, null);
     }
@@ -1265,6 +1333,7 @@
     public void onIsVoipAudioModeChanged(Call call) {
         Log.d(this, "onIsVoipAudioModeChanged %s", call);
         updateCall(call);
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     @Override
@@ -1894,10 +1963,18 @@
      * @param call The call to add.
      */
     private void addCall(Call call) {
+        if (mCallIdMapper.getCalls().size() == 0) {
+            mAppOpsManager.startWatchingActive(new String[] { OPSTR_RECORD_AUDIO },
+                    java.lang.Runnable::run, this);
+            updateAllCarrierPrivileged();
+            updateAllCarrierPrivilegedUsingMic();
+        }
+
         if (mCallIdMapper.getCallId(call) == null) {
             mCallIdMapper.addCall(call);
             call.addListener(mCallListener);
         }
+
         maybeTrackMicrophoneUse(isMuted());
     }
 
@@ -2136,7 +2213,8 @@
      */
     private void maybeTrackMicrophoneUse(boolean isMuted) {
         boolean wasTrackingManagedCall = mIsCallUsingMicrophone;
-        mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted;
+        mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted
+                && !carrierPrivilegedUsingMicDuringVoipCall();
         if (wasTrackingManagedCall != mIsCallUsingMicrophone) {
             if (mIsCallUsingMicrophone) {
                 mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
@@ -2158,6 +2236,11 @@
                 && c.getState() != CallState.AUDIO_PROCESSING && c.getState() != CallState.DIALING);
     }
 
+    private boolean carrierPrivilegedUsingMicDuringVoipCall() {
+        return !mActiveCarrierPrivilegedApps.isEmpty() &&
+                mCallIdMapper.getCalls().stream().anyMatch(Call::getIsVoipAudioMode);
+    }
+
     /**
      * @return {@code true} if the audio is currently muted, {@code false} otherwise.
      */
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 8662e65..6cc19ad 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -514,7 +514,7 @@
         // Pretend that the call has gone away.
         when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList());
         mInCallController.onCallRemoved(mMockCall);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TelecomSystemTest.TEST_TIMEOUT);
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
 
         verify(mMockPackageManager).revokeRuntimePermission(eq(SYS_PKG),
                 eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle));