blob: 367b966a46edc553350ca35d44c2b71adc79ab24 [file] [log] [blame]
/*
* 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.HashSet;
import java.util.Set;
/**
* 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<>();
Set<Integer> mVendorDefinedUseCase = new HashSet<>();
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;
}
}
}