TvInputInfo.java revision 4350a6210fac21311a412f77cf56963d468b1371
1/*
2 * Copyright (C) 2014 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
17package android.media.tv;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.PackageManager;
23import android.content.pm.PackageManager.NameNotFoundException;
24import android.content.pm.ResolveInfo;
25import android.content.pm.ServiceInfo;
26import android.content.res.Resources;
27import android.content.res.TypedArray;
28import android.content.res.XmlResourceParser;
29import android.graphics.drawable.Drawable;
30import android.hardware.hdmi.HdmiCecDeviceInfo;
31import android.os.Parcel;
32import android.os.Parcelable;
33import android.text.TextUtils;
34import android.util.AttributeSet;
35import android.util.Log;
36import android.util.Xml;
37
38import org.xmlpull.v1.XmlPullParser;
39import org.xmlpull.v1.XmlPullParserException;
40
41import java.io.IOException;
42
43/**
44 * This class is used to specify meta information of a TV input.
45 */
46public final class TvInputInfo implements Parcelable {
47    private static final boolean DEBUG = false;
48    private static final String TAG = "TvInputInfo";
49
50    /**
51     * TV input type: the TV input service is not handling input from hardware. For example,
52     * services showing streaming from the internet falls into this type.
53     */
54    public static final int TYPE_VIRTUAL = 0;
55
56    // Should be in sync with TvInputHardwareInfo.
57
58    /**
59     * TV input type: a generic hardware TV input type.
60     */
61    public static final int TYPE_OTHER_HARDWARE = TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE;
62    /**
63     * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner)
64     */
65    public static final int TYPE_TUNER = TvInputHardwareInfo.TV_INPUT_TYPE_TUNER;
66    /**
67     * TV input type: the TV input service represents a composite port.
68     */
69    public static final int TYPE_COMPOSITE = TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE;
70    /**
71     * TV input type: the TV input service represents a SVIDEO port.
72     */
73    public static final int TYPE_SVIDEO = TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO;
74    /**
75     * TV input type: the TV input service represents a SCART port.
76     */
77    public static final int TYPE_SCART = TvInputHardwareInfo.TV_INPUT_TYPE_SCART;
78    /**
79     * TV input type: the TV input service represents a component port.
80     */
81    public static final int TYPE_COMPONENT = TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT;
82    /**
83     * TV input type: the TV input service represents a VGA port.
84     */
85    public static final int TYPE_VGA = TvInputHardwareInfo.TV_INPUT_TYPE_VGA;
86    /**
87     * TV input type: the TV input service represents a DVI port.
88     */
89    public static final int TYPE_DVI = TvInputHardwareInfo.TV_INPUT_TYPE_DVI;
90    /**
91     * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
92     */
93    public static final int TYPE_HDMI = TvInputHardwareInfo.TV_INPUT_TYPE_HDMI;
94    /**
95     * TV input type: the TV input service represents a display port.
96     */
97    public static final int TYPE_DISPLAY_PORT = TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT;
98
99    /**
100     * The ID of the TV input to provide to the setup activity and settings activity.
101     */
102    public static final String EXTRA_INPUT_ID = "inputId";
103
104    private static final String XML_START_TAG_NAME = "tv-input";
105
106    private final ResolveInfo mService;
107    private final String mId;
108    private final String mParentId;
109
110    // Attributes from XML meta data.
111    private String mSetupActivity;
112    private String mSettingsActivity;
113    private int mType = TYPE_VIRTUAL;
114
115    /**
116     * Create a new instance of the TvInputInfo class,
117     * instantiating it from the given Context and ResolveInfo.
118     *
119     * @param service The ResolveInfo returned from the package manager about this TV input service.
120     * @hide
121     */
122    public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service)
123            throws XmlPullParserException, IOException {
124        return createTvInputInfo(context, service, generateInputIdForComponentName(
125                new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)));
126    }
127
128    /**
129     * Create a new instance of the TvInputInfo class,
130     * instantiating it from the given Context, ResolveInfo, and HdmiCecDeviceInfo.
131     *
132     * @param service The ResolveInfo returned from the package manager about this TV input service.
133     * @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device.
134     * @hide
135     */
136    public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
137            HdmiCecDeviceInfo cecInfo) throws XmlPullParserException, IOException {
138        return createTvInputInfo(context, service, generateInputIdForHdmiCec(
139                new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
140                cecInfo));
141    }
142
143    /**
144     * Create a new instance of the TvInputInfo class,
145     * instantiating it from the given Context, ResolveInfo, and TvInputHardwareInfo.
146     *
147     * @param service The ResolveInfo returned from the package manager about this TV input service.
148     * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
149     * @hide
150     */
151    public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
152            TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException {
153        return createTvInputInfo(context, service, generateInputIdForHardware(
154                new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
155                hardwareInfo));
156    }
157
158    private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
159            String id) throws XmlPullParserException, IOException {
160        ServiceInfo si = service.serviceInfo;
161        PackageManager pm = context.getPackageManager();
162        XmlResourceParser parser = null;
163        try {
164            parser = si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
165            if (parser == null) {
166                throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
167                        + " meta-data for " + si.name);
168            }
169
170            Resources res = pm.getResourcesForApplication(si.applicationInfo);
171            AttributeSet attrs = Xml.asAttributeSet(parser);
172
173            int type;
174            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
175                    && type != XmlPullParser.START_TAG) {
176            }
177
178            String nodeName = parser.getName();
179            if (!XML_START_TAG_NAME.equals(nodeName)) {
180                throw new XmlPullParserException(
181                        "Meta-data does not start with tv-input-service tag in " + si.name);
182            }
183
184            TvInputInfo input = new TvInputInfo(context, service, id, null);
185            TypedArray sa = res.obtainAttributes(attrs,
186                    com.android.internal.R.styleable.TvInputService);
187            input.mSetupActivity = sa.getString(
188                    com.android.internal.R.styleable.TvInputService_setupActivity);
189            if (DEBUG) {
190                Log.d(TAG, "Setup activity loaded. [" + input.mSetupActivity + "] for " + si.name);
191            }
192            input.mSettingsActivity = sa.getString(
193                    com.android.internal.R.styleable.TvInputService_settingsActivity);
194            if (DEBUG) {
195                Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
196                        + si.name);
197            }
198            if (pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, si.packageName)
199                    == PackageManager.PERMISSION_GRANTED) {
200                input.mType = sa.getInt(
201                        com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
202                if (DEBUG) {
203                    Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
204                }
205            }
206            sa.recycle();
207
208            return input;
209        } catch (NameNotFoundException e) {
210            throw new XmlPullParserException("Unable to create context for: " + si.packageName);
211        } finally {
212            if (parser != null) {
213                parser.close();
214            }
215        }
216    }
217
218    /**
219     * Constructor.
220     *
221     * @param service The ResolveInfo returned from the package manager about this TV input service.
222     * @param id ID of this TV input. Should be generated via generateInputId*().
223     * @param parentId ID of this TV input's parent input. {@code null} if none exists.
224     */
225    private TvInputInfo(Context context, ResolveInfo service, String id, String parentId) {
226        mService = service;
227        ServiceInfo si = service.serviceInfo;
228        mId = id;
229        mParentId = parentId;
230    }
231
232    /**
233     * Returns a unique ID for this TV input. The ID is generated from the package and class name
234     * implementing the TV input service.
235     */
236    public String getId() {
237        return mId;
238    }
239
240    /**
241     * Returns the parent input ID.
242     * <p>
243     * A TV input may have a parent input if the TV input is actually a logical representation of
244     * a device behind the hardware port represented by the parent input.
245     * For example, a HDMI CEC logical device, connected to a HDMI port, appears as another TV
246     * input. In this case, the parent input of this logical device is the HDMI port.
247     * </p><p>
248     * Applications may group inputs by parent input ID to provide an easier access to inputs
249     * sharing the same physical port. In the example of HDMI CEC, logical HDMI CEC devices behind
250     * the same HDMI port have the same parent ID, which is the ID representing the port. Thus
251     * applications can group the hardware HDMI port and the logical HDMI CEC devices behind it
252     * together using this method.
253     * </p>
254     *
255     * @return the ID of the parent input, if exists. Returns {@code null} if the parent input is
256     *         not specified.
257     */
258    public String getParentId() {
259        return mParentId;
260    }
261
262    /**
263     * Returns the information of the service that implements this TV input.
264     */
265    public ServiceInfo getServiceInfo() {
266        return mService.serviceInfo;
267    }
268
269    /**
270     * Returns the component of the service that implements this TV input.
271     * @hide
272     */
273    public ComponentName getComponent() {
274        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
275    }
276
277    /**
278     * Returns an intent to start the setup activity for this TV input service.
279     */
280    public Intent getIntentForSetupActivity() {
281        if (!TextUtils.isEmpty(mSetupActivity)) {
282            Intent intent = new Intent(Intent.ACTION_MAIN);
283            intent.setClassName(mService.serviceInfo.packageName, mSetupActivity);
284            intent.putExtra(EXTRA_INPUT_ID, getId());
285            return intent;
286        }
287        return null;
288    }
289
290    /**
291     * Returns an intent to start the settings activity for this TV input service.
292     */
293    public Intent getIntentForSettingsActivity() {
294        if (!TextUtils.isEmpty(mSettingsActivity)) {
295            Intent intent = new Intent(Intent.ACTION_MAIN);
296            intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity);
297            intent.putExtra(EXTRA_INPUT_ID, getId());
298            return intent;
299        }
300        return null;
301    }
302
303    /**
304     * Returns the type of this TV input service.
305     */
306    public int getType() {
307        return mType;
308    }
309
310    /**
311     * Returns {@code true} if this TV input is pass-though which does not have any real channels
312     * in TvProvider. {@code false} otherwise.
313     *
314     * @see TvContract#buildChannelUriForPassthroughTvInput(String)
315     */
316    public boolean isPassthroughInputType() {
317        if (mType == TYPE_HDMI || mType == TYPE_DISPLAY_PORT || mType == TYPE_SCART
318                || mType == TYPE_DVI || mType == TYPE_VGA || mType == TYPE_COMPONENT
319                || mType == TYPE_COMPOSITE || mType == TYPE_SVIDEO) {
320            return true;
321        }
322        return false;
323    }
324
325    /**
326     * Loads the user-displayed label for this TV input service.
327     *
328     * @param context Supplies a {@link Context} used to load the label.
329     * @return a CharSequence containing the TV input's label. If the TV input does not have
330     *         a label, its name is returned.
331     */
332    public CharSequence loadLabel(Context context) {
333        return mService.loadLabel(context.getPackageManager());
334    }
335
336    /**
337     * Loads the user-displayed icon for this TV input service.
338     *
339     * @param context Supplies a {@link Context} used to load the icon.
340     * @return a Drawable containing the TV input's icon. If the TV input does not have
341     *         an icon, application icon is returned. If it's unavailable too, system default is
342     *         returned.
343     */
344    public Drawable loadIcon(Context context) {
345        return mService.serviceInfo.loadIcon(context.getPackageManager());
346    }
347
348    @Override
349    public int describeContents() {
350        return 0;
351    }
352
353    @Override
354    public int hashCode() {
355        return mId.hashCode();
356    }
357
358    @Override
359    public boolean equals(Object o) {
360        if (o == this) {
361            return true;
362        }
363
364        if (!(o instanceof TvInputInfo)) {
365            return false;
366        }
367
368        TvInputInfo obj = (TvInputInfo) o;
369        return mId.equals(obj.mId);
370    }
371
372    @Override
373    public String toString() {
374        return "TvInputInfo{id=" + mId
375                + ", pkg=" + mService.serviceInfo.packageName
376                + ", service=" + mService.serviceInfo.name + "}";
377    }
378
379    /**
380     * Used to package this object into a {@link Parcel}.
381     *
382     * @param dest The {@link Parcel} to be written.
383     * @param flags The flags used for parceling.
384     */
385    @Override
386    public void writeToParcel(Parcel dest, int flags) {
387        dest.writeString(mId);
388        dest.writeString(mParentId);
389        mService.writeToParcel(dest, flags);
390        dest.writeString(mSetupActivity);
391        dest.writeString(mSettingsActivity);
392        dest.writeInt(mType);
393    }
394
395    /**
396     * Used to generate an input id from a ComponentName.
397     *
398     * @param name the component name for generating an input id.
399     * @return the generated input id for the given {@code name}.
400     */
401    private static final String generateInputIdForComponentName(ComponentName name) {
402        return name.flattenToShortString();
403    }
404
405    /**
406     * Used to generate an input id from a ComponentName and HdmiCecDeviceInfo.
407     *
408     * @param name the component name for generating an input id.
409     * @param cecInfo HdmiCecDeviceInfo describing this TV input.
410     * @return the generated input id for the given {@code name} and {@code cecInfo}.
411     */
412    private static final String generateInputIdForHdmiCec(
413            ComponentName name, HdmiCecDeviceInfo cecInfo) {
414        return name.flattenToShortString() + String.format("|CEC%08X%08X",
415                cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress());
416    }
417
418    /**
419     * Used to generate an input id from a ComponentName and TvInputHardwareInfo
420     *
421     * @param name the component name for generating an input id.
422     * @param hardwareInfo TvInputHardwareInfo describing this TV input.
423     * @return the generated input id for the given {@code name} and {@code hardwareInfo}.
424     */
425    private static final String generateInputIdForHardware(
426            ComponentName name, TvInputHardwareInfo hardwareInfo) {
427        return name.flattenToShortString() + String.format("|HW%d", hardwareInfo.getDeviceId());
428    }
429
430    /**
431     * Used to make this class parcelable.
432     *
433     * @hide
434     */
435    public static final Parcelable.Creator<TvInputInfo> CREATOR =
436            new Parcelable.Creator<TvInputInfo>() {
437        @Override
438        public TvInputInfo createFromParcel(Parcel in) {
439            return new TvInputInfo(in);
440        }
441
442        @Override
443        public TvInputInfo[] newArray(int size) {
444            return new TvInputInfo[size];
445        }
446    };
447
448    private TvInputInfo(Parcel in) {
449        mId = in.readString();
450        mParentId = in.readString();
451        mService = ResolveInfo.CREATOR.createFromParcel(in);
452        mSetupActivity = in.readString();
453        mSettingsActivity = in.readString();
454        mType = in.readInt();
455    }
456}
457