Partner.java revision 3dfa929b24f38ac7836450176d88ceab41dc6ac5
1/*
2 * Copyright (C) 2017 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 com.android.tv.util;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
25import android.content.res.Resources;
26import android.media.tv.TvInputInfo;
27import android.text.TextUtils;
28import android.util.Log;
29
30import java.util.HashMap;
31import java.util.Map;
32
33/**
34 * This file refers to Partner.java in LeanbackLauncher. Interact with partner customizations. There
35 * can only be one set of customizations on a device, and it must be bundled with the system.
36 */
37public class Partner {
38    private static final String TAG = "Partner";
39    /** Marker action used to discover partner */
40    private static final String ACTION_PARTNER_CUSTOMIZATION =
41            "com.google.android.leanbacklauncher.action.PARTNER_CUSTOMIZATION";
42
43    /** ID tags for device input types */
44    public static final String INPUT_TYPE_BUNDLED_TUNER = "input_type_combined_tuners";
45    public static final String INPUT_TYPE_TUNER = "input_type_tuner";
46    public static final String INPUT_TYPE_CEC_LOGICAL = "input_type_cec_logical";
47    public static final String INPUT_TYPE_CEC_RECORDER = "input_type_cec_recorder";
48    public static final String INPUT_TYPE_CEC_PLAYBACK = "input_type_cec_playback";
49    public static final String INPUT_TYPE_MHL_MOBILE = "input_type_mhl_mobile";
50    public static final String INPUT_TYPE_HDMI = "input_type_hdmi";
51    public static final String INPUT_TYPE_DVI = "input_type_dvi";
52    public static final String INPUT_TYPE_COMPONENT = "input_type_component";
53    public static final String INPUT_TYPE_SVIDEO = "input_type_svideo";
54    public static final String INPUT_TYPE_COMPOSITE = "input_type_composite";
55    public static final String INPUT_TYPE_DISPLAY_PORT = "input_type_displayport";
56    public static final String INPUT_TYPE_VGA = "input_type_vga";
57    public static final String INPUT_TYPE_SCART = "input_type_scart";
58    public static final String INPUT_TYPE_OTHER = "input_type_other";
59
60    private static final String INPUTS_ORDER = "home_screen_inputs_ordering";
61    private static final String TYPE_ARRAY = "array";
62
63    private static Partner sPartner;
64    private static final Object sLock = new Object();
65
66    private final String mPackageName;
67    private final String mReceiverName;
68    private final Resources mResources;
69
70    private static final Map<String, Integer> INPUT_TYPE_MAP = new HashMap<>();
71    static {
72        INPUT_TYPE_MAP.put(INPUT_TYPE_BUNDLED_TUNER, TvInputManagerHelper.TYPE_BUNDLED_TUNER);
73        INPUT_TYPE_MAP.put(INPUT_TYPE_TUNER, TvInputInfo.TYPE_TUNER);
74        INPUT_TYPE_MAP.put(INPUT_TYPE_CEC_LOGICAL, TvInputManagerHelper.TYPE_CEC_DEVICE);
75        INPUT_TYPE_MAP.put(INPUT_TYPE_CEC_RECORDER, TvInputManagerHelper.TYPE_CEC_DEVICE_RECORDER);
76        INPUT_TYPE_MAP.put(INPUT_TYPE_CEC_PLAYBACK, TvInputManagerHelper.TYPE_CEC_DEVICE_PLAYBACK);
77        INPUT_TYPE_MAP.put(INPUT_TYPE_MHL_MOBILE, TvInputManagerHelper.TYPE_MHL_MOBILE);
78        INPUT_TYPE_MAP.put(INPUT_TYPE_HDMI, TvInputInfo.TYPE_HDMI);
79        INPUT_TYPE_MAP.put(INPUT_TYPE_DVI, TvInputInfo.TYPE_DVI);
80        INPUT_TYPE_MAP.put(INPUT_TYPE_COMPONENT, TvInputInfo.TYPE_COMPONENT);
81        INPUT_TYPE_MAP.put(INPUT_TYPE_SVIDEO, TvInputInfo.TYPE_SVIDEO);
82        INPUT_TYPE_MAP.put(INPUT_TYPE_COMPOSITE, TvInputInfo.TYPE_COMPOSITE);
83        INPUT_TYPE_MAP.put(INPUT_TYPE_DISPLAY_PORT, TvInputInfo.TYPE_DISPLAY_PORT);
84        INPUT_TYPE_MAP.put(INPUT_TYPE_VGA, TvInputInfo.TYPE_VGA);
85        INPUT_TYPE_MAP.put(INPUT_TYPE_SCART, TvInputInfo.TYPE_SCART);
86        INPUT_TYPE_MAP.put(INPUT_TYPE_OTHER, TvInputInfo.TYPE_OTHER);
87    }
88
89    private Partner(String packageName, String receiverName, Resources res) {
90        mPackageName = packageName;
91        mReceiverName = receiverName;
92        mResources = res;
93    }
94
95    /** Returns partner instance. */
96    public static Partner getInstance(Context context) {
97        PackageManager pm = context.getPackageManager();
98        synchronized (sLock) {
99            ResolveInfo info = getPartnerResolveInfo(pm);
100            if (info != null) {
101                final String packageName = info.activityInfo.packageName;
102                final String receiverName = info.activityInfo.name;
103                try {
104                    final Resources res = pm.getResourcesForApplication(packageName);
105                    sPartner = new Partner(packageName, receiverName, res);
106                    sPartner.sendInitBroadcast(context);
107                } catch (PackageManager.NameNotFoundException e) {
108                    Log.w(TAG, "Failed to find resources for " + packageName);
109                }
110            }
111            if (sPartner == null) {
112                sPartner = new Partner(null, null, null);
113            }
114        }
115        return sPartner;
116    }
117
118    /** Resets the Partner instance to handle the partner package has changed. */
119    public static void reset(Context context, String packageName) {
120        synchronized (sLock) {
121            if (sPartner != null && !TextUtils.isEmpty(packageName)) {
122                if (packageName.equals(sPartner.mPackageName)) {
123                    // Force a refresh, so we send an Init to the updated package
124                    sPartner = null;
125                    getInstance(context);
126                }
127            }
128        }
129    }
130
131    /** This method is used to send init broadcast to the new/changed partner package. */
132    private void sendInitBroadcast(Context context) {
133        if (!TextUtils.isEmpty(mPackageName) && !TextUtils.isEmpty(mReceiverName)) {
134            Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
135            final ComponentName componentName = new ComponentName(mPackageName, mReceiverName);
136            intent.setComponent(componentName);
137            intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
138            context.sendBroadcast(intent);
139        }
140    }
141
142    /** Returns the order of inputs. */
143    public Map<Integer, Integer> getInputsOrderMap() {
144        HashMap<Integer, Integer> map = new HashMap<>();
145        if (mResources != null && !TextUtils.isEmpty(mPackageName)) {
146            String[] inputsArray = null;
147            final int resId = mResources.getIdentifier(INPUTS_ORDER, TYPE_ARRAY, mPackageName);
148            if (resId != 0) {
149                inputsArray = mResources.getStringArray(resId);
150            }
151            if (inputsArray != null) {
152                int priority = 0;
153                for (String input : inputsArray) {
154                    Integer type = INPUT_TYPE_MAP.get(input);
155                    if (type != null) {
156                        map.put(type, priority++);
157                    }
158                }
159            }
160        }
161        return map;
162    }
163
164    private static ResolveInfo getPartnerResolveInfo(PackageManager pm) {
165        final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
166        ResolveInfo partnerInfo = null;
167        for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
168            if (isSystemApp(info)) {
169                partnerInfo = info;
170                break;
171            }
172        }
173        return partnerInfo;
174    }
175
176    protected static boolean isSystemApp(ResolveInfo info) {
177        return (info.activityInfo != null
178                && info.activityInfo.applicationInfo != null
179                && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
180    }
181}
182