From a2249444a70c5cb420c184dd8ba0c06910709a6c Mon Sep 17 00:00:00 2001 From: Amy Zhang Date: Wed, 29 Apr 2020 17:02:50 -0700 Subject: [PATCH] Complete the acquireHardware API implementation in the TvInputHardwareManager This CL: 1. Finish the logic in TIHM to utilize the TRM for a client priority check. 2. FInish the TRM isHigherPriority API to allow the TIHM to compare the client priority. 3. Finish the TRM isForeground method to check if a process is foreground or not. 3. Add unit test for TRM isHigherPriority. Test: atest com.android.server.tv.tunerresourcemanager Bug: 155339425 Change-Id: I3887ccad31195d3386f1fc6b3de3e641df91092f --- .../java/android/media/tv/TvInputManager.java | 8 +- .../ResourceClientProfile.java | 5 +- .../server/tv/TvInputHardwareManager.java | 51 +++++++++---- .../TunerResourceManagerService.java | 74 +++++++++++++++---- .../TunerResourceManagerServiceTest.java | 26 ++++++- 5 files changed, 129 insertions(+), 35 deletions(-) diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index f058a02ceb1e1..e701055c28941 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -1805,7 +1805,7 @@ public final class TvInputManager { String tvInputSessionId, int priorityHint, Executor executor, final HardwareCallback callback) { try { - return new Hardware( + ITvInputHardware hardware = mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() { @Override public void onReleased() { @@ -1826,7 +1826,11 @@ public final class TvInputManager { Binder.restoreCallingIdentity(identity); } } - }, info, mUserId, tvInputSessionId, priorityHint)); + }, info, mUserId, tvInputSessionId, priorityHint); + if (hardware == null) { + return null; + } + return new Hardware(hardware); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java index 598ff8f3f0758..28f1ac916690b 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java +++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java @@ -17,6 +17,7 @@ package android.media.tv.tunerresourcemanager; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -81,7 +82,7 @@ public final class ResourceClientProfile implements Parcelable { * OEM. The id of the useCaseVendor should be passed through this parameter. Any * undefined use case would cause IllegalArgumentException. */ - public ResourceClientProfile(@NonNull String tvInputSessionId, + public ResourceClientProfile(@Nullable String tvInputSessionId, int useCase) { mTvInputSessionId = tvInputSessionId; mUseCase = useCase; @@ -92,7 +93,7 @@ public final class ResourceClientProfile implements Parcelable { * * @return the value of the tv input session id. */ - @NonNull + @Nullable public String getTvInputSessionId() { return mTvInputSessionId; } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 8f71943129fa6..2314afc787c37 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -46,6 +46,8 @@ import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvInputService.PriorityHintUseCaseType; import android.media.tv.TvStreamConfig; +import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -179,7 +181,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId); return; } - connection.resetLocked(null, null, null, null, null); + connection.resetLocked(null, null, null, null, null, null); mConnections.remove(deviceId); buildHardwareListLocked(); TvInputHardwareInfo info = connection.getHardwareInfoLocked(); @@ -369,25 +371,34 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (callback == null) { throw new NullPointerException(); } + TunerResourceManager trm = (TunerResourceManager) mContext.getSystemService( + Context.TV_TUNER_RESOURCE_MGR_SERVICE); synchronized (mLock) { Connection connection = mConnections.get(deviceId); if (connection == null) { Slog.e(TAG, "Invalid deviceId : " + deviceId); return null; } - // TODO: check with TRM to compare the client's priority with the current holder's - // priority. If lower, do nothing. - if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) { - TvInputHardwareImpl hardware = - new TvInputHardwareImpl(connection.getHardwareInfoLocked()); - try { - callback.asBinder().linkToDeath(connection, 0); - } catch (RemoteException e) { - hardware.release(); - return null; - } - connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId); + + ResourceClientProfile profile = + new ResourceClientProfile(tvInputSessionId, priorityHint); + ResourceClientProfile holderProfile = connection.getResourceClientProfileLocked(); + if (holderProfile != null && trm != null + && !trm.isHigherPriority(profile, holderProfile)) { + Slog.d(TAG, "Acquiring does not show higher priority than the current holder." + + " Device id:" + deviceId); + return null; } + TvInputHardwareImpl hardware = + new TvInputHardwareImpl(connection.getHardwareInfoLocked()); + try { + callback.asBinder().linkToDeath(connection, 0); + } catch (RemoteException e) { + hardware.release(); + return null; + } + connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId, + profile); return connection.getHardwareLocked(); } } @@ -411,7 +422,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (callback != null) { callback.asBinder().unlinkToDeath(connection, 0); } - connection.resetLocked(null, null, null, null, null); + connection.resetLocked(null, null, null, null, null, null); } } @@ -621,6 +632,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { private Integer mCallingUid = null; private Integer mResolvedUserId = null; private Runnable mOnFirstFrameCaptured; + private ResourceClientProfile mResourceClientProfile = null; public Connection(TvInputHardwareInfo hardwareInfo) { mHardwareInfo = hardwareInfo; @@ -629,7 +641,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { // *Locked methods assume TvInputHardwareManager.mLock is held. public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback, - TvInputInfo info, Integer callingUid, Integer resolvedUserId) { + TvInputInfo info, Integer callingUid, Integer resolvedUserId, + ResourceClientProfile profile) { if (mHardware != null) { try { mCallback.onReleased(); @@ -644,6 +657,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { mCallingUid = callingUid; mResolvedUserId = resolvedUserId; mOnFirstFrameCaptured = null; + mResourceClientProfile = profile; if (mHardware != null && mCallback != null) { try { @@ -698,10 +712,14 @@ class TvInputHardwareManager implements TvInputHal.Callback { return mOnFirstFrameCaptured; } + public ResourceClientProfile getResourceClientProfileLocked() { + return mResourceClientProfile; + } + @Override public void binderDied() { synchronized (mLock) { - resetLocked(null, null, null, null, null); + resetLocked(null, null, null, null, null, null); } } @@ -713,6 +731,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { + ", mConfigs: " + Arrays.toString(mConfigs) + ", mCallingUid: " + mCallingUid + ", mResolvedUserId: " + mResolvedUserId + + ", mResourceClientProfile: " + mResourceClientProfile + " }"; } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 2f70840cfc8b7..41aa4ee65f30f 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -18,6 +18,8 @@ package com.android.server.tv.tunerresourcemanager; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; import android.media.tv.TvInputManager; import android.media.tv.tunerresourcemanager.CasSessionRequest; @@ -42,6 +44,7 @@ import com.android.server.SystemService; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -71,7 +74,8 @@ public class TunerResourceManagerService extends SystemService { @GuardedBy("mLock") private Map mListeners = new HashMap<>(); - private TvInputManager mManager; + private TvInputManager mTvInputManager; + private ActivityManager mActivityManager; private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints(); // An internal resource request count to help generate resource handle. @@ -94,7 +98,9 @@ public class TunerResourceManagerService extends SystemService { if (!isForTesting) { publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); } - mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); + mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); + mActivityManager = + (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); mPriorityCongfig.parse(); } @@ -204,7 +210,7 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean requestDemux(@NonNull TunerDemuxRequest request, - @NonNull int[] demuxHandle) throws RemoteException { + @NonNull int[] demuxHandle) throws RemoteException { enforceTunerAccessPermission("requestDemux"); enforceTrmAccessPermission("requestDemux"); if (demuxHandle == null) { @@ -362,14 +368,15 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean isHigherPriority( - ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { + ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) + throws RemoteException { enforceTrmAccessPermission("isHigherPriority"); - if (DEBUG) { - Slog.d(TAG, - "isHigherPriority(challengerProfile=" + challengerProfile - + ", holderProfile=" + challengerProfile + ")"); + if (challengerProfile == null || holderProfile == null) { + throw new RemoteException("Client profiles can't be null."); + } + synchronized (mLock) { + return isHigherPriorityInternal(challengerProfile, holderProfile); } - return true; } } @@ -381,7 +388,7 @@ public class TunerResourceManagerService extends SystemService { } clientId[0] = INVALID_CLIENT_ID; - if (mManager == null) { + if (mTvInputManager == null) { Slog.e(TAG, "TvInputManager is null. Can't register client profile."); return; } @@ -390,7 +397,7 @@ public class TunerResourceManagerService extends SystemService { int pid = profile.getTvInputSessionId() == null ? Binder.getCallingPid() /*callingPid*/ - : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/ + : mTvInputManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/ ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) .tvInputSessionId(profile.getTvInputSessionId()) @@ -692,6 +699,33 @@ public class TunerResourceManagerService extends SystemService { return false; } + @VisibleForTesting + protected boolean isHigherPriorityInternal(ResourceClientProfile challengerProfile, + ResourceClientProfile holderProfile) { + if (DEBUG) { + Slog.d(TAG, + "isHigherPriority(challengerProfile=" + challengerProfile + + ", holderProfile=" + challengerProfile + ")"); + } + if (mTvInputManager == null) { + Slog.e(TAG, "TvInputManager is null. Can't compare the priority."); + // Allow the client to acquire the hardware interface + // when the TRM is not able to compare the priority. + return true; + } + + int challengerPid = challengerProfile.getTvInputSessionId() == null + ? Binder.getCallingPid() /*callingPid*/ + : mTvInputManager.getClientPid(challengerProfile.getTvInputSessionId()); /*tvAppId*/ + int holderPid = holderProfile.getTvInputSessionId() == null + ? Binder.getCallingPid() /*callingPid*/ + : mTvInputManager.getClientPid(holderProfile.getTvInputSessionId()); /*tvAppId*/ + + int challengerPriority = getClientPriority(challengerProfile.getUseCase(), challengerPid); + int holderPriority = getClientPriority(holderProfile.getUseCase(), holderPid); + return challengerPriority > holderPriority; + } + @VisibleForTesting protected void releaseFrontendInternal(FrontendResource fe) { if (DEBUG) { @@ -818,8 +852,20 @@ public class TunerResourceManagerService extends SystemService { @VisibleForTesting protected boolean isForeground(int pid) { - // TODO: how to get fg/bg information from pid - return true; + if (mActivityManager == null) { + return false; + } + List appProcesses = mActivityManager.getRunningAppProcesses(); + if (appProcesses == null) { + return false; + } + for (RunningAppProcessInfo appProcess : appProcesses) { + if (appProcess.pid == pid + && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + return true; + } + } + return false; } private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) { @@ -1044,7 +1090,7 @@ public class TunerResourceManagerService extends SystemService { } private void enforceTrmAccessPermission(String apiName) { - getContext().enforceCallingPermission("android.permission.TUNER_RESOURCE_ACCESS", + getContext().enforceCallingOrSelfPermission("android.permission.TUNER_RESOURCE_ACCESS", TAG + ": " + apiName); } diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 21af3563b8690..f9343236662bf 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -64,6 +64,7 @@ public class TunerResourceManagerServiceTest { private Context mContextSpy; @Mock private ITvInputManager mITvInputManagerMock; private TunerResourceManagerService mTunerResourceManagerService; + private boolean mIsForeground; private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub { boolean mReclaimed; @@ -104,7 +105,12 @@ public class TunerResourceManagerServiceTest { TvInputManager tvInputManager = new TvInputManager(mITvInputManagerMock, 0); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager); - mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy); + mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) { + @Override + protected boolean isForeground(int pid) { + return mIsForeground; + } + }; mTunerResourceManagerService.onStart(true /*isForTesting*/); } @@ -737,4 +743,22 @@ public class TunerResourceManagerServiceTest { .isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0); } + + @Test + public void isHigherPriorityTest() { + mIsForeground = false; + ResourceClientProfile backgroundPlaybackProfile = + new ResourceClientProfile(null /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + ResourceClientProfile backgroundRecordProfile = + new ResourceClientProfile(null /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); + int backgroundPlaybackPriority = mTunerResourceManagerService.getClientPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 0); + int backgroundRecordPriority = mTunerResourceManagerService.getClientPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 0); + assertThat(mTunerResourceManagerService.isHigherPriorityInternal(backgroundPlaybackProfile, + backgroundRecordProfile)).isEqualTo( + (backgroundPlaybackPriority > backgroundRecordPriority)); + } }