From caf201a8e72583dc6d1c717bbc252e5178219671 Mon Sep 17 00:00:00 2001 From: Amy Date: Fri, 14 Feb 2020 19:19:38 -0800 Subject: [PATCH] Implement TRM setFrontendInfoList logic with unit tests Note that this CL includes some format changes and redundant implementation removing in ClientProfile.java and TunerResourceManagerService.java. The key change of the CL is the FrontendResource.java object and the impl of the setFrontendResourceList method in TunerResourceManagerService.java. Test: atest TunerResourceManagerServiceTest Bug: 147380513 Change-Id: I3df8e386ad37bc67694594b07de04cc9f3b88e02 --- .../tunerresourcemanager/ClientProfile.java | 70 ++---- .../FrontendResource.java | 204 ++++++++++++++++++ .../TunerResourceManagerService.java | 183 +++++++++++----- .../TunerResourceManagerServiceTest.java | 190 ++++++++++++++++ 4 files changed, 541 insertions(+), 106 deletions(-) create mode 100644 services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java create mode 100644 services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index bad2b78dab48b..07c565a729bbe 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.server.tv.tunerresourcemanager; /** @@ -23,12 +22,14 @@ package com.android.server.tv.tunerresourcemanager; * @hide */ public final class ClientProfile { + public static final int INVALID_GROUP_ID = -1; + /** * Client id sent to the client when registering with * {@link #registerClientProfile(ResourceClientProfile, TunerResourceManagerCallback, int[])} */ - private final int mClientId; + private final int mId; /** * see {@link ResourceClientProfile} @@ -41,7 +42,7 @@ public final class ClientProfile { private final int mUseCase; /** - * Process id queried from {@link TvInputManager#} + * Process id queried from {@link TvInputManager#getPid(String)}. */ private final int mProcessId; @@ -66,18 +67,15 @@ public final class ClientProfile { */ private int mPriority; - private ClientProfile(ClientProfileBuilder builder) { - this.mClientId = builder.mClientId; + private ClientProfile(Builder builder) { + this.mId = builder.mId; this.mTvInputSessionId = builder.mTvInputSessionId; this.mUseCase = builder.mUseCase; this.mProcessId = builder.mProcessId; - this.mGroupId = builder.mGroupId; - this.mNiceValue = builder.mNiceValue; - this.mPriority = builder.mPriority; } - public int getClientId() { - return mClientId; + public int getId() { + return mId; } public String getTvInputSessionId() { @@ -118,24 +116,21 @@ public final class ClientProfile { @Override public String toString() { - return "ClientProfile: " + this.mClientId + ", " + this.mTvInputSessionId + ", " - + this.mUseCase + ", " + this.mProcessId; + return "ClientProfile[id=" + this.mId + ", tvInputSessionId=" + this.mTvInputSessionId + + ", useCase=" + this.mUseCase + ", processId=" + this.mProcessId + "]"; } /** * Builder class for {@link ClientProfile}. */ - public static class ClientProfileBuilder { - private final int mClientId; + public static class Builder { + private final int mId; private String mTvInputSessionId; private int mUseCase; private int mProcessId; - private int mGroupId; - private int mNiceValue; - private int mPriority; - ClientProfileBuilder(int clientId) { - this.mClientId = clientId; + Builder(int id) { + this.mId = id; } /** @@ -143,7 +138,7 @@ public final class ClientProfile { * * @param useCase the useCase of the client. */ - public ClientProfileBuilder useCase(int useCase) { + public Builder useCase(int useCase) { this.mUseCase = useCase; return this; } @@ -153,7 +148,7 @@ public final class ClientProfile { * * @param tvInputSessionId the id of the tv input session. */ - public ClientProfileBuilder tvInputSessionId(String tvInputSessionId) { + public Builder tvInputSessionId(String tvInputSessionId) { this.mTvInputSessionId = tvInputSessionId; return this; } @@ -163,42 +158,11 @@ public final class ClientProfile { * * @param processId the id of process. */ - public ClientProfileBuilder processId(int processId) { + public Builder processId(int processId) { this.mProcessId = processId; return this; } - - /** - * Builder for {@link ClientProfile}. - * - * @param groupId the id of the group that shares the same resource. - */ - public ClientProfileBuilder groupId(int groupId) { - this.mGroupId = groupId; - return this; - } - - /** - * Builder for {@link ClientProfile}. - * - * @param niceValue the nice value of the client. - */ - public ClientProfileBuilder niceValue(int niceValue) { - this.mNiceValue = niceValue; - return this; - } - - /** - * Builder for {@link ClientProfile}. - * - * @param priority the priority value of the client. - */ - public ClientProfileBuilder priority(int priority) { - this.mPriority = priority; - return this; - } - /** * Build a {@link ClientProfile}. * diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java new file mode 100644 index 0000000000000..a109265e2f502 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java @@ -0,0 +1,204 @@ +/* + * Copyright 2020 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.tv.tunerresourcemanager; + +import android.annotation.Nullable; +import android.media.tv.tuner.frontend.FrontendSettings.Type; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A frontend resource object used by the Tuner Resource Manager to record the tuner frontend + * information. + * + * @hide + */ +public final class FrontendResource { + public static final int INVALID_OWNER_ID = -1; + + /** + * Id of the current frontend. Should not be changed and should be aligned with the driver level + * implementation. + */ + private final int mId; + + /** + * see {@link android.media.tv.tuner.frontend.FrontendSettings.Type} + */ + @Type private final int mType; + + /** + * The exclusive group id of the FE. FEs under the same id can't be used at the same time. + */ + private final int mExclusiveGroupId; + + /** + * An array to save all the FE ids under the same exclisive group. + */ + private List mExclusiveGroupMemberFeIds = new ArrayList<>(); + + /** + * If the current resource is in use. Once resources under the same exclusive group id is in use + * all other resources in the same group would be considered in use. + */ + private boolean mIsInUse; + + /** + * The owner client's id if this resource is occupied. Owner of the resource under the same + * exclusive group id would be considered as the whole group's owner. + */ + private int mOwnerClientId = INVALID_OWNER_ID; + + private FrontendResource(Builder builder) { + this.mId = builder.mId; + this.mType = builder.mType; + this.mExclusiveGroupId = builder.mExclusiveGroupId; + } + + public int getId() { + return mId; + } + + public int getType() { + return mType; + } + + public int getExclusiveGroupId() { + return mExclusiveGroupId; + } + + public List getExclusiveGroupMemberFeIds() { + return mExclusiveGroupMemberFeIds; + } + + /** + * Add one id into the exclusive group member id list. + * + * @param id the id to be added. + */ + public void addExclusiveGroupMemberFeId(int id) { + mExclusiveGroupMemberFeIds.add(id); + } + + /** + * Add one id list to the exclusive group member id list. + * + * @param ids the id list to be added. + */ + public void addExclusiveGroupMemberFeId(List ids) { + mExclusiveGroupMemberFeIds.addAll(ids); + } + + /** + * Remove one id from the exclusive group member id list. + * + * @param id the id to be removed. + */ + public void removeExclusiveGroupMemberFeId(int id) { + mExclusiveGroupMemberFeIds.remove(new Integer(id)); + } + + public boolean isInUse() { + return mIsInUse; + } + + public int getOwnerClientId() { + return mOwnerClientId; + } + + /** + * Set an owner client on the resource. + * + * @param ownerClientId the id of the owner client. + */ + public void setOwner(int ownerClientId) { + mIsInUse = true; + mOwnerClientId = ownerClientId; + } + + /** + * Remove an owner client from the resource. + */ + public void removeOwner() { + mIsInUse = false; + mOwnerClientId = INVALID_OWNER_ID; + } + + @Override + public String toString() { + return "FrontendResource[id=" + this.mId + ", type=" + this.mType + + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberIds=" + + Arrays.toString(this.mExclusiveGroupMemberFeIds.toArray()) + + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof FrontendResource) { + FrontendResource fe = (FrontendResource) o; + return mId == fe.getId() && mType == fe.getType() + && mExclusiveGroupId == fe.getExclusiveGroupId() + && mExclusiveGroupMemberFeIds.equals(fe.getExclusiveGroupMemberFeIds()) + && mIsInUse == fe.isInUse() && mOwnerClientId == fe.getOwnerClientId(); + } + return false; + } + + /** + * Builder class for {@link FrontendResource}. + */ + public static class Builder { + private final int mId; + @Type private int mType; + private int mExclusiveGroupId; + + Builder(int id) { + this.mId = id; + } + + /** + * Builder for {@link FrontendResource}. + * + * @param type the type of the frontend. See {@link Type} + */ + public Builder type(@Type int type) { + this.mType = type; + return this; + } + + /** + * Builder for {@link FrontendResource}. + * + * @param exclusiveGroupId the id of exclusive group. + */ + public Builder exclusiveGroupId(int exclusiveGroupId) { + this.mExclusiveGroupId = exclusiveGroupId; + return this; + } + + /** + * Build a {@link FrontendResource}. + * + * @return {@link FrontendResource}. + */ + public FrontendResource build() { + FrontendResource frontendResource = new FrontendResource(this); + return frontendResource; + } + } +} 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 49a7045bf57a7..c91a9297eb17a 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -33,28 +33,40 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import java.util.ArrayList; import java.util.List; /** - * This class provides a system service that manages the TV tuner resources. - * - * @hide - */ + * This class provides a system service that manages the TV tuner resources. + * + * @hide + */ public class TunerResourceManagerService extends SystemService { private static final String TAG = "TunerResourceManagerService"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private SparseArray mClientProfiles = new SparseArray<>(); - private SparseArray mListeners = new SparseArray<>(); - private int mNextUnusedFrontendId = 0; + // Array of the registered client profiles + @VisibleForTesting private SparseArray mClientProfiles = new SparseArray<>(); + private int mNextUnusedClientId = 0; private List mReleasedClientId = new ArrayList(); + + // Array of the current available frontend resources + @VisibleForTesting + private SparseArray mFrontendResources = new SparseArray<>(); + @VisibleForTesting private SparseArray mFrontendTypeMap = new SparseArray<>(); + // Array of the current available frontend ids private List mAvailableFrontendIds = new ArrayList(); + private SparseArray mListeners = new SparseArray<>(); + private TvInputManager mManager; + // Used to synchronize the access to the service. + private final Object mLock = new Object(); + public TunerResourceManagerService(@Nullable Context context) { super(context); } @@ -62,22 +74,20 @@ public class TunerResourceManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); - mManager = (TvInputManager) getContext() - .getSystemService(Context.TV_INPUT_SERVICE); + mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); } private final class BinderService extends ITunerResourceManager.Stub { @Override public void registerClientProfile(@NonNull ResourceClientProfile profile, - @NonNull IResourcesReclaimListener listener, - @NonNull int[] clientId) { + @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId) { if (DEBUG) { Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); } // TODO tell if the client already exists if (mReleasedClientId.isEmpty()) { - clientId[0] = mNextUnusedFrontendId++; + clientId[0] = mNextUnusedClientId++; } else { clientId[0] = mReleasedClientId.get(0); mReleasedClientId.remove(0); @@ -90,12 +100,11 @@ public class TunerResourceManagerService extends SystemService { int callingPid = mManager.getClientPid(profile.getTvInputSessionId()); - ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder( - clientId[0]) - .tvInputSessionId(profile.getTvInputSessionId()) - .useCase(profile.getUseCase()) - .processId(callingPid) - .build(); + ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) + .tvInputSessionId(profile.getTvInputSessionId()) + .useCase(profile.getUseCase()) + .processId(callingPid) + .build(); mClientProfiles.append(clientId[0], clientProfile); mListeners.append(clientId[0], listener); } @@ -114,14 +123,16 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean updateClientPriority(int clientId, int priority, int niceValue) { if (DEBUG) { - Slog.d(TAG, "updateClientPriority(clientId=" + clientId - + ", priority=" + priority + ", niceValue=" + niceValue + ")"); + Slog.d(TAG, + "updateClientPriority(clientId=" + clientId + ", priority=" + priority + + ", niceValue=" + niceValue + ")"); } ClientProfile profile = mClientProfiles.get(clientId); if (profile == null) { - Slog.e(TAG, "Can not find client profile with id " + clientId - + " when trying to update the client priority."); + Slog.e(TAG, + "Can not find client profile with id " + clientId + + " when trying to update the client priority."); return false; } @@ -132,26 +143,22 @@ public class TunerResourceManagerService extends SystemService { } @Override - public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) - throws RemoteException { - if (infos == null || infos.length == 0) { - Slog.d(TAG, "Can't update with empty frontend info"); - return; + public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException { + enforceAccessPermission(); + if (infos == null) { + throw new RemoteException("TunerFrontendInfo can't be null"); } - - if (DEBUG) { - Slog.d(TAG, "updateFrontendInfo:"); - for (int i = 0; i < infos.length; i++) { - Slog.d(TAG, infos[i].toString()); - } + synchronized (mLock) { + setFrontendInfoListInternal(infos); } } @Override public void updateCasInfo(int casSystemId, int maxSessionNum) { if (DEBUG) { - Slog.d(TAG, "updateCasInfo(casSystemId=" - + casSystemId + ", maxSessionNum=" + maxSessionNum + ")"); + Slog.d(TAG, + "updateCasInfo(casSystemId=" + casSystemId + + ", maxSessionNum=" + maxSessionNum + ")"); } } @@ -166,26 +173,19 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean requestFrontend(@NonNull TunerFrontendRequest request, - @NonNull int[] frontendId) throws RemoteException { + @NonNull int[] frontendId) throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestFrontend(request=" + request + ")"); } frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID; - if (getContext() == null) { - Slog.e(TAG, "Can not find context when requesting frontend"); - return false; - } - if (mClientProfiles.get(request.getClientId()) == null) { - Slog.e(TAG, "Request from unregistered client. Id: " - + request.getClientId()); + Slog.e(TAG, "Request from unregistered client. Id: " + request.getClientId()); return false; } - String sessionId = mClientProfiles.get(request.getClientId()) - .getTvInputSessionId(); + String sessionId = mClientProfiles.get(request.getClientId()).getTvInputSessionId(); if (DEBUG) { Slog.d(TAG, "session Id:" + sessionId + ")"); @@ -201,14 +201,13 @@ public class TunerResourceManagerService extends SystemService { @Override public void shareFrontend(int selfClientId, int targetClientId) { if (DEBUG) { - Slog.d(TAG, "shareFrontend from " - + selfClientId + " with " + targetClientId); + Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); } } @Override - public boolean requestCasSession(@NonNull CasSessionRequest request, - @NonNull int[] sessionResourceId) { + public boolean requestCasSession( + @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) { if (DEBUG) { Slog.d(TAG, "requestCasSession(request=" + request + ")"); } @@ -246,13 +245,91 @@ public class TunerResourceManagerService extends SystemService { } @Override - public boolean isHigherPriority(ResourceClientProfile challengerProfile, - ResourceClientProfile holderProfile) { + public boolean isHigherPriority( + ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { if (DEBUG) { - Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile - + ", holderProfile=" + challengerProfile + ")"); + Slog.d(TAG, + "isHigherPriority(challengerProfile=" + challengerProfile + + ", holderProfile=" + challengerProfile + ")"); } return true; } } + + @VisibleForTesting + protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) { + if (DEBUG) { + Slog.d(TAG, "updateFrontendInfo:"); + for (int i = 0; i < infos.length; i++) { + Slog.d(TAG, infos[i].toString()); + } + } + + // An arrayList to record the frontends pending on updating. Ids will be removed + // from this list once its updating finished. Any frontend left in this list when all + // the updates are done will be removed from mAvailableFrontendIds and + // mFrontendResources. + List updatingFrontendIds = new ArrayList<>(mAvailableFrontendIds); + + // Update frontendResources sparse array and other mappings accordingly + for (int i = 0; i < infos.length; i++) { + if (mFrontendResources.get(infos[i].getId()) != null) { + if (DEBUG) { + Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists."); + } + updatingFrontendIds.remove(new Integer(infos[i].getId())); + } else { + // Add a new fe resource + FrontendResource newFe = new FrontendResource.Builder(infos[i].getId()) + .type(infos[i].getFrontendType()) + .exclusiveGroupId(infos[i].getExclusiveGroupId()) + .build(); + // Update the exclusive group member list in all the existing Frontend resource + for (Integer feId : mAvailableFrontendIds) { + FrontendResource fe = mFrontendResources.get(feId.intValue()); + if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) { + newFe.addExclusiveGroupMemberFeId(fe.getId()); + newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds()); + for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { + mFrontendResources.get(excGroupmemberFeId.intValue()) + .addExclusiveGroupMemberFeId(newFe.getId()); + } + fe.addExclusiveGroupMemberFeId(newFe.getId()); + break; + } + } + // Update resource list and available id list + mFrontendResources.append(newFe.getId(), newFe); + mAvailableFrontendIds.add(newFe.getId()); + } + } + + // TODO check if the removing resource is in use or not. Handle the conflict. + for (Integer removingId : updatingFrontendIds) { + // update the exclusive group id memver list + FrontendResource fe = mFrontendResources.get(removingId.intValue()); + fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId())); + for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { + mFrontendResources.get(excGroupmemberFeId.intValue()) + .removeExclusiveGroupMemberFeId(new Integer(fe.getId())); + } + mFrontendResources.remove(removingId.intValue()); + mAvailableFrontendIds.remove(removingId); + } + for (int i = 0; i < mFrontendResources.size(); i++) { + int key = mFrontendResources.keyAt(i); + // get the object by the key. + FrontendResource r = mFrontendResources.get(key); + } + } + + @VisibleForTesting + protected SparseArray getFrontendResources() { + return mFrontendResources; + } + + private void enforceAccessPermission() { + getContext().enforceCallingOrSelfPermission( + "android.permission.TUNER_RESOURCE_ACCESS", TAG); + } } 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 new file mode 100644 index 0000000000000..02060ee03d86c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2020 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.tv.tunerresourcemanager; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.content.ContextWrapper; +import android.media.tv.tuner.frontend.FrontendSettings; +import android.media.tv.tunerresourcemanager.TunerFrontendInfo; +import android.util.SparseArray; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Tests for {@link TunerResourceManagerService} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class TunerResourceManagerServiceTest { + private static final String TAG = "TunerResourceManagerServiceTest"; + private Context mContextSpy; + private TunerResourceManagerService mTunerResourceManagerService; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) {}; + } + + @Test + public void setFrontendListTest_addFrontendResources_noExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + SparseArray resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos.length); + for (int id = 0; id < infos.length; id++) { + FrontendResource fe = resources.get(infos[id].getId()); + assertThat(fe.getId()).isEqualTo(infos[id].getId()); + assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void setFrontendListTest_addFrontendResources_underTheSameExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[4]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[3] = + new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + SparseArray resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos.length); + for (int id = 0; id < infos.length; id++) { + FrontendResource fe = resources.get(infos[id].getId()); + assertThat(fe.getId()).isEqualTo(infos[id].getId()); + assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId()); + } + + assertThat(resources.get(0).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList()); + assertThat(resources.get(1).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList(Arrays.asList(2, 3))); + assertThat(resources.get(2).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList(Arrays.asList(1, 3))); + assertThat(resources.get(3).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList(Arrays.asList(1, 2))); + } + + @Test + public void setFrontendListTest_updateExistingFrontendResources() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + SparseArray resources0 = + mTunerResourceManagerService.getFrontendResources(); + + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + SparseArray resources1 = + mTunerResourceManagerService.getFrontendResources(); + + assertThat(resources0).isEqualTo(resources1); + } + + @Test + public void setFrontendListTest_removeFrontendResources_noExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; + infos0[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos0[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos0[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBT, 2 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); + + TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; + infos1[0] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos1); + + SparseArray resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos1.length); + for (int id = 0; id < infos1.length; id++) { + FrontendResource fe = resources.get(infos1[id].getId()); + assertThat(fe.getId()).isEqualTo(infos1[id].getId()); + assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void setFrontendListTest_removeFrontendResources_underTheSameExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; + infos0[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos0[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos0[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); + + TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; + infos1[0] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos1); + + SparseArray resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos1.length); + for (int id = 0; id < infos1.length; id++) { + FrontendResource fe = resources.get(infos1[id].getId()); + assertThat(fe.getId()).isEqualTo(infos1[id].getId()); + assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } +}