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

Commit b88b3114 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Perform camera permission and app ops check when setting camera for VT.

When a calling InCallService attempts to use the setCamera API on the
VideoCall, Telecom will perform a permission check to ensure that the
caller has the correct camera permission and passes the app-ops camera
check.  A failure to set the camera will result in a callback via the
call session event API.

This got a little messy as the app ops package name needs to come from the
InCallService, and handler usage in the VideoProvider API means we had to
pass around the uid/pid of the caller, obtained before we trampoline onto
the handler.

Test: Unit tests added, manual testing performed.
Bug: 32747443
Change-Id: I555a04f9c3fb45e60bb811f64ba855ccf2e3b0e2
parent baca8f50
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -36511,6 +36511,7 @@ package android.telecom {
    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
    method public void setCallDataUsage(long);
    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
    field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7
    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+1 −0
Original line number Diff line number Diff line
@@ -39420,6 +39420,7 @@ package android.telecom {
    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
    method public void setCallDataUsage(long);
    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
    field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7
    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+1 −0
Original line number Diff line number Diff line
@@ -36600,6 +36600,7 @@ package android.telecom {
    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
    method public void setCallDataUsage(long);
    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
    field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7
    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+8 −3
Original line number Diff line number Diff line
@@ -843,6 +843,7 @@ public final class Call {
    private String mParentId = null;
    private int mState;
    private List<String> mCannedTextResponses = null;
    private String mCallingPackage;
    private String mRemainingPostDialSequence;
    private VideoCallImpl mVideoCallImpl;
    private Details mDetails;
@@ -1330,19 +1331,22 @@ public final class Call {
    }

    /** {@hide} */
    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage) {
        mPhone = phone;
        mTelecomCallId = telecomCallId;
        mInCallAdapter = inCallAdapter;
        mState = STATE_NEW;
        mCallingPackage = callingPackage;
    }

    /** {@hide} */
    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state,
            String callingPackage) {
        mPhone = phone;
        mTelecomCallId = telecomCallId;
        mInCallAdapter = inCallAdapter;
        mState = state;
        mCallingPackage = callingPackage;
    }

    /** {@hide} */
@@ -1352,6 +1356,7 @@ public final class Call {

    /** {@hide} */
    final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {

        // First, we update the internal state as far as possible before firing any updates.
        Details details = Details.createFromParcelableCall(parcelableCall);
        boolean detailsChanged = !Objects.equals(mDetails, details);
@@ -1367,7 +1372,7 @@ public final class Call {
            cannedTextResponsesChanged = true;
        }

        VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl();
        VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage);
        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
                !Objects.equals(mVideoCallImpl, newVideoCallImpl);
        if (videoCallChanged) {
+64 −7
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -785,7 +786,7 @@ public abstract class Connection extends Conferenceable {
        public static final int SESSION_EVENT_TX_STOP = 4;

        /**
         * A camera failure has occurred for the selected camera.  The {@link InCallService} can use
         * A camera failure has occurred for the selected camera.  The {@link VideoProvider} can use
         * this as a cue to inform the user the camera is not available.
         * @see #handleCallSessionEvent(int)
         */
@@ -793,12 +794,20 @@ public abstract class Connection extends Conferenceable {

        /**
         * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
         * for operation.  The {@link InCallService} can use this as a cue to inform the user that
         * for operation.  The {@link VideoProvider} can use this as a cue to inform the user that
         * the camera has become available again.
         * @see #handleCallSessionEvent(int)
         */
        public static final int SESSION_EVENT_CAMERA_READY = 6;

        /**
         * Session event raised by Telecom when
         * {@link android.telecom.InCallService.VideoCall#setCamera(String)} is called and the
         * caller does not have the necessary {@link android.Manifest.permission#CAMERA} permission.
         * @see #handleCallSessionEvent(int)
         */
        public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7;

        /**
         * Session modify request was successful.
         * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
@@ -848,6 +857,8 @@ public abstract class Connection extends Conferenceable {
        private static final String SESSION_EVENT_TX_STOP_STR = "TX_STOP";
        private static final String SESSION_EVENT_CAMERA_FAILURE_STR = "CAMERA_FAIL";
        private static final String SESSION_EVENT_CAMERA_READY_STR = "CAMERA_READY";
        private static final String SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR =
                "CAMERA_PERMISSION_ERROR";
        private static final String SESSION_EVENT_UNKNOWN_STR = "UNKNOWN";

        private VideoProvider.VideoProviderHandler mMessageHandler;
@@ -906,7 +917,16 @@ public abstract class Connection extends Conferenceable {
                        break;
                    }
                    case MSG_SET_CAMERA:
                        onSetCamera((String) msg.obj);
                    {
                        SomeArgs args = (SomeArgs) msg.obj;
                        try {
                            onSetCamera((String) args.arg1);
                            onSetCamera((String) args.arg1, (String) args.arg2, args.argi1,
                                    args.argi2);
                        } finally {
                            args.recycle();
                        }
                    }
                    break;
                    case MSG_SET_PREVIEW_SURFACE:
                        onSetPreviewSurface((Surface) msg.obj);
@@ -962,8 +982,19 @@ public abstract class Connection extends Conferenceable {
                        MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
            }

            public void setCamera(String cameraId) {
                mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
            public void setCamera(String cameraId, String callingPackageName) {
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = cameraId;
                // Propagate the calling package; originally determined in
                // android.telecom.InCallService.VideoCall#setCamera(String) from the calling
                // process.
                args.arg2 = callingPackageName;
                // Pass along the uid and pid of the calling app; this gets lost when we put the
                // message onto the handler.  These are required for Telecom to perform a permission
                // check to see if the calling app is able to use the camera.
                args.argi1 = Binder.getCallingUid();
                args.argi2 = Binder.getCallingPid();
                mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
            }

            public void setPreviewSurface(Surface surface) {
@@ -1047,6 +1078,29 @@ public abstract class Connection extends Conferenceable {
         */
        public abstract void onSetCamera(String cameraId);

        /**
         * Sets the camera to be used for the outgoing video.
         * <p>
         * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
         * camera via
         * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
         * <p>
         * This prototype is used internally to ensure that the calling package name, UID and PID
         * are sent to Telecom so that can perform a camera permission check on the caller.
         * <p>
         * Sent from the {@link InCallService} via
         * {@link InCallService.VideoCall#setCamera(String)}.
         *
         * @param cameraId The id of the camera (use ids as reported by
         * {@link CameraManager#getCameraIdList()}).
         * @param callingPackageName The AppOpps package name of the caller.
         * @param callingUid The UID of the caller.
         * @param callingPid The PID of the caller.
         * @hide
         */
        public void onSetCamera(String cameraId, String callingPackageName, int callingUid,
                int callingPid) {}

        /**
         * Sets the surface to be used for displaying a preview of what the user's camera is
         * currently capturing.  When video transmission is enabled, this is the video signal which
@@ -1233,7 +1287,8 @@ public abstract class Connection extends Conferenceable {
         *      {@link VideoProvider#SESSION_EVENT_TX_START},
         *      {@link VideoProvider#SESSION_EVENT_TX_STOP},
         *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
         *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY}.
         *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY},
         *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}.
         */
        public void handleCallSessionEvent(int event) {
            if (mVideoCallbacks != null) {
@@ -1382,6 +1437,8 @@ public abstract class Connection extends Conferenceable {
                    return SESSION_EVENT_TX_START_STR;
                case SESSION_EVENT_TX_STOP:
                    return SESSION_EVENT_TX_STOP_STR;
                case SESSION_EVENT_CAMERA_PERMISSION_ERROR:
                    return SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR;
                default:
                    return SESSION_EVENT_UNKNOWN_STR + " " + event;
            }
Loading