1 /*
2  * Copyright (C) 2018 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.tv.common.compat;
17 
18 import android.content.Context;
19 import android.content.pm.PackageManager;
20 import android.content.pm.ServiceInfo;
21 import android.media.tv.TvInputInfo;
22 import android.media.tv.TvInputService;
23 import android.support.annotation.VisibleForTesting;
24 import android.util.Log;
25 
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Map;
29 
30 import org.xmlpull.v1.XmlPullParser;
31 
32 /**
33  * TIF Compatibility for {@link TvInputInfo}.
34  */
35 public class TvInputInfoCompat {
36     private static final String TAG = "TvInputInfoCompat";
37     private static final String ATTRIBUTE_NAMESPACE_ANDROID =
38             "http://schemas.android.com/apk/res/android";
39     private static final String TV_INPUT_XML_START_TAG_NAME = "tv-input";
40     private static final String TV_INPUT_EXTRA_XML_START_TAG_NAME = "extra";
41     private static final String ATTRIBUTE_NAME = "name";
42     private static final String ATTRIBUTE_VALUE = "value";
43     private static final String ATTRIBUTE_NAME_AUDIO_ONLY =
44             "com.android.tv.common.compat.tvinputinfocompat.audioOnly";
45 
46     private final Context mContext;
47     private final TvInputInfo mTvInputInfo;
48     private boolean mAudioOnly;
49     private boolean mAudioAttributeInit = false;
50 
TvInputInfoCompat(Context context, TvInputInfo tvInputInfo)51     public TvInputInfoCompat(Context context, TvInputInfo tvInputInfo) {
52         mContext = context;
53         mTvInputInfo = tvInputInfo;
54     }
55 
getTvInputInfo()56     public TvInputInfo getTvInputInfo() {
57         return mTvInputInfo;
58     }
59 
isAudioOnly()60     public boolean isAudioOnly() {
61         // TODO(b/112938832): use tvInputInfo.isAudioOnly() when SDK is updated
62         if (!mAudioAttributeInit) {
63             mAudioOnly = Boolean.parseBoolean(getExtras().get(ATTRIBUTE_NAME_AUDIO_ONLY));
64             mAudioAttributeInit = true;
65         }
66         return mAudioOnly;
67     }
68 
getType()69     public int getType() {
70         return mTvInputInfo.getType();
71     }
72 
73     @VisibleForTesting
getExtras()74     public Map<String, String> getExtras() {
75         ServiceInfo si = mTvInputInfo.getServiceInfo();
76 
77         try {
78             XmlPullParser parser = getXmlResourceParser();
79             int type;
80             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
81                     && type != XmlPullParser.START_TAG) {
82             }
83 
84             if (!TV_INPUT_XML_START_TAG_NAME.equals(parser.getName())) {
85                 Log.w(TAG, "Meta-data does not start with " + TV_INPUT_XML_START_TAG_NAME
86                         + " tag for " + si.name);
87                 return Collections.emptyMap();
88             }
89             // <tv-input> start tag found
90             Map<String, String> extras = new HashMap<>();
91             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
92                 if (type == XmlPullParser.END_TAG
93                         && TV_INPUT_XML_START_TAG_NAME.equals(parser.getName())) {
94                     // </tv-input> end tag found
95                     return extras;
96                 }
97                 if (type == XmlPullParser.START_TAG
98                         && TV_INPUT_EXTRA_XML_START_TAG_NAME.equals(parser.getName())) {
99                     String extraName =
100                             parser.getAttributeValue(ATTRIBUTE_NAMESPACE_ANDROID, ATTRIBUTE_NAME);
101                     String extraValue =
102                             parser.getAttributeValue(ATTRIBUTE_NAMESPACE_ANDROID, ATTRIBUTE_VALUE);
103                     if (extraName != null && extraValue != null) {
104                         extras.put(extraName, extraValue);
105                     }
106                 }
107             }
108 
109         } catch (Exception e) {
110             Log.e(TAG, "Failed to get extras of " + mTvInputInfo.getId() , e);
111         }
112         return Collections.emptyMap();
113     }
114 
115     @VisibleForTesting
getXmlResourceParser()116     XmlPullParser getXmlResourceParser() {
117         ServiceInfo si = mTvInputInfo.getServiceInfo();
118         PackageManager pm = mContext.getPackageManager();
119         return si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
120     }
121 }
122