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
This commit is contained in:
Amy
2020-02-19 19:40:30 -08:00
parent 9d150f58bd
commit 2f62bf64ca
3 changed files with 346 additions and 3 deletions

View File

@@ -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

View File

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

View File

@@ -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 =
"<!-- 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);
}
}