Loading src/com/android/server/telecom/Call.java +2 −1 Original line number Diff line number Diff line Loading @@ -2008,7 +2008,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable { if (videoProvider != null ) { try { mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this); mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this, mCallsManager); } catch (RemoteException ignored) { // Ignore RemoteException. } Loading src/com/android/server/telecom/CallsManager.java +4 −2 Original line number Diff line number Diff line Loading @@ -96,7 +96,7 @@ import java.util.stream.Stream; */ @VisibleForTesting public class CallsManager extends Call.ListenerBase implements VideoProviderProxy.Listener, CallFilterResultCallback { implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy { // TODO: Consider renaming this CallsManagerPlugin. @VisibleForTesting Loading Loading @@ -650,6 +650,7 @@ public class CallsManager extends Call.ListenerBase return mCallAudioManager.getForegroundCall(); } @Override public UserHandle getCurrentUserHandle() { return mCurrentUserHandle; } Loading Loading @@ -2274,7 +2275,8 @@ public class CallsManager extends Call.ListenerBase * Callback when foreground user is switched. We will reload missed call in all profiles * including the user itself. There may be chances that profiles are not started yet. */ void onUserSwitch(UserHandle userHandle) { @VisibleForTesting public void onUserSwitch(UserHandle userHandle) { mCurrentUserHandle = userHandle; mMissedCallNotifier.setCurrentUserHandle(userHandle); final UserManager userManager = UserManager.get(mContext); Loading src/com/android/server/telecom/CurrentUserProxy.java 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.telecom; import android.os.UserHandle; /** * Defines common functionality for a class which has knowledge of the currently logged in user. * Implemented by {@link CallsManager} and used by {@link VideoProviderProxy} so that it does not * have a dependency on the entire CallsManager when all that is required is the ability to find out * the handle of the currently logged in user. */ public interface CurrentUserProxy { UserHandle getCurrentUserHandle(); } src/com/android/server/telecom/VideoProviderProxy.java +23 −4 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.telecom.Connection; import android.telecom.InCallService; import android.telecom.Log; Loading Loading @@ -88,6 +89,11 @@ public class VideoProviderProxy extends Connection.VideoProvider { */ private Call mCall; /** * Interface providing access to the currently logged in user. */ private CurrentUserProxy mCurrentUserProxy; private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { Loading @@ -106,7 +112,8 @@ public class VideoProviderProxy extends Connection.VideoProvider { * @throws RemoteException Remote exception. */ VideoProviderProxy(TelecomSystem.SyncRoot lock, IVideoProvider videoProvider, Call call) throws RemoteException { IVideoProvider videoProvider, Call call, CurrentUserProxy currentUserProxy) throws RemoteException { super(Looper.getMainLooper()); Loading @@ -118,6 +125,7 @@ public class VideoProviderProxy extends Connection.VideoProvider { mVideoCallListenerBinder = new VideoCallListenerBinder(); mConectionServiceVideoProvider.addVideoCallback(mVideoCallListenerBinder); mCall = call; mCurrentUserProxy = currentUserProxy; } /** Loading Loading @@ -300,13 +308,15 @@ public class VideoProviderProxy extends Connection.VideoProvider { public void onSetCamera(String cameraId, String callingPackage, int callingUid, int callingPid) { synchronized (mLock) { logFromInCall("setCamera: " + cameraId + " callingPackage=" + callingPackage); logFromInCall("setCamera: " + cameraId + " callingPackage=" + callingPackage + "; callingUid=" + callingUid); if (!TextUtils.isEmpty(cameraId)) { if (!canUseCamera(mCall.getContext(), callingPackage, callingUid, callingPid)) { // Calling app is not permitted to use the camera. Ignore the request and send // back a call session event indicating the error. Log.i(this, "onSetCamera: camera permission denied; package=%d, uid=%d, pid=%d", Log.i(this, "onSetCamera: camera permission denied; package=%d, uid=%d, " + "pid=%d", callingPackage, callingUid, callingPid); VideoProviderProxy.this.handleCallSessionEvent( Connection.VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR); Loading Loading @@ -528,11 +538,20 @@ public class VideoProviderProxy extends Connection.VideoProvider { * @param context The context. * @param callingPackage The package name of the caller (i.e. Dialer). * @param callingUid The UID of the caller. * @param callingPid The PID of the caller. * @return {@code true} if the calling uid and package can use the camera, {@code false} * otherwise. */ private boolean canUseCamera(Context context, String callingPackage, int callingUid, int callingPid) { UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); UserHandle currentUserHandle = mCurrentUserProxy.getCurrentUserHandle(); if (currentUserHandle != null && !currentUserHandle.equals(callingUser)) { Log.w(this, "canUseCamera attempt to user camera by background user."); return false; } try { context.enforcePermission(Manifest.permission.CAMERA, callingPid, callingUid, "Camera permission required."); Loading @@ -548,7 +567,7 @@ public class VideoProviderProxy extends Connection.VideoProvider { return appOpsManager != null && appOpsManager.noteOp(AppOpsManager.OP_CAMERA, callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED; } catch (SecurityException se) { Log.w(this, "canUserCamera got appOpps Exception " + se.toString()); Log.w(this, "canUseCamera got appOpps Exception " + se.toString()); return false; } } Loading tests/src/com/android/server/telecom/tests/VideoProviderTest.java +26 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.graphics.SurfaceTexture; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.telecom.Connection.VideoProvider; import android.telecom.InCallService; import android.telecom.InCallService.VideoCall; Loading Loading @@ -210,6 +211,31 @@ public class VideoProviderTest extends TelecomSystemTest { sessionEventCaptor.getValue().intValue()); } /** * Tests the caller user handle check in {@link VideoCall#setCamera(String)} to ensure a camera * change from a background user is not permitted. */ @MediumTest public void testCameraChangeUserFail() throws Exception { // Wait until the callback has been received before performing verification. doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt()); // Set a fake user to be the current foreground user. mTelecomSystem.getCallsManager().onUserSwitch(new UserHandle(1000)); // Make a request to change the camera mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT); mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS); // Capture the session event reported via the callback. ArgumentCaptor<Integer> sessionEventCaptor = ArgumentCaptor.forClass(Integer.class); verify(mVideoCallCallback, timeout(TEST_TIMEOUT)).onCallSessionEvent( sessionEventCaptor.capture()); assertEquals(VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR, sessionEventCaptor.getValue().intValue()); } /** * Tests the caller permission check in {@link VideoCall#setCamera(String)} to ensure the * caller can null out the camera, even if they do not have camera permission. Loading Loading
src/com/android/server/telecom/Call.java +2 −1 Original line number Diff line number Diff line Loading @@ -2008,7 +2008,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable { if (videoProvider != null ) { try { mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this); mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this, mCallsManager); } catch (RemoteException ignored) { // Ignore RemoteException. } Loading
src/com/android/server/telecom/CallsManager.java +4 −2 Original line number Diff line number Diff line Loading @@ -96,7 +96,7 @@ import java.util.stream.Stream; */ @VisibleForTesting public class CallsManager extends Call.ListenerBase implements VideoProviderProxy.Listener, CallFilterResultCallback { implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy { // TODO: Consider renaming this CallsManagerPlugin. @VisibleForTesting Loading Loading @@ -650,6 +650,7 @@ public class CallsManager extends Call.ListenerBase return mCallAudioManager.getForegroundCall(); } @Override public UserHandle getCurrentUserHandle() { return mCurrentUserHandle; } Loading Loading @@ -2274,7 +2275,8 @@ public class CallsManager extends Call.ListenerBase * Callback when foreground user is switched. We will reload missed call in all profiles * including the user itself. There may be chances that profiles are not started yet. */ void onUserSwitch(UserHandle userHandle) { @VisibleForTesting public void onUserSwitch(UserHandle userHandle) { mCurrentUserHandle = userHandle; mMissedCallNotifier.setCurrentUserHandle(userHandle); final UserManager userManager = UserManager.get(mContext); Loading
src/com/android/server/telecom/CurrentUserProxy.java 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.telecom; import android.os.UserHandle; /** * Defines common functionality for a class which has knowledge of the currently logged in user. * Implemented by {@link CallsManager} and used by {@link VideoProviderProxy} so that it does not * have a dependency on the entire CallsManager when all that is required is the ability to find out * the handle of the currently logged in user. */ public interface CurrentUserProxy { UserHandle getCurrentUserHandle(); }
src/com/android/server/telecom/VideoProviderProxy.java +23 −4 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.telecom.Connection; import android.telecom.InCallService; import android.telecom.Log; Loading Loading @@ -88,6 +89,11 @@ public class VideoProviderProxy extends Connection.VideoProvider { */ private Call mCall; /** * Interface providing access to the currently logged in user. */ private CurrentUserProxy mCurrentUserProxy; private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { Loading @@ -106,7 +112,8 @@ public class VideoProviderProxy extends Connection.VideoProvider { * @throws RemoteException Remote exception. */ VideoProviderProxy(TelecomSystem.SyncRoot lock, IVideoProvider videoProvider, Call call) throws RemoteException { IVideoProvider videoProvider, Call call, CurrentUserProxy currentUserProxy) throws RemoteException { super(Looper.getMainLooper()); Loading @@ -118,6 +125,7 @@ public class VideoProviderProxy extends Connection.VideoProvider { mVideoCallListenerBinder = new VideoCallListenerBinder(); mConectionServiceVideoProvider.addVideoCallback(mVideoCallListenerBinder); mCall = call; mCurrentUserProxy = currentUserProxy; } /** Loading Loading @@ -300,13 +308,15 @@ public class VideoProviderProxy extends Connection.VideoProvider { public void onSetCamera(String cameraId, String callingPackage, int callingUid, int callingPid) { synchronized (mLock) { logFromInCall("setCamera: " + cameraId + " callingPackage=" + callingPackage); logFromInCall("setCamera: " + cameraId + " callingPackage=" + callingPackage + "; callingUid=" + callingUid); if (!TextUtils.isEmpty(cameraId)) { if (!canUseCamera(mCall.getContext(), callingPackage, callingUid, callingPid)) { // Calling app is not permitted to use the camera. Ignore the request and send // back a call session event indicating the error. Log.i(this, "onSetCamera: camera permission denied; package=%d, uid=%d, pid=%d", Log.i(this, "onSetCamera: camera permission denied; package=%d, uid=%d, " + "pid=%d", callingPackage, callingUid, callingPid); VideoProviderProxy.this.handleCallSessionEvent( Connection.VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR); Loading Loading @@ -528,11 +538,20 @@ public class VideoProviderProxy extends Connection.VideoProvider { * @param context The context. * @param callingPackage The package name of the caller (i.e. Dialer). * @param callingUid The UID of the caller. * @param callingPid The PID of the caller. * @return {@code true} if the calling uid and package can use the camera, {@code false} * otherwise. */ private boolean canUseCamera(Context context, String callingPackage, int callingUid, int callingPid) { UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); UserHandle currentUserHandle = mCurrentUserProxy.getCurrentUserHandle(); if (currentUserHandle != null && !currentUserHandle.equals(callingUser)) { Log.w(this, "canUseCamera attempt to user camera by background user."); return false; } try { context.enforcePermission(Manifest.permission.CAMERA, callingPid, callingUid, "Camera permission required."); Loading @@ -548,7 +567,7 @@ public class VideoProviderProxy extends Connection.VideoProvider { return appOpsManager != null && appOpsManager.noteOp(AppOpsManager.OP_CAMERA, callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED; } catch (SecurityException se) { Log.w(this, "canUserCamera got appOpps Exception " + se.toString()); Log.w(this, "canUseCamera got appOpps Exception " + se.toString()); return false; } } Loading
tests/src/com/android/server/telecom/tests/VideoProviderTest.java +26 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.graphics.SurfaceTexture; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.telecom.Connection.VideoProvider; import android.telecom.InCallService; import android.telecom.InCallService.VideoCall; Loading Loading @@ -210,6 +211,31 @@ public class VideoProviderTest extends TelecomSystemTest { sessionEventCaptor.getValue().intValue()); } /** * Tests the caller user handle check in {@link VideoCall#setCamera(String)} to ensure a camera * change from a background user is not permitted. */ @MediumTest public void testCameraChangeUserFail() throws Exception { // Wait until the callback has been received before performing verification. doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt()); // Set a fake user to be the current foreground user. mTelecomSystem.getCallsManager().onUserSwitch(new UserHandle(1000)); // Make a request to change the camera mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT); mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS); // Capture the session event reported via the callback. ArgumentCaptor<Integer> sessionEventCaptor = ArgumentCaptor.forClass(Integer.class); verify(mVideoCallCallback, timeout(TEST_TIMEOUT)).onCallSessionEvent( sessionEventCaptor.capture()); assertEquals(VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR, sessionEventCaptor.getValue().intValue()); } /** * Tests the caller permission check in {@link VideoCall#setCamera(String)} to ensure the * caller can null out the camera, even if they do not have camera permission. Loading