1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.tv.tunerresourcemanager; 17 18 import android.media.tv.TvInputService; 19 import android.media.tv.TvInputService.PriorityHintUseCaseType; 20 import android.util.Log; 21 import android.util.Slog; 22 import android.util.SparseArray; 23 import android.util.TypedXmlPullParser; 24 import android.util.Xml; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import org.xmlpull.v1.XmlPullParser; 29 import org.xmlpull.v1.XmlPullParserException; 30 31 import java.io.File; 32 import java.io.FileInputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.util.HashSet; 36 import java.util.Set; 37 38 /** 39 * This class provides the Tuner Resource Manager use case priority hints config info including a 40 * parser that can read the xml config from the vendors. 41 * 42 * @hide 43 */ 44 public class UseCasePriorityHints { 45 private static final String TAG = "UseCasePriorityHints"; 46 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 47 48 private static final String PATH_TO_VENDOR_CONFIG_XML = 49 "/vendor/etc/tunerResourceManagerUseCaseConfig.xml"; 50 private static final int INVALID_PRIORITY_VALUE = -1; 51 private static final int INVALID_USE_CASE = -1; 52 53 /** 54 * Array of the configured use case priority hints. Key is the use case id. Value is a size 2 55 * int array. The first element carries the priority of the use case on foreground. The second 56 * shows the background priority. 57 */ 58 SparseArray<int[]> mPriorityHints = new SparseArray<>(); 59 60 Set<Integer> mVendorDefinedUseCase = new HashSet<>(); 61 62 private int mDefaultForeground = 150; 63 private int mDefaultBackground = 50; 64 getForegroundPriority(int useCase)65 int getForegroundPriority(int useCase) { 66 if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) { 67 return mPriorityHints.get(useCase)[0]; 68 } 69 return mDefaultForeground; 70 } 71 getBackgroundPriority(int useCase)72 int getBackgroundPriority(int useCase) { 73 if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) { 74 return mPriorityHints.get(useCase)[1]; 75 } 76 return mDefaultBackground; 77 } 78 isDefinedUseCase(int useCase)79 boolean isDefinedUseCase(int useCase) { 80 return (mVendorDefinedUseCase.contains(useCase) || isPredefinedUseCase(useCase)); 81 } 82 83 /** 84 * To parse the vendor use case config. 85 */ parse()86 public void parse() { 87 // Override the default priority with vendor setting if available. 88 File file = new File(PATH_TO_VENDOR_CONFIG_XML); 89 if (file.exists()) { 90 try { 91 InputStream in = new FileInputStream(file); 92 parseInternal(in); 93 return; 94 } catch (IOException e) { 95 Slog.e(TAG, "Error reading vendor file: " + file, e); 96 } catch (XmlPullParserException e) { 97 Slog.e(TAG, "Unable to parse vendor file: " + file, e); 98 } 99 } else { 100 if (DEBUG) { 101 Slog.i(TAG, "no vendor priority configuration available. Using default priority"); 102 } 103 addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, 180, 100); 104 addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN, 450, 200); 105 addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 480, 300); 106 addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 490, 400); 107 addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 600, 500); 108 } 109 } 110 111 // We don't use namespaces 112 private static final String NS = null; 113 114 @VisibleForTesting parseInternal(InputStream in)115 protected void parseInternal(InputStream in) 116 throws IOException, XmlPullParserException { 117 try { 118 TypedXmlPullParser parser = Xml.resolvePullParser(in); 119 parser.nextTag(); 120 readUseCase(parser); 121 in.close(); 122 } catch (IOException | XmlPullParserException e) { 123 throw e; 124 } 125 for (int i = 0; i < mPriorityHints.size(); i++) { 126 int useCase = mPriorityHints.keyAt(i); 127 int[] priorities = mPriorityHints.get(useCase); 128 if (DEBUG) { 129 Slog.d(TAG, "{defaultFg=" + mDefaultForeground 130 + ", defaultBg=" + mDefaultBackground + "}"); 131 Slog.d(TAG, "{useCase=" + useCase 132 + ", fg=" + priorities[0] 133 + ", bg=" + priorities[1] 134 + "}"); 135 } 136 } 137 } 138 readUseCase(TypedXmlPullParser parser)139 private void readUseCase(TypedXmlPullParser parser) 140 throws XmlPullParserException, IOException { 141 parser.require(XmlPullParser.START_TAG, NS, "config"); 142 while (parser.next() != XmlPullParser.END_TAG) { 143 if (parser.getEventType() != XmlPullParser.START_TAG) { 144 continue; 145 } 146 String name = parser.getName(); 147 int useCase; 148 if (name.equals("useCaseDefault")) { 149 mDefaultForeground = readAttributeToInt("fgPriority", parser); 150 mDefaultBackground = readAttributeToInt("bgPriority", parser); 151 parser.nextTag(); 152 parser.require(XmlPullParser.END_TAG, NS, name); 153 } else if (name.equals("useCasePreDefined")) { 154 useCase = formatTypeToNum("type", parser); 155 if (useCase == INVALID_USE_CASE) { 156 Slog.e(TAG, "Wrong predefined use case name given in the vendor config."); 157 continue; 158 } 159 addNewUseCasePriority(useCase, 160 readAttributeToInt("fgPriority", parser), 161 readAttributeToInt("bgPriority", parser)); 162 parser.nextTag(); 163 parser.require(XmlPullParser.END_TAG, NS, name); 164 } else if (name.equals("useCaseVendor")) { 165 useCase = readAttributeToInt("id", parser); 166 addNewUseCasePriority(useCase, 167 readAttributeToInt("fgPriority", parser), 168 readAttributeToInt("bgPriority", parser)); 169 mVendorDefinedUseCase.add(useCase); 170 parser.nextTag(); 171 parser.require(XmlPullParser.END_TAG, NS, name); 172 } else { 173 skip(parser); 174 } 175 } 176 } 177 skip(TypedXmlPullParser parser)178 private void skip(TypedXmlPullParser parser) throws XmlPullParserException, IOException { 179 if (parser.getEventType() != XmlPullParser.START_TAG) { 180 throw new IllegalStateException(); 181 } 182 int depth = 1; 183 while (depth != 0) { 184 switch (parser.next()) { 185 case XmlPullParser.END_TAG: 186 depth--; 187 break; 188 case XmlPullParser.START_TAG: 189 depth++; 190 break; 191 } 192 } 193 } 194 readAttributeToInt(String attributeName, TypedXmlPullParser parser)195 private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) 196 throws XmlPullParserException { 197 return parser.getAttributeInt(null, attributeName); 198 } 199 addNewUseCasePriority(int useCase, int fgPriority, int bgPriority)200 private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) { 201 int[] priorities = {fgPriority, bgPriority}; 202 mPriorityHints.append(useCase, priorities); 203 } 204 205 @PriorityHintUseCaseType formatTypeToNum(String attributeName, TypedXmlPullParser parser)206 private static int formatTypeToNum(String attributeName, TypedXmlPullParser parser) { 207 String useCaseName = parser.getAttributeValue(null, attributeName); 208 switch (useCaseName) { 209 case "USE_CASE_BACKGROUND": 210 return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND; 211 case "USE_CASE_SCAN": 212 return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN; 213 case "USE_CASE_PLAYBACK": 214 return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK; 215 case "USE_CASE_LIVE": 216 return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE; 217 case "USE_CASE_RECORD": 218 return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD; 219 default: 220 return INVALID_USE_CASE; 221 } 222 } 223 isPredefinedUseCase(int useCase)224 private static boolean isPredefinedUseCase(int useCase) { 225 switch (useCase) { 226 case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND: 227 case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN: 228 case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK: 229 case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE: 230 case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD: 231 return true; 232 default: 233 return false; 234 } 235 } 236 } 237