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