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 Original line Diff line number Diff line
@@ -18,3 +18,10 @@ flag {
    bug: "362720120"
    bug: "362720120"
    is_exported: true
    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 Original line Diff line number Diff line
@@ -170,7 +170,6 @@ public final class MediaProjectionManagerService extends SystemService
        mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger(context);
        mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger(context);
        mMediaProjectionStopController = new MediaProjectionStopController(context,
        mMediaProjectionStopController = new MediaProjectionStopController(context,
                this::maybeStopMediaProjection);
                this::maybeStopMediaProjection);
        mMediaProjectionStopController.startTrackingStopReasons(context);
        Watchdog.getInstance().addMonitor(this);
        Watchdog.getInstance().addMonitor(this);
    }
    }


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

        mMediaProjectionStopController.startTrackingStopReasons(mContext);
    }
    }


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


@@ -44,17 +47,26 @@ public class MediaProjectionStopController {
    private static final String TAG = "MediaProjectionStopController";
    private static final String TAG = "MediaProjectionStopController";
    @VisibleForTesting
    @VisibleForTesting
    static final int STOP_REASON_KEYGUARD = 1;
    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 Consumer<Integer> mStopReasonConsumer;
    private final KeyguardManager mKeyguardManager;
    private final KeyguardManager mKeyguardManager;
    private final TelecomManager mTelecomManager;
    private final TelephonyManager mTelephonyManager;
    private final AppOpsManager mAppOpsManager;
    private final AppOpsManager mAppOpsManager;
    private final PackageManager mPackageManager;
    private final PackageManager mPackageManager;
    private final RoleManager mRoleManager;
    private final RoleManager mRoleManager;
    private final ContentResolver mContentResolver;
    private final ContentResolver mContentResolver;


    private boolean mIsInCall;

    public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
    public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
        mStopReasonConsumer = stopReasonConsumer;
        mStopReasonConsumer = stopReasonConsumer;
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
        mTelecomManager = context.getSystemService(TelecomManager.class);
        mTelephonyManager = context.getSystemService(TelephonyManager.class);
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mAppOpsManager = context.getSystemService(AppOpsManager.class);
        mPackageManager = context.getPackageManager();
        mPackageManager = context.getPackageManager();
        mRoleManager = context.getSystemService(RoleManager.class);
        mRoleManager = context.getSystemService(RoleManager.class);
@@ -69,6 +81,11 @@ public class MediaProjectionStopController {
        try {
        try {
            mKeyguardManager.addKeyguardLockedStateListener(context.getMainExecutor(),
            mKeyguardManager.addKeyguardLockedStateListener(context.getMainExecutor(),
                    this::onKeyguardLockedStateChanged);
                    this::onKeyguardLockedStateChanged);
            if (com.android.media.projection.flags.Flags.stopMediaProjectionOnCallEnd()) {
                callStateChanged();
                mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(),
                        mTelephonyCallback);
            }
        } finally {
        } finally {
            Binder.restoreCallingIdentity(token);
            Binder.restoreCallingIdentity(token);
        }
        }
@@ -165,6 +182,21 @@ public class MediaProjectionStopController {
        mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
        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.
     * @return a String representation of the stop reason interrupting MediaProjection.
     */
     */
@@ -173,7 +205,18 @@ public class MediaProjectionStopController {
            case STOP_REASON_KEYGUARD -> {
            case STOP_REASON_KEYGUARD -> {
                return "STOP_REASON_KEYGUARD";
                return "STOP_REASON_KEYGUARD";
            }
            }
            case STOP_REASON_CALL_END -> {
                return "STOP_REASON_CALL_END";
            }
        }
        }
        return "";
        return "";
    }
    }

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


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


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


        mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
        mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
@@ -303,6 +308,66 @@ public class MediaProjectionStopControllerTest {
        verify(mStopConsumer).accept(MediaProjectionStopController.STOP_REASON_KEYGUARD);
        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()
    private MediaProjectionManagerService.MediaProjection createMediaProjection()
            throws NameNotFoundException {
            throws NameNotFoundException {
        return createMediaProjection(PACKAGE_NAME);
        return createMediaProjection(PACKAGE_NAME);