Home | History | Annotate | Download | only in tv
      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 
     17 package android.media.tv;
     18 
     19 import android.annotation.SystemApi;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.pm.PackageManager;
     24 import android.content.pm.PackageManager.NameNotFoundException;
     25 import android.content.pm.ResolveInfo;
     26 import android.content.pm.ServiceInfo;
     27 import android.content.res.Resources;
     28 import android.content.res.TypedArray;
     29 import android.content.res.XmlResourceParser;
     30 import android.graphics.drawable.Drawable;
     31 import android.hardware.hdmi.HdmiDeviceInfo;
     32 import android.net.Uri;
     33 import android.os.Parcel;
     34 import android.os.Parcelable;
     35 import android.os.UserHandle;
     36 import android.provider.Settings;
     37 import android.text.TextUtils;
     38 import android.util.AttributeSet;
     39 import android.util.Log;
     40 import android.util.SparseIntArray;
     41 import android.util.Xml;
     42 
     43 import org.xmlpull.v1.XmlPullParser;
     44 import org.xmlpull.v1.XmlPullParserException;
     45 
     46 import java.io.IOException;
     47 import java.io.InputStream;
     48 import java.util.HashMap;
     49 import java.util.HashSet;
     50 import java.util.Map;
     51 import java.util.Set;
     52 
     53 /**
     54  * This class is used to specify meta information of a TV input.
     55  */
     56 public final class TvInputInfo implements Parcelable {
     57     private static final boolean DEBUG = false;
     58     private static final String TAG = "TvInputInfo";
     59 
     60     // Should be in sync with frameworks/base/core/res/res/values/attrs.xml
     61     /**
     62      * TV input type: the TV input service is a tuner which provides channels.
     63      */
     64     public static final int TYPE_TUNER = 0;
     65     /**
     66      * TV input type: a generic hardware TV input type.
     67      */
     68     public static final int TYPE_OTHER = 1000;
     69     /**
     70      * TV input type: the TV input service represents a composite port.
     71      */
     72     public static final int TYPE_COMPOSITE = 1001;
     73     /**
     74      * TV input type: the TV input service represents a SVIDEO port.
     75      */
     76     public static final int TYPE_SVIDEO = 1002;
     77     /**
     78      * TV input type: the TV input service represents a SCART port.
     79      */
     80     public static final int TYPE_SCART = 1003;
     81     /**
     82      * TV input type: the TV input service represents a component port.
     83      */
     84     public static final int TYPE_COMPONENT = 1004;
     85     /**
     86      * TV input type: the TV input service represents a VGA port.
     87      */
     88     public static final int TYPE_VGA = 1005;
     89     /**
     90      * TV input type: the TV input service represents a DVI port.
     91      */
     92     public static final int TYPE_DVI = 1006;
     93     /**
     94      * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
     95      */
     96     public static final int TYPE_HDMI = 1007;
     97     /**
     98      * TV input type: the TV input service represents a display port.
     99      */
    100     public static final int TYPE_DISPLAY_PORT = 1008;
    101 
    102     /**
    103      * The ID of the TV input to provide to the setup activity and settings activity.
    104      */
    105     public static final String EXTRA_INPUT_ID = "android.media.tv.extra.INPUT_ID";
    106 
    107     private static SparseIntArray sHardwareTypeToTvInputType = new SparseIntArray();
    108 
    109     private static final String XML_START_TAG_NAME = "tv-input";
    110     private static final String DELIMITER_INFO_IN_ID = "/";
    111     private static final String PREFIX_HDMI_DEVICE = "HDMI";
    112     private static final String PREFIX_HARDWARE_DEVICE = "HW";
    113     private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4;
    114     private static final int LENGTH_HDMI_DEVICE_ID = 2;
    115 
    116     private final ResolveInfo mService;
    117     private final String mId;
    118     private final String mParentId;
    119 
    120     // Attributes from XML meta data.
    121     private String mSetupActivity;
    122     private String mSettingsActivity;
    123 
    124     private int mType = TYPE_TUNER;
    125     private HdmiDeviceInfo mHdmiDeviceInfo;
    126     private String mLabel;
    127     private Uri mIconUri;
    128     private boolean mIsConnectedToHdmiSwitch;
    129 
    130     static {
    131         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
    132                 TYPE_OTHER);
    133         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_TUNER, TYPE_TUNER);
    134         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE, TYPE_COMPOSITE);
    135         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO, TYPE_SVIDEO);
    136         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SCART, TYPE_SCART);
    137         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT, TYPE_COMPONENT);
    138         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_VGA, TYPE_VGA);
    139         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DVI, TYPE_DVI);
    140         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI, TYPE_HDMI);
    141         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT,
    142                 TYPE_DISPLAY_PORT);
    143     }
    144 
    145     /**
    146      * Create a new instance of the TvInputInfo class,
    147      * instantiating it from the given Context and ResolveInfo.
    148      *
    149      * @param service The ResolveInfo returned from the package manager about this TV input service.
    150      * @hide
    151      */
    152     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service)
    153             throws XmlPullParserException, IOException {
    154         return createTvInputInfo(context, service, generateInputIdForComponentName(
    155                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)),
    156                 null, TYPE_TUNER, null, null, false);
    157     }
    158 
    159     /**
    160      * Create a new instance of the TvInputInfo class, instantiating it from the given Context,
    161      * ResolveInfo, and HdmiDeviceInfo.
    162      *
    163      * @param service The ResolveInfo returned from the package manager about this TV input service.
    164      * @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
    165      * @param parentId The ID of this TV input's parent input. {@code null} if none exists.
    166      * @param iconUri The {@link android.net.Uri} to load the icon image. See
    167      *            {@link android.content.ContentResolver#openInputStream}. If it is {@code null},
    168      *            the application icon of {@code service} will be loaded.
    169      * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service}
    170      *            label will be loaded.
    171      * @hide
    172      */
    173     @SystemApi
    174     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
    175             HdmiDeviceInfo hdmiDeviceInfo, String parentId, String label, Uri iconUri)
    176                     throws XmlPullParserException, IOException {
    177         boolean isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0;
    178         TvInputInfo input = createTvInputInfo(context, service, generateInputIdForHdmiDevice(
    179                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
    180                 hdmiDeviceInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch);
    181         input.mHdmiDeviceInfo = hdmiDeviceInfo;
    182         return input;
    183     }
    184 
    185     /**
    186      * Create a new instance of the TvInputInfo class, instantiating it from the given Context,
    187      * ResolveInfo, and TvInputHardwareInfo.
    188      *
    189      * @param service The ResolveInfo returned from the package manager about this TV input service.
    190      * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
    191      * @param iconUri The {@link android.net.Uri} to load the icon image. See
    192      *            {@link android.content.ContentResolver#openInputStream}. If it is {@code null},
    193      *            the application icon of {@code service} will be loaded.
    194      * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service}
    195      *            label will be loaded.
    196      * @hide
    197      */
    198     @SystemApi
    199     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
    200             TvInputHardwareInfo hardwareInfo, String label, Uri iconUri)
    201                     throws XmlPullParserException, IOException {
    202         int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER);
    203         return createTvInputInfo(context, service, generateInputIdForHardware(
    204                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
    205                 hardwareInfo), null, inputType, label, iconUri, false);
    206     }
    207 
    208     private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
    209             String id, String parentId, int inputType, String label, Uri iconUri,
    210             boolean isConnectedToHdmiSwitch)
    211                     throws XmlPullParserException, IOException {
    212         ServiceInfo si = service.serviceInfo;
    213         PackageManager pm = context.getPackageManager();
    214         XmlResourceParser parser = null;
    215         try {
    216             parser = si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
    217             if (parser == null) {
    218                 throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
    219                         + " meta-data for " + si.name);
    220             }
    221 
    222             Resources res = pm.getResourcesForApplication(si.applicationInfo);
    223             AttributeSet attrs = Xml.asAttributeSet(parser);
    224 
    225             int type;
    226             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
    227                     && type != XmlPullParser.START_TAG) {
    228             }
    229 
    230             String nodeName = parser.getName();
    231             if (!XML_START_TAG_NAME.equals(nodeName)) {
    232                 throw new XmlPullParserException(
    233                         "Meta-data does not start with tv-input-service tag in " + si.name);
    234             }
    235 
    236             TvInputInfo input = new TvInputInfo(service, id, parentId, inputType);
    237             TypedArray sa = res.obtainAttributes(attrs,
    238                     com.android.internal.R.styleable.TvInputService);
    239             input.mSetupActivity = sa.getString(
    240                     com.android.internal.R.styleable.TvInputService_setupActivity);
    241             if (DEBUG) {
    242                 Log.d(TAG, "Setup activity loaded. [" + input.mSetupActivity + "] for " + si.name);
    243             }
    244             if (inputType == TYPE_TUNER && TextUtils.isEmpty(input.mSetupActivity)) {
    245                 throw new XmlPullParserException("Setup activity not found in " + si.name);
    246             }
    247             input.mSettingsActivity = sa.getString(
    248                     com.android.internal.R.styleable.TvInputService_settingsActivity);
    249             if (DEBUG) {
    250                 Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
    251                         + si.name);
    252             }
    253             sa.recycle();
    254 
    255             input.mLabel = label;
    256             input.mIconUri = iconUri;
    257             input.mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch;
    258             return input;
    259         } catch (NameNotFoundException e) {
    260             throw new XmlPullParserException("Unable to create context for: " + si.packageName);
    261         } finally {
    262             if (parser != null) {
    263                 parser.close();
    264             }
    265         }
    266     }
    267 
    268     /**
    269      * Constructor.
    270      *
    271      * @param service The ResolveInfo returned from the package manager about this TV input service.
    272      * @param id ID of this TV input. Should be generated via generateInputId*().
    273      * @param parentId ID of this TV input's parent input. {@code null} if none exists.
    274      * @param type The type of this TV input service.
    275      */
    276     private TvInputInfo(ResolveInfo service, String id, String parentId, int type) {
    277         mService = service;
    278         mId = id;
    279         mParentId = parentId;
    280         mType = type;
    281     }
    282 
    283     /**
    284      * Returns a unique ID for this TV input. The ID is generated from the package and class name
    285      * implementing the TV input service.
    286      */
    287     public String getId() {
    288         return mId;
    289     }
    290 
    291     /**
    292      * Returns the parent input ID.
    293      * <p>
    294      * A TV input may have a parent input if the TV input is actually a logical representation of
    295      * a device behind the hardware port represented by the parent input.
    296      * For example, a HDMI CEC logical device, connected to a HDMI port, appears as another TV
    297      * input. In this case, the parent input of this logical device is the HDMI port.
    298      * </p><p>
    299      * Applications may group inputs by parent input ID to provide an easier access to inputs
    300      * sharing the same physical port. In the example of HDMI CEC, logical HDMI CEC devices behind
    301      * the same HDMI port have the same parent ID, which is the ID representing the port. Thus
    302      * applications can group the hardware HDMI port and the logical HDMI CEC devices behind it
    303      * together using this method.
    304      * </p>
    305      *
    306      * @return the ID of the parent input, if exists. Returns {@code null} if the parent input is
    307      *         not specified.
    308      */
    309     public String getParentId() {
    310         return mParentId;
    311     }
    312 
    313     /**
    314      * Returns the information of the service that implements this TV input.
    315      */
    316     public ServiceInfo getServiceInfo() {
    317         return mService.serviceInfo;
    318     }
    319 
    320     /**
    321      * Returns the component of the service that implements this TV input.
    322      * @hide
    323      */
    324     public ComponentName getComponent() {
    325         return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
    326     }
    327 
    328     /**
    329      * Returns an intent to start the setup activity for this TV input.
    330      */
    331     public Intent createSetupIntent() {
    332         if (!TextUtils.isEmpty(mSetupActivity)) {
    333             Intent intent = new Intent(Intent.ACTION_MAIN);
    334             intent.setClassName(mService.serviceInfo.packageName, mSetupActivity);
    335             intent.putExtra(EXTRA_INPUT_ID, getId());
    336             return intent;
    337         }
    338         return null;
    339     }
    340 
    341     /**
    342      * Returns an intent to start the settings activity for this TV input.
    343      */
    344     public Intent createSettingsIntent() {
    345         if (!TextUtils.isEmpty(mSettingsActivity)) {
    346             Intent intent = new Intent(Intent.ACTION_MAIN);
    347             intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity);
    348             intent.putExtra(EXTRA_INPUT_ID, getId());
    349             return intent;
    350         }
    351         return null;
    352     }
    353 
    354     /**
    355      * Returns the type of this TV input.
    356      */
    357     public int getType() {
    358         return mType;
    359     }
    360 
    361     /**
    362      * Returns the HDMI device information of this TV input.
    363      * @hide
    364      */
    365     @SystemApi
    366     public HdmiDeviceInfo getHdmiDeviceInfo() {
    367         if (mType == TYPE_HDMI) {
    368             return mHdmiDeviceInfo;
    369         }
    370         return null;
    371     }
    372 
    373     /**
    374      * Returns {@code true} if this TV input is pass-though which does not have any real channels in
    375      * TvProvider. {@code false} otherwise.
    376      *
    377      * @see TvContract#buildChannelUriForPassthroughInput(String)
    378      */
    379     public boolean isPassthroughInput() {
    380         return mType != TYPE_TUNER;
    381     }
    382 
    383     /**
    384      * Returns {@code true}, if a CEC device for this TV input is connected to an HDMI switch, i.e.,
    385      * the device isn't directly connected to a HDMI port.
    386      * @hide
    387      */
    388     @SystemApi
    389     public boolean isConnectedToHdmiSwitch() {
    390         return mIsConnectedToHdmiSwitch;
    391     }
    392 
    393     /**
    394      * Checks if this TV input is marked hidden by the user in the settings.
    395      *
    396      * @param context Supplies a {@link Context} used to check if this TV input is hidden.
    397      * @return {@code true} if the user marked this TV input hidden in settings. {@code false}
    398      *         otherwise.
    399      * @hide
    400      */
    401     @SystemApi
    402     public boolean isHidden(Context context) {
    403         return TvInputSettings.isHidden(context, mId, UserHandle.myUserId());
    404     }
    405 
    406     /**
    407      * Loads the user-displayed label for this TV input.
    408      *
    409      * @param context Supplies a {@link Context} used to load the label.
    410      * @return a CharSequence containing the TV input's label. If the TV input does not have
    411      *         a label, its name is returned.
    412      */
    413     public CharSequence loadLabel(Context context) {
    414         if (TextUtils.isEmpty(mLabel)) {
    415             return mService.loadLabel(context.getPackageManager());
    416         } else {
    417             return mLabel;
    418         }
    419     }
    420 
    421     /**
    422      * Loads the custom label set by user in settings.
    423      *
    424      * @param context Supplies a {@link Context} used to load the custom label.
    425      * @return a CharSequence containing the TV input's custom label. {@code null} if there is no
    426      *         custom label.
    427      * @hide
    428      */
    429     @SystemApi
    430     public CharSequence loadCustomLabel(Context context) {
    431         return TvInputSettings.getCustomLabel(context, mId, UserHandle.myUserId());
    432     }
    433 
    434     /**
    435      * Loads the user-displayed icon for this TV input.
    436      *
    437      * @param context Supplies a {@link Context} used to load the icon.
    438      * @return a Drawable containing the TV input's icon. If the TV input does not have an icon,
    439      *         application's icon is returned. If it's unavailable too, {@code null} is returned.
    440      */
    441     public Drawable loadIcon(Context context) {
    442         if (mIconUri == null) {
    443             return loadServiceIcon(context);
    444         }
    445         try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) {
    446             Drawable drawable = Drawable.createFromStream(is, null);
    447             if (drawable == null) {
    448                 return loadServiceIcon(context);
    449             }
    450             return drawable;
    451         } catch (IOException e) {
    452             Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e);
    453             return loadServiceIcon(context);
    454         }
    455     }
    456 
    457     @Override
    458     public int describeContents() {
    459         return 0;
    460     }
    461 
    462     @Override
    463     public int hashCode() {
    464         return mId.hashCode();
    465     }
    466 
    467     @Override
    468     public boolean equals(Object o) {
    469         if (o == this) {
    470             return true;
    471         }
    472 
    473         if (!(o instanceof TvInputInfo)) {
    474             return false;
    475         }
    476 
    477         TvInputInfo obj = (TvInputInfo) o;
    478         return mId.equals(obj.mId);
    479     }
    480 
    481     @Override
    482     public String toString() {
    483         return "TvInputInfo{id=" + mId
    484                 + ", pkg=" + mService.serviceInfo.packageName
    485                 + ", service=" + mService.serviceInfo.name + "}";
    486     }
    487 
    488     /**
    489      * Used to package this object into a {@link Parcel}.
    490      *
    491      * @param dest The {@link Parcel} to be written.
    492      * @param flags The flags used for parceling.
    493      */
    494     @Override
    495     public void writeToParcel(Parcel dest, int flags) {
    496         dest.writeString(mId);
    497         dest.writeString(mParentId);
    498         mService.writeToParcel(dest, flags);
    499         dest.writeString(mSetupActivity);
    500         dest.writeString(mSettingsActivity);
    501         dest.writeInt(mType);
    502         dest.writeParcelable(mHdmiDeviceInfo, flags);
    503         dest.writeParcelable(mIconUri, flags);
    504         dest.writeString(mLabel);
    505         dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0);
    506     }
    507 
    508     private Drawable loadServiceIcon(Context context) {
    509         if (mService.serviceInfo.icon == 0
    510                 && mService.serviceInfo.applicationInfo.icon == 0) {
    511             return null;
    512         }
    513         return mService.serviceInfo.loadIcon(context.getPackageManager());
    514     }
    515 
    516     /**
    517      * Used to generate an input id from a ComponentName.
    518      *
    519      * @param name the component name for generating an input id.
    520      * @return the generated input id for the given {@code name}.
    521      */
    522     private static final String generateInputIdForComponentName(ComponentName name) {
    523         return name.flattenToShortString();
    524     }
    525 
    526     /**
    527      * Used to generate an input id from a ComponentName and HdmiDeviceInfo.
    528      *
    529      * @param name the component name for generating an input id.
    530      * @param deviceInfo HdmiDeviceInfo describing this TV input.
    531      * @return the generated input id for the given {@code name} and {@code deviceInfo}.
    532      */
    533     private static final String generateInputIdForHdmiDevice(
    534             ComponentName name, HdmiDeviceInfo deviceInfo) {
    535         // Example of the format : "/HDMI%04X%02X"
    536         String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_HDMI_DEVICE,
    537                 LENGTH_HDMI_PHYSICAL_ADDRESS, LENGTH_HDMI_DEVICE_ID);
    538         return name.flattenToShortString() + String.format(format,
    539                 deviceInfo.getPhysicalAddress(), deviceInfo.getId());
    540     }
    541 
    542     /**
    543      * Used to generate an input id from a ComponentName and TvInputHardwareInfo
    544      *
    545      * @param name the component name for generating an input id.
    546      * @param hardwareInfo TvInputHardwareInfo describing this TV input.
    547      * @return the generated input id for the given {@code name} and {@code hardwareInfo}.
    548      */
    549     private static final String generateInputIdForHardware(
    550             ComponentName name, TvInputHardwareInfo hardwareInfo) {
    551         return name.flattenToShortString() + String.format("%s%s%d",
    552                 DELIMITER_INFO_IN_ID, PREFIX_HARDWARE_DEVICE, hardwareInfo.getDeviceId());
    553     }
    554 
    555     public static final Parcelable.Creator<TvInputInfo> CREATOR =
    556             new Parcelable.Creator<TvInputInfo>() {
    557         @Override
    558         public TvInputInfo createFromParcel(Parcel in) {
    559             return new TvInputInfo(in);
    560         }
    561 
    562         @Override
    563         public TvInputInfo[] newArray(int size) {
    564             return new TvInputInfo[size];
    565         }
    566     };
    567 
    568     private TvInputInfo(Parcel in) {
    569         mId = in.readString();
    570         mParentId = in.readString();
    571         mService = ResolveInfo.CREATOR.createFromParcel(in);
    572         mSetupActivity = in.readString();
    573         mSettingsActivity = in.readString();
    574         mType = in.readInt();
    575         mHdmiDeviceInfo = in.readParcelable(null);
    576         mIconUri = in.readParcelable(null);
    577         mLabel = in.readString();
    578         mIsConnectedToHdmiSwitch = in.readByte() == 1 ? true : false;
    579     }
    580 
    581     /**
    582      * Utility class for putting and getting settings for TV input.
    583      *
    584      * @hide
    585      */
    586     @SystemApi
    587     public static final class TvInputSettings {
    588         private static final String TV_INPUT_SEPARATOR = ":";
    589         private static final String CUSTOM_NAME_SEPARATOR = ",";
    590 
    591         private TvInputSettings() { }
    592 
    593         private static boolean isHidden(Context context, String inputId, int userId) {
    594             return getHiddenTvInputIds(context, userId).contains(inputId);
    595         }
    596 
    597         private static String getCustomLabel(Context context, String inputId, int userId) {
    598             return getCustomLabels(context, userId).get(inputId);
    599         }
    600 
    601         /**
    602          * Returns a set of TV input IDs which are marked as hidden by user in the settings.
    603          *
    604          * @param context The application context
    605          * @param userId The user ID for the stored hidden input set
    606          * @hide
    607          */
    608         @SystemApi
    609         public static Set<String> getHiddenTvInputIds(Context context, int userId) {
    610             String hiddenIdsString = Settings.Secure.getStringForUser(
    611                     context.getContentResolver(), Settings.Secure.TV_INPUT_HIDDEN_INPUTS, userId);
    612             Set<String> set = new HashSet<String>();
    613             if (TextUtils.isEmpty(hiddenIdsString)) {
    614                 return set;
    615             }
    616             String[] ids = hiddenIdsString.split(TV_INPUT_SEPARATOR);
    617             for (String id : ids) {
    618                 set.add(Uri.decode(id));
    619             }
    620             return set;
    621         }
    622 
    623         /**
    624          * Returns a map of TV input ID/custom label pairs set by the user in the settings.
    625          *
    626          * @param context The application context
    627          * @param userId The user ID for the stored hidden input map
    628          * @hide
    629          */
    630         @SystemApi
    631         public static Map<String, String> getCustomLabels(Context context, int userId) {
    632             String labelsString = Settings.Secure.getStringForUser(
    633                     context.getContentResolver(), Settings.Secure.TV_INPUT_CUSTOM_LABELS, userId);
    634             Map<String, String> map = new HashMap<String, String>();
    635             if (TextUtils.isEmpty(labelsString)) {
    636                 return map;
    637             }
    638             String[] pairs = labelsString.split(TV_INPUT_SEPARATOR);
    639             for (String pairString : pairs) {
    640                 String[] pair = pairString.split(CUSTOM_NAME_SEPARATOR);
    641                 map.put(Uri.decode(pair[0]), Uri.decode(pair[1]));
    642             }
    643             return map;
    644         }
    645 
    646         /**
    647          * Stores a set of TV input IDs which are marked as hidden by user. This is expected to
    648          * be called from the settings app.
    649          *
    650          * @param context The application context
    651          * @param hiddenInputIds A set including all the hidden TV input IDs
    652          * @param userId The user ID for the stored hidden input set
    653          * @hide
    654          */
    655         @SystemApi
    656         public static void putHiddenTvInputs(Context context, Set<String> hiddenInputIds,
    657                 int userId) {
    658             StringBuilder builder = new StringBuilder();
    659             boolean firstItem = true;
    660             for (String inputId : hiddenInputIds) {
    661                 ensureValidField(inputId);
    662                 if (firstItem) {
    663                     firstItem = false;
    664                 } else {
    665                     builder.append(TV_INPUT_SEPARATOR);
    666                 }
    667                 builder.append(Uri.encode(inputId));
    668             }
    669             Settings.Secure.putStringForUser(context.getContentResolver(),
    670                     Settings.Secure.TV_INPUT_HIDDEN_INPUTS, builder.toString(), userId);
    671         }
    672 
    673         /**
    674          * Stores a map of TV input ID/custom label set by user. This is expected to be
    675          * called from the settings app.
    676          *
    677          * @param context The application context.
    678          * @param customLabels A map of TV input ID/custom label pairs
    679          * @param userId The user ID for the stored hidden input map
    680          * @hide
    681          */
    682         @SystemApi
    683         public static void putCustomLabels(Context context,
    684                 Map<String, String> customLabels, int userId) {
    685             StringBuilder builder = new StringBuilder();
    686             boolean firstItem = true;
    687             for (Map.Entry<String, String> entry: customLabels.entrySet()) {
    688                 ensureValidField(entry.getKey());
    689                 ensureValidField(entry.getValue());
    690                 if (firstItem) {
    691                     firstItem = false;
    692                 } else {
    693                     builder.append(TV_INPUT_SEPARATOR);
    694                 }
    695                 builder.append(Uri.encode(entry.getKey()));
    696                 builder.append(CUSTOM_NAME_SEPARATOR);
    697                 builder.append(Uri.encode(entry.getValue()));
    698             }
    699             Settings.Secure.putStringForUser(context.getContentResolver(),
    700                     Settings.Secure.TV_INPUT_CUSTOM_LABELS, builder.toString(), userId);
    701         }
    702 
    703         private static void ensureValidField(String value) {
    704             if (TextUtils.isEmpty(value)) {
    705                 throw new IllegalArgumentException(value + " should not empty ");
    706             }
    707         }
    708     }
    709 }
    710