| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tv.util; |
| |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.res.Resources; |
| import android.media.tv.TvInputInfo; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * This file refers to Partner.java in LeanbackLauncher. Interact with partner customizations. There |
| * can only be one set of customizations on a device, and it must be bundled with the system. |
| */ |
| public class Partner { |
| private static final String TAG = "Partner"; |
| /** Marker action used to discover partner */ |
| private static final String ACTION_PARTNER_CUSTOMIZATION = |
| "com.google.android.leanbacklauncher.action.PARTNER_CUSTOMIZATION"; |
| |
| /** ID tags for device input types */ |
| public static final String INPUT_TYPE_BUNDLED_TUNER = "input_type_combined_tuners"; |
| |
| public static final String INPUT_TYPE_TUNER = "input_type_tuner"; |
| public static final String INPUT_TYPE_CEC_LOGICAL = "input_type_cec_logical"; |
| public static final String INPUT_TYPE_CEC_RECORDER = "input_type_cec_recorder"; |
| public static final String INPUT_TYPE_CEC_PLAYBACK = "input_type_cec_playback"; |
| public static final String INPUT_TYPE_MHL_MOBILE = "input_type_mhl_mobile"; |
| public static final String INPUT_TYPE_HDMI = "input_type_hdmi"; |
| public static final String INPUT_TYPE_DVI = "input_type_dvi"; |
| public static final String INPUT_TYPE_COMPONENT = "input_type_component"; |
| public static final String INPUT_TYPE_SVIDEO = "input_type_svideo"; |
| public static final String INPUT_TYPE_COMPOSITE = "input_type_composite"; |
| public static final String INPUT_TYPE_DISPLAY_PORT = "input_type_displayport"; |
| public static final String INPUT_TYPE_VGA = "input_type_vga"; |
| public static final String INPUT_TYPE_SCART = "input_type_scart"; |
| public static final String INPUT_TYPE_OTHER = "input_type_other"; |
| |
| private static final String INPUTS_ORDER = "home_screen_inputs_ordering"; |
| private static final String TYPE_ARRAY = "array"; |
| |
| private static Partner sPartner; |
| private static final Object sLock = new Object(); |
| |
| private final String mPackageName; |
| private final String mReceiverName; |
| private final Resources mResources; |
| |
| private static final Map<String, Integer> INPUT_TYPE_MAP = new HashMap<>(); |
| |
| static { |
| INPUT_TYPE_MAP.put(INPUT_TYPE_BUNDLED_TUNER, TvInputManagerHelper.TYPE_BUNDLED_TUNER); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_TUNER, TvInputInfo.TYPE_TUNER); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_CEC_LOGICAL, TvInputManagerHelper.TYPE_CEC_DEVICE); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_CEC_RECORDER, TvInputManagerHelper.TYPE_CEC_DEVICE_RECORDER); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_CEC_PLAYBACK, TvInputManagerHelper.TYPE_CEC_DEVICE_PLAYBACK); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_MHL_MOBILE, TvInputManagerHelper.TYPE_MHL_MOBILE); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_HDMI, TvInputInfo.TYPE_HDMI); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_DVI, TvInputInfo.TYPE_DVI); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_COMPONENT, TvInputInfo.TYPE_COMPONENT); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_SVIDEO, TvInputInfo.TYPE_SVIDEO); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_COMPOSITE, TvInputInfo.TYPE_COMPOSITE); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_DISPLAY_PORT, TvInputInfo.TYPE_DISPLAY_PORT); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_VGA, TvInputInfo.TYPE_VGA); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_SCART, TvInputInfo.TYPE_SCART); |
| INPUT_TYPE_MAP.put(INPUT_TYPE_OTHER, TvInputInfo.TYPE_OTHER); |
| } |
| |
| private Partner(String packageName, String receiverName, Resources res) { |
| mPackageName = packageName; |
| mReceiverName = receiverName; |
| mResources = res; |
| } |
| |
| /** Returns partner instance. */ |
| public static Partner getInstance(Context context) { |
| PackageManager pm = context.getPackageManager(); |
| synchronized (sLock) { |
| ResolveInfo info = getPartnerResolveInfo(pm); |
| if (info != null) { |
| final String packageName = info.activityInfo.packageName; |
| final String receiverName = info.activityInfo.name; |
| try { |
| final Resources res = pm.getResourcesForApplication(packageName); |
| sPartner = new Partner(packageName, receiverName, res); |
| sPartner.sendInitBroadcast(context); |
| } catch (PackageManager.NameNotFoundException e) { |
| Log.w(TAG, "Failed to find resources for " + packageName); |
| } |
| } |
| if (sPartner == null) { |
| sPartner = new Partner(null, null, null); |
| } |
| } |
| return sPartner; |
| } |
| |
| /** Resets the Partner instance to handle the partner package has changed. */ |
| public static void reset(Context context, String packageName) { |
| synchronized (sLock) { |
| if (sPartner != null && !TextUtils.isEmpty(packageName)) { |
| if (packageName.equals(sPartner.mPackageName)) { |
| // Force a refresh, so we send an Init to the updated package |
| sPartner = null; |
| getInstance(context); |
| } |
| } |
| } |
| } |
| |
| /** This method is used to send init broadcast to the new/changed partner package. */ |
| private void sendInitBroadcast(Context context) { |
| if (!TextUtils.isEmpty(mPackageName) && !TextUtils.isEmpty(mReceiverName)) { |
| Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION); |
| final ComponentName componentName = new ComponentName(mPackageName, mReceiverName); |
| intent.setComponent(componentName); |
| intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); |
| context.sendBroadcast(intent); |
| } |
| } |
| |
| /** Returns the order of inputs. */ |
| public Map<Integer, Integer> getInputsOrderMap() { |
| HashMap<Integer, Integer> map = new HashMap<>(); |
| if (mResources != null && !TextUtils.isEmpty(mPackageName)) { |
| String[] inputsArray = null; |
| final int resId = mResources.getIdentifier(INPUTS_ORDER, TYPE_ARRAY, mPackageName); |
| if (resId != 0) { |
| inputsArray = mResources.getStringArray(resId); |
| } |
| if (inputsArray != null) { |
| int priority = 0; |
| for (String input : inputsArray) { |
| Integer type = INPUT_TYPE_MAP.get(input); |
| if (type != null) { |
| map.put(type, priority++); |
| } |
| } |
| } |
| } |
| return map; |
| } |
| |
| private static ResolveInfo getPartnerResolveInfo(PackageManager pm) { |
| final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION); |
| ResolveInfo partnerInfo = null; |
| for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { |
| if (isSystemApp(info)) { |
| partnerInfo = info; |
| break; |
| } |
| } |
| return partnerInfo; |
| } |
| |
| protected static boolean isSystemApp(ResolveInfo info) { |
| return (info.activityInfo != null |
| && info.activityInfo.applicationInfo != null |
| && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); |
| } |
| } |