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

Commit abdf7b6a authored by Amy's avatar Amy
Browse files

Add a TRM use case priority hints info object

This object includes a parser that reads the vendor config xml

Test: atest UseCasePriorityHintsTest
Bug: 147380513
Change-Id: I4bff0bc196a0c2a78ce4305acec7ce7cc9a4fa70
(cherry picked from commit 2f62bf64)
parent 38ff0181
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ public class TunerResourceManagerService extends SystemService {
    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();
@@ -84,6 +85,7 @@ public class TunerResourceManagerService extends SystemService {
            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 {
@@ -409,14 +411,21 @@ public class TunerResourceManagerService extends SystemService {

    @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;
        if (isForeground(callingPid)) {
            return mPriorityCongfig.getForegroundPriority(useCase);
        }
        return mPriorityCongfig.getBackgroundPriority(useCase);
    }

    @VisibleForTesting
    protected boolean isForeground(int callingPid) {
        // TODO: how to get fg/bg information from pid
        return true;
    }

    @VisibleForTesting
+234 −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;
        }
    }
}
+100 −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 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();
    }

    @Test
    public void parseTest_parseSampleXml() {
        try {
            mPriorityHints.parseInternal(
                    new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8)));
        } catch (IOException | XmlPullParserException e) {
            Slog.e(TAG, "Error parse xml.", e);
        }

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