Loading src/com/android/server/telecom/InCallController.java +85 −2 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.telecom; package com.android.server.telecom; import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.os.Process.myUid; import static android.os.Process.myUid; import android.Manifest; import android.Manifest; Loading Loading @@ -80,7 +81,8 @@ import java.util.stream.Collectors; * can send updates to the in-call app. This class is created and owned by CallsManager and retains * 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). * 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 int IN_CALL_SERVICE_NOTIFICATION_ID = 3; public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName(); public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName(); Loading Loading @@ -1008,6 +1010,9 @@ public class InCallController extends CallsManagerListenerBase { */ */ private ArrayList<String> mCallsUsingCamera = new ArrayList<>(); 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, public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper, Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper, Loading Loading @@ -1036,6 +1041,65 @@ public class InCallController extends CallsManagerListenerBase { mToken, packageRestriction, UserHandle.USER_ALL); mToken, packageRestriction, UserHandle.USER_ALL); } } @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 @Override public void onCallAdded(Call call) { public void onCallAdded(Call call) { if (!isBoundAndConnectedToServices()) { if (!isBoundAndConnectedToServices()) { Loading Loading @@ -1116,6 +1180,10 @@ public class InCallController extends CallsManagerListenerBase { } } call.removeListener(mCallListener); call.removeListener(mCallListener); mCallIdMapper.removeCall(call); mCallIdMapper.removeCall(call); if (mCallIdMapper.getCalls().isEmpty()) { mActiveCarrierPrivilegedApps.clear(); mAppOpsManager.stopWatchingActive(this); } maybeTrackMicrophoneUse(isMuted()); maybeTrackMicrophoneUse(isMuted()); onSetCamera(call, null); onSetCamera(call, null); } } Loading Loading @@ -1269,6 +1337,7 @@ public class InCallController extends CallsManagerListenerBase { public void onIsVoipAudioModeChanged(Call call) { public void onIsVoipAudioModeChanged(Call call) { Log.d(this, "onIsVoipAudioModeChanged %s", call); Log.d(this, "onIsVoipAudioModeChanged %s", call); updateCall(call); updateCall(call); maybeTrackMicrophoneUse(isMuted()); } } @Override @Override Loading Loading @@ -1898,10 +1967,18 @@ public class InCallController extends CallsManagerListenerBase { * @param call The call to add. * @param call The call to add. */ */ private void addCall(Call call) { 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) { if (mCallIdMapper.getCallId(call) == null) { mCallIdMapper.addCall(call); mCallIdMapper.addCall(call); call.addListener(mCallListener); call.addListener(mCallListener); } } maybeTrackMicrophoneUse(isMuted()); maybeTrackMicrophoneUse(isMuted()); } } Loading Loading @@ -2140,7 +2217,8 @@ public class InCallController extends CallsManagerListenerBase { */ */ private void maybeTrackMicrophoneUse(boolean isMuted) { private void maybeTrackMicrophoneUse(boolean isMuted) { boolean wasTrackingManagedCall = mIsCallUsingMicrophone; boolean wasTrackingManagedCall = mIsCallUsingMicrophone; mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted; mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted && !carrierPrivilegedUsingMicDuringVoipCall(); if (wasTrackingManagedCall != mIsCallUsingMicrophone) { if (wasTrackingManagedCall != mIsCallUsingMicrophone) { if (mIsCallUsingMicrophone) { if (mIsCallUsingMicrophone) { mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(), mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(), Loading @@ -2162,6 +2240,11 @@ public class InCallController extends CallsManagerListenerBase { c.getState())); c.getState())); } } private boolean carrierPrivilegedUsingMicDuringVoipCall() { return !mActiveCarrierPrivilegedApps.isEmpty() && mCallIdMapper.getCalls().stream().anyMatch(Call::getIsVoipAudioMode); } /** /** * @return {@code true} if the audio is currently muted, {@code false} otherwise. * @return {@code true} if the audio is currently muted, {@code false} otherwise. */ */ Loading tests/src/com/android/server/telecom/tests/InCallControllerTests.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -514,7 +514,7 @@ public class InCallControllerTests extends TelecomTestCase { // Pretend that the call has gone away. // Pretend that the call has gone away. when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); mInCallController.onCallRemoved(mMockCall); mInCallController.onCallRemoved(mMockCall); waitForHandlerAction(new Handler(Looper.getMainLooper()), TelecomSystemTest.TEST_TIMEOUT); waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); verify(mMockPackageManager).revokeRuntimePermission(eq(SYS_PKG), verify(mMockPackageManager).revokeRuntimePermission(eq(SYS_PKG), eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle)); eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle)); Loading Loading
src/com/android/server/telecom/InCallController.java +85 −2 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.telecom; package com.android.server.telecom; import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.os.Process.myUid; import static android.os.Process.myUid; import android.Manifest; import android.Manifest; Loading Loading @@ -80,7 +81,8 @@ import java.util.stream.Collectors; * can send updates to the in-call app. This class is created and owned by CallsManager and retains * 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). * 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 int IN_CALL_SERVICE_NOTIFICATION_ID = 3; public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName(); public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName(); Loading Loading @@ -1008,6 +1010,9 @@ public class InCallController extends CallsManagerListenerBase { */ */ private ArrayList<String> mCallsUsingCamera = new ArrayList<>(); 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, public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper, Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper, Loading Loading @@ -1036,6 +1041,65 @@ public class InCallController extends CallsManagerListenerBase { mToken, packageRestriction, UserHandle.USER_ALL); mToken, packageRestriction, UserHandle.USER_ALL); } } @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 @Override public void onCallAdded(Call call) { public void onCallAdded(Call call) { if (!isBoundAndConnectedToServices()) { if (!isBoundAndConnectedToServices()) { Loading Loading @@ -1116,6 +1180,10 @@ public class InCallController extends CallsManagerListenerBase { } } call.removeListener(mCallListener); call.removeListener(mCallListener); mCallIdMapper.removeCall(call); mCallIdMapper.removeCall(call); if (mCallIdMapper.getCalls().isEmpty()) { mActiveCarrierPrivilegedApps.clear(); mAppOpsManager.stopWatchingActive(this); } maybeTrackMicrophoneUse(isMuted()); maybeTrackMicrophoneUse(isMuted()); onSetCamera(call, null); onSetCamera(call, null); } } Loading Loading @@ -1269,6 +1337,7 @@ public class InCallController extends CallsManagerListenerBase { public void onIsVoipAudioModeChanged(Call call) { public void onIsVoipAudioModeChanged(Call call) { Log.d(this, "onIsVoipAudioModeChanged %s", call); Log.d(this, "onIsVoipAudioModeChanged %s", call); updateCall(call); updateCall(call); maybeTrackMicrophoneUse(isMuted()); } } @Override @Override Loading Loading @@ -1898,10 +1967,18 @@ public class InCallController extends CallsManagerListenerBase { * @param call The call to add. * @param call The call to add. */ */ private void addCall(Call call) { 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) { if (mCallIdMapper.getCallId(call) == null) { mCallIdMapper.addCall(call); mCallIdMapper.addCall(call); call.addListener(mCallListener); call.addListener(mCallListener); } } maybeTrackMicrophoneUse(isMuted()); maybeTrackMicrophoneUse(isMuted()); } } Loading Loading @@ -2140,7 +2217,8 @@ public class InCallController extends CallsManagerListenerBase { */ */ private void maybeTrackMicrophoneUse(boolean isMuted) { private void maybeTrackMicrophoneUse(boolean isMuted) { boolean wasTrackingManagedCall = mIsCallUsingMicrophone; boolean wasTrackingManagedCall = mIsCallUsingMicrophone; mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted; mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted && !carrierPrivilegedUsingMicDuringVoipCall(); if (wasTrackingManagedCall != mIsCallUsingMicrophone) { if (wasTrackingManagedCall != mIsCallUsingMicrophone) { if (mIsCallUsingMicrophone) { if (mIsCallUsingMicrophone) { mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(), mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(), Loading @@ -2162,6 +2240,11 @@ public class InCallController extends CallsManagerListenerBase { c.getState())); c.getState())); } } private boolean carrierPrivilegedUsingMicDuringVoipCall() { return !mActiveCarrierPrivilegedApps.isEmpty() && mCallIdMapper.getCalls().stream().anyMatch(Call::getIsVoipAudioMode); } /** /** * @return {@code true} if the audio is currently muted, {@code false} otherwise. * @return {@code true} if the audio is currently muted, {@code false} otherwise. */ */ Loading
tests/src/com/android/server/telecom/tests/InCallControllerTests.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -514,7 +514,7 @@ public class InCallControllerTests extends TelecomTestCase { // Pretend that the call has gone away. // Pretend that the call has gone away. when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); mInCallController.onCallRemoved(mMockCall); mInCallController.onCallRemoved(mMockCall); waitForHandlerAction(new Handler(Looper.getMainLooper()), TelecomSystemTest.TEST_TIMEOUT); waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); verify(mMockPackageManager).revokeRuntimePermission(eq(SYS_PKG), verify(mMockPackageManager).revokeRuntimePermission(eq(SYS_PKG), eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle)); eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle)); Loading