Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aed43401 authored by Marvin Ramin's avatar Marvin Ramin
Browse files

Stop MediaProjection on (phone) calls ending

Upon (phone) calls ending MediaProjection is stopped for enhanced user
security. Update the stop controller to handle this case and add test
coverage.

Bug: 377877538
Test: atest MediaProjectionStopControllerTest
Flag: com.android.media.projection.flags.stop_media_projection_on_call_end
Change-Id: I2653ce58b54e8f7530d91be82eb9c1ec0369ee7f
parent 27863594
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -18,3 +18,10 @@ flag {
    bug: "362720120"
    is_exported: true
}

flag {
     namespace: "media_projection"
     name: "stop_media_projection_on_call_end"
     description: "Stops MediaProjection sessions when a call ends"
     bug: "368336349"
}
 No newline at end of file
+2 −1
Original line number Diff line number Diff line
@@ -170,7 +170,6 @@ public final class MediaProjectionManagerService extends SystemService
        mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger(context);
        mMediaProjectionStopController = new MediaProjectionStopController(context,
                this::maybeStopMediaProjection);
        mMediaProjectionStopController.startTrackingStopReasons(context);
        Watchdog.getInstance().addMonitor(this);
    }

@@ -248,6 +247,8 @@ public final class MediaProjectionManagerService extends SystemService
                }
            });
        }

        mMediaProjectionStopController.startTrackingStopReasons(mContext);
    }

    @Override
+43 −0
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.Slog;
import android.view.Display;

@@ -44,17 +47,26 @@ public class MediaProjectionStopController {
    private static final String TAG = "MediaProjectionStopController";
    @VisibleForTesting
    static final int STOP_REASON_KEYGUARD = 1;
    @VisibleForTesting
    static final int STOP_REASON_CALL_END = 2;

    private final TelephonyCallback mTelephonyCallback = new ProjectionTelephonyCallback();
    private final Consumer<Integer> mStopReasonConsumer;
    private final KeyguardManager mKeyguardManager;
    private final TelecomManager mTelecomManager;
    private final TelephonyManager mTelephonyManager;
    private final AppOpsManager mAppOpsManager;
    private final PackageManager mPackageManager;
    private final RoleManager mRoleManager;
    private final ContentResolver mContentResolver;

    private boolean mIsInCall;

    public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
        mStopReasonConsumer = stopReasonConsumer;
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
        mTelecomManager = context.getSystemService(TelecomManager.class);
        mTelephonyManager = context.getSystemService(TelephonyManager.class);
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mPackageManager = context.getPackageManager();
        mRoleManager = context.getSystemService(RoleManager.class);
@@ -69,6 +81,11 @@ public class MediaProjectionStopController {
        try {
            mKeyguardManager.addKeyguardLockedStateListener(context.getMainExecutor(),
                    this::onKeyguardLockedStateChanged);
            if (com.android.media.projection.flags.Flags.stopMediaProjectionOnCallEnd()) {
                callStateChanged();
                mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(),
                        mTelephonyCallback);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
@@ -165,6 +182,21 @@ public class MediaProjectionStopController {
        mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
    }

    @VisibleForTesting
    void callStateChanged() {
        if (!com.android.media.projection.flags.Flags.stopMediaProjectionOnCallEnd()) {
            return;
        }
        boolean isInCall = mTelecomManager.isInCall();
        if (isInCall == mIsInCall) {
            return;
        }
        if (mIsInCall && !isInCall) {
            mStopReasonConsumer.accept(STOP_REASON_CALL_END);
        }
        mIsInCall = isInCall;
    }

    /**
     * @return a String representation of the stop reason interrupting MediaProjection.
     */
@@ -173,7 +205,18 @@ public class MediaProjectionStopController {
            case STOP_REASON_KEYGUARD -> {
                return "STOP_REASON_KEYGUARD";
            }
            case STOP_REASON_CALL_END -> {
                return "STOP_REASON_CALL_END";
            }
        }
        return "";
    }

    private final class ProjectionTelephonyCallback extends TelephonyCallback implements
            TelephonyCallback.CallStateListener {
        @Override
        public void onCallStateChanged(int state) {
            callStateChanged();
        }
    }
}
+65 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -55,6 +56,7 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.testing.TestableContext;
import android.util.ArraySet;

@@ -119,6 +121,8 @@ public class MediaProjectionStopControllerTest {
    private PackageManager mPackageManager;
    @Mock
    private KeyguardManager mKeyguardManager;
    @Mock
    private TelecomManager mTelecomManager;

    private AppOpsManager mAppOpsManager;
    @Mock
@@ -138,6 +142,7 @@ public class MediaProjectionStopControllerTest {
        mAppOpsManager = mockAppOpsManager();
        mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
        mContext.addMockSystemService(KeyguardManager.class, mKeyguardManager);
        mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
        mContext.setMockPackageManager(mPackageManager);

        mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
@@ -303,6 +308,66 @@ public class MediaProjectionStopControllerTest {
        verify(mStopConsumer).accept(MediaProjectionStopController.STOP_REASON_KEYGUARD);
    }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
    public void testCallStateChanged_callStarts() {
        // Setup call state to false
        when(mTelecomManager.isInCall()).thenReturn(false);
        mStopController.callStateChanged();

        clearInvocations(mStopConsumer);

        when(mTelecomManager.isInCall()).thenReturn(true);
        mStopController.callStateChanged();

        verify(mStopConsumer, never()).accept(anyInt());
    }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
    public void testCallStateChanged_remainsInCall() {
        // Setup call state to false
        when(mTelecomManager.isInCall()).thenReturn(true);
        mStopController.callStateChanged();

        clearInvocations(mStopConsumer);

        when(mTelecomManager.isInCall()).thenReturn(true);
        mStopController.callStateChanged();

        verify(mStopConsumer, never()).accept(anyInt());
    }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
    public void testCallStateChanged_remainsNoCall() {
        // Setup call state to false
        when(mTelecomManager.isInCall()).thenReturn(false);
        mStopController.callStateChanged();

        clearInvocations(mStopConsumer);

        when(mTelecomManager.isInCall()).thenReturn(false);
        mStopController.callStateChanged();

        verify(mStopConsumer, never()).accept(anyInt());
    }

    @Test
    @EnableFlags(com.android.media.projection.flags.Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
    public void testCallStateChanged_callEnds() {
        // Setup call state to false
        when(mTelecomManager.isInCall()).thenReturn(true);
        mStopController.callStateChanged();

        clearInvocations(mStopConsumer);

        when(mTelecomManager.isInCall()).thenReturn(false);
        mStopController.callStateChanged();

        verify(mStopConsumer).accept(MediaProjectionStopController.STOP_REASON_CALL_END);
    }

    private MediaProjectionManagerService.MediaProjection createMediaProjection()
            throws NameNotFoundException {
        return createMediaProjection(PACKAGE_NAME);