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));