Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38ff0181 authored by Amy's avatar Amy
Browse files

Add requestFrontend implementation in TunerResourceManager

Test: atest TunerResourceManagerServiceTest
Bug: 147380513
Change-Id: Icec048913e2cbe141448c0c322d37c233588dfcd
(cherry picked from commit 9d150f58)
parent 9796ba2f
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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) {
+207 −69
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ public class TunerResourceManagerService extends SystemService {
    private static final String TAG = "TunerResourceManagerService";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    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;
@@ -56,7 +59,6 @@ public class TunerResourceManagerService extends SystemService {
    // Array of the current available frontend resources
    @VisibleForTesting
    private SparseArray<FrontendResource> mFrontendResources = new SparseArray<>();
    @VisibleForTesting private SparseArray<Integer[]> mFrontendTypeMap = new SparseArray<>();
    // Array of the current available frontend ids
    private List<Integer> mAvailableFrontendIds = new ArrayList<Integer>();

@@ -73,40 +75,34 @@ public class TunerResourceManagerService extends SystemService {

    @Override
    public void onStart() {
        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);
    }

    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] = mNextUnusedClientId++;
            } else {
                clientId[0] = mReleasedClientId.get(0);
                mReleasedClientId.remove(0);
            if (clientId == null || clientId.length != 1) {
                throw new RemoteException("clientId must be a size 1 array!");
            }

            if (mManager == null) {
                Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
                return;
            synchronized (mLock) {
                registerClientProfileInternal(profile, listener, clientId);
            }

            int callingPid = mManager.getClientPid(profile.getTvInputSessionId());

            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);
        }

        @Override
@@ -122,24 +118,11 @@ 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 + ")");
            }
            enforceAccessPermission();

            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;
            synchronized (mLock) {
                return updateClientPriorityInternal(clientId, priority, niceValue);
            }

            profile.setPriority(priority);
            profile.setNiceValue(niceValue);

            return true;
        }

        @Override
@@ -174,28 +157,17 @@ 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 + ")");
            enforceAccessPermission();
            if (frontendId == null) {
                throw new RemoteException("frontendId can't be null");
            }

            frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;

            if (mClientProfiles.get(request.getClientId()) == null) {
                Slog.e(TAG, "Request from unregistered client. Id: " + request.getClientId());
                return false;
            synchronized (mLock) {
                try {
                    return requestFrontendInternal(request, frontendId);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }

            String sessionId = mClientProfiles.get(request.getClientId()).getTvInputSessionId();

            if (DEBUG) {
                Slog.d(TAG, "session Id:" + sessionId + ")");
            }

            if (DEBUG) {
                Slog.d(TAG, "No available Frontend found.");
            }

            return false;
        }

        @Override
@@ -256,6 +228,61 @@ public class TunerResourceManagerService extends SystemService {
        }
    }

    @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
        if (mReleasedClientId.isEmpty()) {
            clientId[0] = mNextUnusedClientId++;
        } else {
            clientId[0] = mReleasedClientId.get(0);
            mReleasedClientId.remove(0);
        }

        int callingPid = mManager.getClientPid(profile.getTvInputSessionId());

        ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
                                              .tvInputSessionId(profile.getTvInputSessionId())
                                              .useCase(profile.getUseCase())
                                              .processId(callingPid)
                                              .build();
        clientProfile.setPriority(getClientPriority(profile.getUseCase(), callingPid));

        mClientProfiles.append(clientId[0], clientProfile);
        mListeners.append(clientId[0], listener);
    }

    @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) {
@@ -273,7 +300,7 @@ public class TunerResourceManagerService extends SystemService {

        // Update frontendResources sparse array and other mappings accordingly
        for (int i = 0; i < infos.length; i++) {
            if (mFrontendResources.get(infos[i].getId()) != null) {
            if (getFrontendResource(infos[i].getId()) != null) {
                if (DEBUG) {
                    Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists.");
                }
@@ -286,12 +313,12 @@ public class TunerResourceManagerService extends SystemService {
                                                 .build();
                // Update the exclusive group member list in all the existing Frontend resource
                for (Integer feId : mAvailableFrontendIds) {
                    FrontendResource fe = mFrontendResources.get(feId.intValue());
                    FrontendResource fe = getFrontendResource(feId.intValue());
                    if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
                        newFe.addExclusiveGroupMemberFeId(fe.getId());
                        newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds());
                        for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
                            mFrontendResources.get(excGroupmemberFeId.intValue())
                            getFrontendResource(excGroupmemberFeId.intValue())
                                    .addExclusiveGroupMemberFeId(newFe.getId());
                        }
                        fe.addExclusiveGroupMemberFeId(newFe.getId());
@@ -307,20 +334,131 @@ public class TunerResourceManagerService extends SystemService {
        // 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());
            FrontendResource fe = getFrontendResource(removingId.intValue());
            fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
            for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
                mFrontendResources.get(excGroupmemberFeId.intValue())
                getFrontendResource(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 boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
            throws RemoteException {
        if (DEBUG) {
            Slog.d(TAG, "requestFrontend(request=" + request + ")");
        }

        frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
        ClientProfile requestClient = getClientProfile(request.getClientId());
        if (requestClient == null) {
            Slog.e(TAG, "Request from unregistered client. Id: " + request.getClientId());
            return false;
        }

        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;
            updateFrontendResourcesOnNewGrant(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());
            updateFrontendResourcesOnNewGrant(frontendId[0], request.getClientId());
            return true;
        }

        return false;
    }

    @VisibleForTesting
    protected int getClientPriority(int useCase, int callingPid) {
        // TODO: how to get fg/bg information from pid
        if (DEBUG) {
            Slog.d(TAG, "getClientPriority useCase=" + useCase
                    + ", calling Pid=" + callingPid + ")");
        }

        // TODO: get priority from the table built from the useCase config xml
        return 0;
    }

    @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 updateFrontendResourcesOnNewGrant(int grantingId, int ownerClientId) {
        FrontendResource grantingFrontend = getFrontendResource(grantingId);
        grantingFrontend.setOwner(ownerClientId);
        for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) {
            getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
        }
    }

    /**
     * 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
+271 −6

File changed.

Preview size limit exceeded, changes collapsed.