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