From 2f62bf64ca8be21d6cc222f061250b0ef56b2ffe Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 19 Feb 2020 19:40:30 -0800 Subject: [PATCH] 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 --- .../TunerResourceManagerService.java | 15 +- .../UseCasePriorityHints.java | 234 ++++++++++++++++++ .../UseCasePriorityHintsTest.java | 100 ++++++++ 3 files changed, 346 insertions(+), 3 deletions(-) create mode 100644 services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java create mode 100644 services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java 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 ede620257dc47..621f2b024e843 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -65,6 +65,7 @@ public class TunerResourceManagerService extends SystemService { private SparseArray 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 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 0000000000000..e6d672d9acbc5 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -0,0 +1,234 @@ +/* + * 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 mPriorityHints = new SparseArray<>(); + + List 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/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java new file mode 100644 index 0000000000000..8a75d5c2a3804 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java @@ -0,0 +1,100 @@ +/* + * 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 = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + @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); + } +}