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

Commit b33aa9d0 authored by Amy Zhang's avatar Amy Zhang Committed by Android (Google) Code Review
Browse files

Merge changes from topic "trm-impl"

* changes:
  Fix issues in register/unregister client API implementation
  Add a TRM use case priority hints info object
  Add requestFrontend implementation in TunerResourceManager
  Implement TRM setFrontendInfoList logic with unit tests
parents 4c5cb336 19b71275
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) {
+49 −53
Original line number Diff line number Diff line
@@ -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;

@@ -58,6 +62,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.
     *
@@ -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}.
          *
+204 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
+354 −105

File changed.

Preview size limit exceeded, changes collapsed.

+236 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
Loading