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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,8 +917,17 @@ public abstract class Connection extends Conferenceable {
|
||||
break;
|
||||
}
|
||||
case MSG_SET_CAMERA:
|
||||
onSetCamera((String) msg.obj);
|
||||
break;
|
||||
{
|
||||
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);
|
||||
break;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ public abstract class InCallService extends Service {
|
||||
|
||||
switch (msg.what) {
|
||||
case MSG_SET_IN_CALL_ADAPTER:
|
||||
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
|
||||
String callingPackage = getApplicationContext().getOpPackageName();
|
||||
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage);
|
||||
mPhone.addListener(mPhoneListener);
|
||||
onPhoneCreated(mPhone);
|
||||
break;
|
||||
@@ -664,7 +665,8 @@ public abstract class InCallService extends Service {
|
||||
* {@link Connection.VideoProvider#SESSION_EVENT_TX_START},
|
||||
* {@link Connection.VideoProvider#SESSION_EVENT_TX_STOP},
|
||||
* {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
|
||||
* {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}.
|
||||
* {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY},
|
||||
* {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_PERMISSION_ERROR}.
|
||||
*/
|
||||
public abstract void onCallSessionEvent(int event);
|
||||
|
||||
|
||||
@@ -182,10 +182,10 @@ public final class ParcelableCall implements Parcelable {
|
||||
|
||||
* @return The video call.
|
||||
*/
|
||||
public VideoCallImpl getVideoCallImpl() {
|
||||
public VideoCallImpl getVideoCallImpl(String callingPackageName) {
|
||||
if (mVideoCall == null && mVideoCallProvider != null) {
|
||||
try {
|
||||
mVideoCall = new VideoCallImpl(mVideoCallProvider);
|
||||
mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName);
|
||||
} catch (RemoteException ignored) {
|
||||
// Ignore RemoteException.
|
||||
}
|
||||
|
||||
@@ -125,13 +125,16 @@ public final class Phone {
|
||||
|
||||
private boolean mCanAddCall = true;
|
||||
|
||||
Phone(InCallAdapter adapter) {
|
||||
private final String mCallingPackage;
|
||||
|
||||
Phone(InCallAdapter adapter, String callingPackage) {
|
||||
mInCallAdapter = adapter;
|
||||
mCallingPackage = callingPackage;
|
||||
}
|
||||
|
||||
final void internalAddCall(ParcelableCall parcelableCall) {
|
||||
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
|
||||
parcelableCall.getState());
|
||||
parcelableCall.getState(), mCallingPackage);
|
||||
mCallByTelecomCallId.put(parcelableCall.getId(), call);
|
||||
mCalls.add(call);
|
||||
checkCallTree(parcelableCall);
|
||||
|
||||
@@ -408,6 +408,8 @@ public final class RemoteConnection {
|
||||
|
||||
private final IVideoProvider mVideoProviderBinder;
|
||||
|
||||
private final String mCallingPackage;
|
||||
|
||||
/**
|
||||
* ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
|
||||
* load factor before resizing, 1 means we only expect a single thread to
|
||||
@@ -416,8 +418,9 @@ public final class RemoteConnection {
|
||||
private final Set<Callback> mCallbacks = Collections.newSetFromMap(
|
||||
new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
|
||||
|
||||
VideoProvider(IVideoProvider videoProviderBinder) {
|
||||
VideoProvider(IVideoProvider videoProviderBinder, String callingPackage) {
|
||||
mVideoProviderBinder = videoProviderBinder;
|
||||
mCallingPackage = callingPackage;
|
||||
try {
|
||||
mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
|
||||
} catch (RemoteException e) {
|
||||
@@ -452,7 +455,7 @@ public final class RemoteConnection {
|
||||
*/
|
||||
public void setCamera(String cameraId) {
|
||||
try {
|
||||
mVideoProviderBinder.setCamera(cameraId);
|
||||
mVideoProviderBinder.setCamera(cameraId, mCallingPackage);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
@@ -628,7 +631,7 @@ public final class RemoteConnection {
|
||||
* @hide
|
||||
*/
|
||||
RemoteConnection(String callId, IConnectionService connectionService,
|
||||
ParcelableConnection connection) {
|
||||
ParcelableConnection connection, String callingPackage) {
|
||||
mConnectionId = callId;
|
||||
mConnectionService = connectionService;
|
||||
mConnected = true;
|
||||
@@ -640,7 +643,7 @@ public final class RemoteConnection {
|
||||
mVideoState = connection.getVideoState();
|
||||
IVideoProvider videoProvider = connection.getVideoProvider();
|
||||
if (videoProvider != null) {
|
||||
mVideoProvider = new RemoteConnection.VideoProvider(videoProvider);
|
||||
mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage);
|
||||
} else {
|
||||
mVideoProvider = null;
|
||||
}
|
||||
|
||||
@@ -283,9 +283,13 @@ final class RemoteConnectionService {
|
||||
@Override
|
||||
public void setVideoProvider(String callId, IVideoProvider videoProvider,
|
||||
Session.Info sessionInfo) {
|
||||
|
||||
String callingPackage = mOurConnectionServiceImpl.getApplicationContext()
|
||||
.getOpPackageName();
|
||||
RemoteConnection.VideoProvider remoteVideoProvider = null;
|
||||
if (videoProvider != null) {
|
||||
remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider);
|
||||
remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider,
|
||||
callingPackage);
|
||||
}
|
||||
findConnectionForAction(callId, "setVideoProvider")
|
||||
.setVideoProvider(remoteVideoProvider);
|
||||
@@ -351,8 +355,10 @@ final class RemoteConnectionService {
|
||||
@Override
|
||||
public void addExistingConnection(String callId, ParcelableConnection connection,
|
||||
Session.Info sessionInfo) {
|
||||
String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
|
||||
getOpPackageName();
|
||||
RemoteConnection remoteConnection = new RemoteConnection(callId,
|
||||
mOutgoingConnectionServiceRpc, connection);
|
||||
mOutgoingConnectionServiceRpc, connection, callingPackage);
|
||||
mConnectionById.put(callId, remoteConnection);
|
||||
remoteConnection.registerCallback(new RemoteConnection.Callback() {
|
||||
@Override
|
||||
|
||||
@@ -43,6 +43,7 @@ public class VideoCallImpl extends VideoCall {
|
||||
private VideoCall.Callback mCallback;
|
||||
private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN;
|
||||
private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
|
||||
private final String mCallingPackageName;
|
||||
|
||||
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
|
||||
@Override
|
||||
@@ -197,12 +198,13 @@ public class VideoCallImpl extends VideoCall {
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
VideoCallImpl(IVideoProvider videoProvider) throws RemoteException {
|
||||
VideoCallImpl(IVideoProvider videoProvider, String callingPackageName) throws RemoteException {
|
||||
mVideoProvider = videoProvider;
|
||||
mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
|
||||
|
||||
mBinder = new VideoCallListenerBinder();
|
||||
mVideoProvider.addVideoCallback(mBinder);
|
||||
mCallingPackageName = callingPackageName;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
@@ -240,7 +242,8 @@ public class VideoCallImpl extends VideoCall {
|
||||
/** {@inheritDoc} */
|
||||
public void setCamera(String cameraId) {
|
||||
try {
|
||||
mVideoProvider.setCamera(cameraId);
|
||||
Log.w(this, "setCamera: cameraId=%s, calling=%s", cameraId, mCallingPackageName);
|
||||
mVideoProvider.setCamera(cameraId, mCallingPackageName);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ oneway interface IVideoProvider {
|
||||
|
||||
void removeVideoCallback(IBinder videoCallbackBinder);
|
||||
|
||||
void setCamera(String cameraId);
|
||||
void setCamera(String cameraId, in String mCallingPackageName);
|
||||
|
||||
void setPreviewSurface(in Surface surface);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user