summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java3
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java102
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java204
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java459
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java236
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java497
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java111
7 files changed, 1453 insertions, 159 deletions
diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
index 68372444cb83..598ff8f3f075 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
+++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
@@ -78,7 +78,8 @@ public final class ResourceClientProfile implements Parcelable {
* {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE}
* {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}.
* New [use case : priority value] pair can be defined in the manifest by the
- * OEM. Any undefined use case would cause IllegalArgumentException.
+ * OEM. The id of the useCaseVendor should be passed through this parameter. Any
+ * undefined use case would cause IllegalArgumentException.
*/
public ResourceClientProfile(@NonNull String tvInputSessionId,
int useCase) {
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 bad2b78dab48..4eff954f1ec8 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.server.tv.tunerresourcemanager;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A client profile object used by the Tuner Resource Manager to record the registered clients'
* information.
@@ -23,12 +25,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 +45,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;
@@ -59,6 +63,11 @@ public final class ClientProfile {
private int mNiceValue;
/**
+ * List of the frontend ids that are used by the current client.
+ */
+ private List<Integer> mUsingFrontendIds = new ArrayList<>();
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -66,18 +75,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() {
@@ -116,26 +122,47 @@ public final class ClientProfile {
mNiceValue = niceValue;
}
+ /**
+ * Set when the client starts to use a frontend.
+ *
+ * @param frontendId being used.
+ */
+ public void useFrontend(int frontendId) {
+ mUsingFrontendIds.add(frontendId);
+ }
+
+ public List<Integer> getInUseFrontendIds() {
+ return mUsingFrontendIds;
+ }
+
+ /**
+ * Called when the client released a frontend.
+ *
+ * <p>This could happen when client resource reclaimed.
+ *
+ * @param frontendId being released.
+ */
+ public void releaseFrontend(int frontendId) {
+ mUsingFrontendIds.remove(frontendId);
+ }
+
@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 +170,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 +180,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 +190,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 000000000000..a109265e2f50
--- /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<Integer> 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<Integer> 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<Integer> 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 49a7045bf57a..cb31a502ecfa 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -28,32 +28,48 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.Binder;
import android.os.RemoteException;
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<ClientProfile> mClientProfiles = new SparseArray<>();
- private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>();
- private int mNextUnusedFrontendId = 0;
- private List<Integer> mReleasedClientId = new ArrayList<Integer>();
+ public static final int INVALID_CLIENT_ID = -1;
+ private static final int MAX_CLIENT_PRIORITY = 1000;
+
+ // Array of the registered client profiles
+ @VisibleForTesting private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>();
+ private int mNextUnusedClientId = 0;
+ private List<Integer> mRegisteredClientIds = new ArrayList<Integer>();
+
+ // Array of the current available frontend resources
+ @VisibleForTesting
+ private SparseArray<FrontendResource> mFrontendResources = new SparseArray<>();
+ // Array of the current available frontend ids
private List<Integer> mAvailableFrontendIds = new ArrayList<Integer>();
+ private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>();
+
private TvInputManager mManager;
+ private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
+
+ // Used to synchronize the access to the service.
+ private final Object mLock = new Object();
public TunerResourceManagerService(@Nullable Context context) {
super(context);
@@ -61,97 +77,78 @@ 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);
+ onStart(false /*isForTesting*/);
+ }
+
+ @VisibleForTesting
+ protected void onStart(boolean isForTesting) {
+ if (!isForTesting) {
+ publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService());
+ }
+ mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ mPriorityCongfig.parse();
}
private final class BinderService extends ITunerResourceManager.Stub {
@Override
public void registerClientProfile(@NonNull ResourceClientProfile profile,
- @NonNull IResourcesReclaimListener listener,
- @NonNull int[] clientId) {
- if (DEBUG) {
- Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")");
+ @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
+ throws RemoteException {
+ enforceAccessPermission();
+ if (profile == null) {
+ throw new RemoteException("ResourceClientProfile can't be null");
}
- // TODO tell if the client already exists
- if (mReleasedClientId.isEmpty()) {
- clientId[0] = mNextUnusedFrontendId++;
- } else {
- clientId[0] = mReleasedClientId.get(0);
- mReleasedClientId.remove(0);
+ if (clientId == null) {
+ throw new RemoteException("clientId can't be null!");
}
- if (mManager == null) {
- Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
- return;
+ if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) {
+ throw new RemoteException("Use undefined client use case:" + profile.getUseCase());
}
- int callingPid = mManager.getClientPid(profile.getTvInputSessionId());
-
- ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder(
- clientId[0])
- .tvInputSessionId(profile.getTvInputSessionId())
- .useCase(profile.getUseCase())
- .processId(callingPid)
- .build();
- mClientProfiles.append(clientId[0], clientProfile);
- mListeners.append(clientId[0], listener);
+ synchronized (mLock) {
+ registerClientProfileInternal(profile, listener, clientId);
+ }
}
@Override
- public void unregisterClientProfile(int clientId) {
- if (DEBUG) {
- Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+ public void unregisterClientProfile(int clientId) throws RemoteException {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ Slog.e(TAG, "Unregistering non exists client:" + clientId);
+ return;
+ }
+ unregisterClientProfileInternal(clientId);
}
-
- mClientProfiles.remove(clientId);
- mListeners.remove(clientId);
- mReleasedClientId.add(clientId);
}
@Override
public boolean updateClientPriority(int clientId, int priority, int niceValue) {
- if (DEBUG) {
- 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.");
- return false;
+ enforceAccessPermission();
+ synchronized (mLock) {
+ return updateClientPriorityInternal(clientId, priority, niceValue);
}
-
- profile.setPriority(priority);
- profile.setNiceValue(niceValue);
-
- return true;
}
@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,49 +163,30 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @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;
+ @NonNull int[] frontendId) throws RemoteException {
+ enforceAccessPermission();
+ if (frontendId == null) {
+ throw new RemoteException("frontendId can't be null");
}
-
- if (mClientProfiles.get(request.getClientId()) == null) {
- Slog.e(TAG, "Request from unregistered client. Id: "
- + request.getClientId());
- return false;
- }
-
- String sessionId = mClientProfiles.get(request.getClientId())
- .getTvInputSessionId();
-
- if (DEBUG) {
- Slog.d(TAG, "session Id:" + sessionId + ")");
- }
-
- if (DEBUG) {
- Slog.d(TAG, "No available Frontend found.");
+ synchronized (mLock) {
+ try {
+ return requestFrontendInternal(request, frontendId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
-
- return false;
}
@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 +224,284 @@ 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 registerClientProfileInternal(ResourceClientProfile profile,
+ IResourcesReclaimListener listener, int[] clientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")");
+ }
+
+ clientId[0] = INVALID_CLIENT_ID;
+ if (mManager == null) {
+ Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
+ return;
+ }
+ // TODO tell if the client already exists
+ clientId[0] = mNextUnusedClientId++;
+
+ int pid = profile.getTvInputSessionId() == null
+ ? Binder.getCallingPid() /*callingPid*/
+ : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
+
+ ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
+ .tvInputSessionId(profile.getTvInputSessionId())
+ .useCase(profile.getUseCase())
+ .processId(pid)
+ .build();
+ clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid));
+
+ mClientProfiles.append(clientId[0], clientProfile);
+ mListeners.append(clientId[0], listener);
+ mRegisteredClientIds.add(clientId[0]);
+ }
+
+ @VisibleForTesting
+ protected void unregisterClientProfileInternal(int clientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+ }
+ for (int id : getClientProfile(clientId).getInUseFrontendIds()) {
+ getFrontendResource(id).removeOwner();
+ for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(groupMemberId).removeOwner();
+ }
+ }
+ mClientProfiles.remove(clientId);
+ mListeners.remove(clientId);
+ mRegisteredClientIds.remove(clientId);
+ }
+
+ @VisibleForTesting
+ protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "updateClientPriority(clientId=" + clientId + ", priority=" + priority
+ + ", niceValue=" + niceValue + ")");
+ }
+
+ ClientProfile profile = getClientProfile(clientId);
+ if (profile == null) {
+ Slog.e(TAG,
+ "Can not find client profile with id " + clientId
+ + " when trying to update the client priority.");
+ return false;
+ }
+
+ profile.setPriority(priority);
+ profile.setNiceValue(niceValue);
+
+ 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<Integer> updatingFrontendIds = new ArrayList<>(mAvailableFrontendIds);
+
+ // Update frontendResources sparse array and other mappings accordingly
+ for (int i = 0; i < infos.length; i++) {
+ if (getFrontendResource(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 = getFrontendResource(feId.intValue());
+ if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
+ newFe.addExclusiveGroupMemberFeId(fe.getId());
+ newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds());
+ for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(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 = getFrontendResource(removingId.intValue());
+ fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
+ for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(excGroupmemberFeId.intValue())
+ .removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
+ }
+ mFrontendResources.remove(removingId.intValue());
+ mAvailableFrontendIds.remove(removingId);
+ }
+ }
+
+ @VisibleForTesting
+ protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "requestFrontend(request=" + request + ")");
+ }
+
+ frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
+ if (!checkClientExists(request.getClientId())) {
+ Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
+ return false;
+ }
+ ClientProfile requestClient = getClientProfile(request.getClientId());
+ int grantingFrontendId = -1;
+ int inUseLowestPriorityFrId = -1;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ for (int id : mAvailableFrontendIds) {
+ FrontendResource fr = getFrontendResource(id);
+ if (fr.getType() == request.getFrontendType()) {
+ if (!fr.isInUse()) {
+ // Grant unused frontend with no exclusive group members first.
+ if (fr.getExclusiveGroupMemberFeIds().size() == 0) {
+ grantingFrontendId = id;
+ break;
+ } else if (grantingFrontendId < 0) {
+ // Grant the unused frontend with lower id first if all the unused
+ // frontends have exclusive group members.
+ grantingFrontendId = id;
+ }
+ } else if (grantingFrontendId < 0) {
+ // Record the frontend id with the lowest client priority among all the
+ // in use frontends when no available frontend has been found.
+ int priority = getOwnerClientPriority(id);
+ if (currentLowestPriority > priority) {
+ inUseLowestPriorityFrId = id;
+ currentLowestPriority = priority;
+ }
+ }
+ }
+ }
+
+ // Grant frontend when there is unused resource.
+ if (grantingFrontendId > -1) {
+ frontendId[0] = grantingFrontendId;
+ updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ return true;
+ }
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
+ frontendId[0] = inUseLowestPriorityFrId;
+ reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId());
+ updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ return true;
+ }
+
+ return false;
+ }
+
+ @VisibleForTesting
+ protected int getClientPriority(int useCase, int pid) {
+ if (DEBUG) {
+ Slog.d(TAG, "getClientPriority useCase=" + useCase
+ + ", pid=" + pid + ")");
+ }
+
+ if (isForeground(pid)) {
+ return mPriorityCongfig.getForegroundPriority(useCase);
+ }
+ return mPriorityCongfig.getBackgroundPriority(useCase);
+ }
+
+ @VisibleForTesting
+ protected boolean isForeground(int pid) {
+ // TODO: how to get fg/bg information from pid
+ return true;
+ }
+
+ @VisibleForTesting
+ protected void reclaimFrontendResource(int reclaimingId) throws RemoteException {
+ if (mListeners.get(reclaimingId) != null) {
+ try {
+ mListeners.get(reclaimingId).onReclaimResources();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) {
+ FrontendResource grantingFrontend = getFrontendResource(grantingId);
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ grantingFrontend.setOwner(ownerClientId);
+ ownerProfile.useFrontend(grantingId);
+ for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
+ ownerProfile.useFrontend(exclusiveGroupMember);
+ }
+ }
+
+ /**
+ * Get the owner client's priority from the frontend id.
+ *
+ * @param frontendId an in use frontend id.
+ * @return the priority of the owner client of the frontend.
+ */
+ private int getOwnerClientPriority(int frontendId) {
+ return getClientProfile(getFrontendResource(frontendId).getOwnerClientId()).getPriority();
+ }
+
+ private ClientProfile getClientProfile(int clientId) {
+ return mClientProfiles.get(clientId);
+ }
+
+ protected FrontendResource getFrontendResource(int frontendId) {
+ return mFrontendResources.get(frontendId);
+ }
+
+ @VisibleForTesting
+ protected SparseArray<ClientProfile> getClientProfiles() {
+ return mClientProfiles;
+ }
+
+ @VisibleForTesting
+ protected SparseArray<FrontendResource> getFrontendResources() {
+ return mFrontendResources;
+ }
+
+ private boolean checkClientExists(int clientId) {
+ return mRegisteredClientIds.contains(clientId);
+ }
+
+ private void enforceAccessPermission() {
+ getContext().enforceCallingOrSelfPermission(
+ "android.permission.TUNER_RESOURCE_ACCESS", TAG);
+ }
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
new file mode 100644
index 000000000000..8c2de475b99b
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
@@ -0,0 +1,236 @@
+/*
+ * 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.media.tv.TvInputService;
+import android.media.tv.TvInputService.PriorityHintUseCaseType;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the Tuner Resource Manager use case priority hints config info including a
+ * parser that can read the xml config from the vendors.
+ *
+ * @hide
+ */
+public class UseCasePriorityHints {
+ private static final String TAG = "UseCasePriorityHints";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String PATH_TO_VENDOR_CONFIG_XML =
+ "/vendor/etc/tunerResourceManagerUseCaseConfig.xml";
+ private static final int INVALID_PRIORITY_VALUE = -1;
+ private static final int INVALID_USE_CASE = -1;
+
+ /**
+ * Array of the configured use case priority hints. Key is the use case id. Value is a size 2
+ * int array. The first element carries the priority of the use case on foreground. The second
+ * shows the background priority.
+ */
+ SparseArray<int[]> mPriorityHints = new SparseArray<>();
+
+ List<Integer> mVendorDefinedUseCase = new ArrayList<>();
+
+ private int mDefaultForeground = 150;
+ private int mDefaultBackground = 50;
+
+ int getForegroundPriority(int useCase) {
+ if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) {
+ return mPriorityHints.get(useCase)[0];
+ }
+ return mDefaultForeground;
+ }
+
+ int getBackgroundPriority(int useCase) {
+ if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) {
+ return mPriorityHints.get(useCase)[1];
+ }
+ return mDefaultBackground;
+ }
+
+ boolean isDefinedUseCase(int useCase) {
+ return (mVendorDefinedUseCase.contains(useCase) || isPredefinedUseCase(useCase));
+ }
+
+ /**
+ * To parse the vendor use case config.
+ */
+ public void parse() {
+ // Override the default priority with vendor setting if available.
+ File file = new File(PATH_TO_VENDOR_CONFIG_XML);
+ if (file.exists()) {
+ try {
+ InputStream in = new FileInputStream(file);
+ parseInternal(in);
+ return;
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading vendor file: " + file, e);
+ } catch (XmlPullParserException e) {
+ Slog.e(TAG, "Unable to parse vendor file: " + file, e);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "no vendor priority configuration available. Using default priority");
+ }
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, 180, 100);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN, 450, 200);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 480, 300);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 490, 400);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 600, 500);
+ }
+ }
+
+ // We don't use namespaces
+ private static final String NS = null;
+
+ @VisibleForTesting
+ protected void parseInternal(InputStream in)
+ throws IOException, XmlPullParserException {
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(in, null);
+ parser.nextTag();
+ readUseCase(parser);
+ in.close();
+ } catch (IOException | XmlPullParserException e) {
+ throw e;
+ }
+ for (int i = 0; i < mPriorityHints.size(); i++) {
+ int useCase = mPriorityHints.keyAt(i);
+ int[] priorities = mPriorityHints.get(useCase);
+ if (DEBUG) {
+ Slog.d(TAG, "{defaultFg=" + mDefaultForeground
+ + ", defaultBg=" + mDefaultBackground + "}");
+ Slog.d(TAG, "{useCase=" + useCase
+ + ", fg=" + priorities[0]
+ + ", bg=" + priorities[1]
+ + "}");
+ }
+ }
+ }
+
+ private void readUseCase(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ parser.require(XmlPullParser.START_TAG, NS, "config");
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ int useCase;
+ if (name.equals("useCaseDefault")) {
+ mDefaultForeground = readAttributeToInt("fgPriority", parser);
+ mDefaultBackground = readAttributeToInt("bgPriority", parser);
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, name);
+ } else if (name.equals("useCasePreDefined")) {
+ useCase = formatTypeToNum("type", parser);
+ if (useCase == INVALID_USE_CASE) {
+ Slog.e(TAG, "Wrong predefined use case name given in the vendor config.");
+ continue;
+ }
+ addNewUseCasePriority(useCase,
+ readAttributeToInt("fgPriority", parser),
+ readAttributeToInt("bgPriority", parser));
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, name);
+ } else if (name.equals("useCaseVendor")) {
+ useCase = readAttributeToInt("id", parser);
+ addNewUseCasePriority(useCase,
+ readAttributeToInt("fgPriority", parser),
+ readAttributeToInt("bgPriority", parser));
+ mVendorDefinedUseCase.add(useCase);
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, name);
+ } else {
+ skip(parser);
+ }
+ }
+ }
+
+ private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ throw new IllegalStateException();
+ }
+ int depth = 1;
+ while (depth != 0) {
+ switch (parser.next()) {
+ case XmlPullParser.END_TAG:
+ depth--;
+ break;
+ case XmlPullParser.START_TAG:
+ depth++;
+ break;
+ }
+ }
+ }
+
+ private int readAttributeToInt(String attributeName, XmlPullParser parser) {
+ return Integer.valueOf(parser.getAttributeValue(null, attributeName));
+ }
+
+ private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) {
+ int[] priorities = {fgPriority, bgPriority};
+ mPriorityHints.append(useCase, priorities);
+ }
+
+ @PriorityHintUseCaseType
+ private static int formatTypeToNum(String attributeName, XmlPullParser parser) {
+ String useCaseName = parser.getAttributeValue(null, attributeName);
+ switch (useCaseName) {
+ case "USE_CASE_BACKGROUND":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND;
+ case "USE_CASE_SCAN":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN;
+ case "USE_CASE_PLAYBACK":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK;
+ case "USE_CASE_LIVE":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE;
+ case "USE_CASE_RECORD":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD;
+ default:
+ return INVALID_USE_CASE;
+ }
+ }
+
+ private static boolean isPredefinedUseCase(int useCase) {
+ switch (useCase) {
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
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 000000000000..192c6fe4ab75
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -0,0 +1,497 @@
+/*
+ * 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 static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.media.tv.ITvInputManager;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.tuner.frontend.FrontendSettings;
+import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
+import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.RemoteException;
+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.Mock;
+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;
+ @Mock private ITvInputManager mITvInputManagerMock;
+ private TunerResourceManagerService mTunerResourceManagerService;
+ private int mReclaimingId;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ 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) {
+ @Override
+ protected void reclaimFrontendResource(int reclaimingId) {
+ mReclaimingId = reclaimingId;
+ }
+ };
+ mTunerResourceManagerService.onStart(true /*isForTesting*/);
+ mReclaimingId = -1;
+ }
+
+ @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<FrontendResource> 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_DVBS, 1 /*exclusiveGroupId*/);
+ infos[3] =
+ new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ SparseArray<FrontendResource> 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<Integer>());
+ assertThat(resources.get(1).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>(Arrays.asList(2, 3)));
+ assertThat(resources.get(2).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 3)));
+ assertThat(resources.get(3).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>(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_DVBS, 1 /*exclusiveGroupId*/);
+
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+ SparseArray<FrontendResource> resources0 =
+ mTunerResourceManagerService.getFrontendResources();
+
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+ SparseArray<FrontendResource> 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_DVBS, 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<FrontendResource> 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_DVBS, 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<FrontendResource> 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 requestFrontendTest_ClientNotRegistered() {
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ }
+
+ @Test
+ public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ }
+
+ @Test
+ public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
+ 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_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(0);
+ }
+
+ @Test
+ public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
+ ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile0, null /*listener*/, clientId0);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile1, null /*listener*/, clientId1);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
+ 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_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ int[] frontendId = new int[1];
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+
+ request =
+ new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[2].getId())
+ .isInUse()).isTrue();
+ }
+
+ @Test
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 50};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], null /*listener*/, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], null /*listener*/, clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // 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_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(mReclaimingId).isEqualTo(-1);
+
+ request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ assertThat(mReclaimingId).isEqualTo(-1);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Test
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 500};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], null /*listener*/, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], null /*listener*/, clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // 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_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+
+ request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources()
+ .get(infos[0].getId()).getOwnerClientId()).isEqualTo(clientId1[0]);
+ assertThat(mTunerResourceManagerService.getFrontendResources()
+ .get(infos[1].getId()).getOwnerClientId()).isEqualTo(clientId1[0]);
+ assertThat(mReclaimingId).isEqualTo(clientId0[0]);
+ }
+
+ @Test
+ public void unregisterClientTest_usingFrontend() {
+ // Register client
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // 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_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isTrue();
+
+ // Unregister client when using frontend
+ mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+ .isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isFalse();
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
new file mode 100644
index 000000000000..ab5665ba99fc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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 android.media.tv.TvInputService;
+import android.util.Slog;
+
+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.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UseCasePriorityHints} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class UseCasePriorityHintsTest {
+ private static final String TAG = "UseCasePriorityHintsTest";
+ private UseCasePriorityHints mPriorityHints;
+
+ private final String mExampleXML =
+ "<!-- A sample Use Case Priority Hints xml -->"
+ + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">"
+ + "<useCaseDefault fgPriority=\"150\" bgPriority=\"50\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_RECORD\" fgPriority=\"600\" bgPriority=\"500\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_LIVE\" fgPriority=\"490\" bgPriority=\"400\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_PLAYBACK\" fgPriority=\"480\""
+ + " bgPriority=\"300\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_BACKGROUND\" fgPriority=\"180\""
+ + " bgPriority=\"100\"/>"
+ + "<useCaseVendor type=\"VENDOR_USE_CASE_1\" id=\"1001\" fgPriority=\"300\""
+ + " bgPriority=\"80\"/>"
+ + "</config>";
+
+ @Before
+ public void setUp() throws Exception {
+ mPriorityHints = new UseCasePriorityHints();
+ try {
+ mPriorityHints.parseInternal(
+ new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8)));
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Error parse xml.", e);
+ }
+ }
+
+ @Test
+ public void parseTest_parseSampleXml() {
+ // Pre-defined foreground
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(180);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(150);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(480);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(490);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(600);
+
+ // Pre-defined background
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(100);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(50);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(300);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(400);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(500);
+
+ // Vendor use case
+ assertThat(mPriorityHints.getForegroundPriority(1001)).isEqualTo(300);
+ assertThat(mPriorityHints.getBackgroundPriority(1001)).isEqualTo(80);
+ }
+
+ @Test
+ public void isDefinedUseCaseTest_invalidUseCase() {
+ assertThat(mPriorityHints.isDefinedUseCase(1992)).isFalse();
+ }
+
+ @Test
+ public void isDefinedUseCaseTest_validUseCase() {
+ assertThat(mPriorityHints.isDefinedUseCase(1001)).isTrue();
+ assertThat(mPriorityHints.isDefinedUseCase(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isTrue();
+ }
+}