diff options
author | 2016-11-09 10:19:23 -0800 | |
---|---|---|
committer | 2017-02-06 14:49:03 -0800 | |
commit | bf9c6fdb43413929787ee3abeb76e5aabd1551d8 (patch) | |
tree | f0d48d87d495158bed81ca22c9b43eca58c4e63b | |
parent | ed839943e08c956ddd3fb74941e45bbb59343c8c (diff) |
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
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | api/test-current.txt | 1 | ||||
-rw-r--r-- | telecomm/java/android/telecom/Call.java | 11 | ||||
-rw-r--r-- | telecomm/java/android/telecom/Connection.java | 71 | ||||
-rw-r--r-- | telecomm/java/android/telecom/InCallService.java | 6 | ||||
-rw-r--r-- | telecomm/java/android/telecom/ParcelableCall.java | 4 | ||||
-rw-r--r-- | telecomm/java/android/telecom/Phone.java | 7 | ||||
-rw-r--r-- | telecomm/java/android/telecom/RemoteConnection.java | 11 | ||||
-rw-r--r-- | telecomm/java/android/telecom/RemoteConnectionService.java | 10 | ||||
-rw-r--r-- | telecomm/java/android/telecom/VideoCallImpl.java | 7 | ||||
-rw-r--r-- | telecomm/java/com/android/internal/telecom/IVideoProvider.aidl | 2 |
12 files changed, 107 insertions, 25 deletions
diff --git a/api/current.txt b/api/current.txt index 1bcbb52d75fb..7da2240bff2c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36655,6 +36655,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 diff --git a/api/system-current.txt b/api/system-current.txt index 2bcac92c9bab..4c903da308d6 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -39635,6 +39635,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 diff --git a/api/test-current.txt b/api/test-current.txt index 77a939327d21..ab3c0d9931e0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -36737,6 +36737,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 diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 58c500244d3b..c69b7c2771ac 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -853,6 +853,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; @@ -1340,19 +1341,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} */ @@ -1362,6 +1366,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); @@ -1377,7 +1382,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) { diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 10faa7d455a7..b7391b4c57ac 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -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; @@ -799,7 +800,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) */ @@ -807,13 +808,21 @@ 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) */ @@ -862,6 +871,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; @@ -920,8 +931,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; @@ -976,8 +996,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) { @@ -1062,6 +1093,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 * is sent to the remote device. @@ -1247,7 +1301,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) { @@ -1396,6 +1451,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; } diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index 69de89d5ed74..5d68aaeda988 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -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); diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index f7a6595241e0..a3fce9ca6709 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -190,10 +190,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. } diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java index a4ef5601e551..30ec5b350307 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -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); diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 0e4f53e4881a..77e0e5486127 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -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; } diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index c65d5ef0b114..0c7404aa5e95 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -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 diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java index e54abeebb880..d8ede5c21316 100644 --- a/telecomm/java/android/telecom/VideoCallImpl.java +++ b/telecomm/java/android/telecom/VideoCallImpl.java @@ -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) { } } diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl index 68e5fd48e1ac..a109e90243fe 100644 --- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl +++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl @@ -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); |