blob: 06c2354c7a7de0a1abf13535df6f6a73ac27fe8b [file] [log] [blame]
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -08001/*
2 * Copyright (C) 2016 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.server.tv;
18
19import android.Manifest;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.pm.ServiceInfo;
28import android.os.Handler;
29import android.os.UserHandle;
30import android.util.Log;
31import android.util.Slog;
32
Andrei Litvinb298d4c2020-02-27 13:29:20 -050033import com.android.internal.annotations.VisibleForTesting;
34
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080035import java.util.ArrayList;
36import java.util.Collections;
37
38/**
39 * Watches for emote provider services to be installed.
40 * Adds a provider for each registered service.
41 *
42 * @see TvRemoteProviderProxy
43 */
44final class TvRemoteProviderWatcher {
45
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020046 private static final String TAG = "TvRemoteProviderWatcher";
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080047 private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
48
49 private final Context mContext;
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080050 private final Handler mHandler;
51 private final PackageManager mPackageManager;
52 private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
53 private final int mUserId;
54 private final String mUnbundledServicePackage;
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020055 private final Object mLock;
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080056
57 private boolean mRunning;
58
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020059 TvRemoteProviderWatcher(Context context, Object lock) {
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080060 mContext = context;
Tom Macieszczakaee0bb22019-09-03 11:41:50 +020061 mHandler = new Handler(true);
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080062 mUserId = UserHandle.myUserId();
63 mPackageManager = context.getPackageManager();
64 mUnbundledServicePackage = context.getString(
65 com.android.internal.R.string.config_tvRemoteServicePackage);
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020066 mLock = lock;
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080067 }
68
69 public void start() {
70 if (DEBUG) Slog.d(TAG, "start()");
71 if (!mRunning) {
72 mRunning = true;
73
74 IntentFilter filter = new IntentFilter();
75 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
76 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
77 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
78 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
79 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
80 filter.addDataScheme("package");
81 mContext.registerReceiverAsUser(mScanPackagesReceiver,
82 new UserHandle(mUserId), filter, null, mHandler);
83
84 // Scan packages.
85 // Also has the side-effect of restarting providers if needed.
86 mHandler.post(mScanPackagesRunnable);
87 }
88 }
89
90 public void stop() {
91 if (mRunning) {
92 mRunning = false;
93
94 mContext.unregisterReceiver(mScanPackagesReceiver);
95 mHandler.removeCallbacks(mScanPackagesRunnable);
96
97 // Stop all providers.
98 for (int i = mProviderProxies.size() - 1; i >= 0; i--) {
99 mProviderProxies.get(i).stop();
100 }
101 }
102 }
103
104 private void scanPackages() {
105 if (!mRunning) {
106 return;
107 }
108
109 if (DEBUG) Log.d(TAG, "scanPackages()");
110 // Add providers for all new services.
111 // Reorder the list so that providers left at the end will be the ones to remove.
112 int targetIndex = 0;
113 Intent intent = new Intent(TvRemoteProviderProxy.SERVICE_INTERFACE);
114 for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
115 intent, 0, mUserId)) {
116 ServiceInfo serviceInfo = resolveInfo.serviceInfo;
117 if (serviceInfo != null && verifyServiceTrusted(serviceInfo)) {
118 int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
119 if (sourceIndex < 0) {
120 TvRemoteProviderProxy providerProxy =
Tom Macieszczak34ba4b12019-09-23 15:24:39 +0200121 new TvRemoteProviderProxy(mContext, mLock,
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800122 new ComponentName(serviceInfo.packageName, serviceInfo.name),
123 mUserId, serviceInfo.applicationInfo.uid);
124 providerProxy.start();
125 mProviderProxies.add(targetIndex++, providerProxy);
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800126 } else if (sourceIndex >= targetIndex) {
127 TvRemoteProviderProxy provider = mProviderProxies.get(sourceIndex);
128 provider.start(); // restart the provider if needed
129 provider.rebindIfDisconnected();
130 Collections.swap(mProviderProxies, sourceIndex, targetIndex++);
131 }
132 }
133 }
134 if (DEBUG) Log.d(TAG, "scanPackages() targetIndex " + targetIndex);
135 // Remove providers for missing services.
136 if (targetIndex < mProviderProxies.size()) {
137 for (int i = mProviderProxies.size() - 1; i >= targetIndex; i--) {
138 TvRemoteProviderProxy providerProxy = mProviderProxies.get(i);
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800139 mProviderProxies.remove(providerProxy);
140 providerProxy.stop();
141 }
142 }
143 }
144
Andrei Litvinb298d4c2020-02-27 13:29:20 -0500145 @VisibleForTesting
146 boolean verifyServiceTrusted(ServiceInfo serviceInfo) {
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800147 if (serviceInfo.permission == null || !serviceInfo.permission.equals(
148 Manifest.permission.BIND_TV_REMOTE_SERVICE)) {
149 // If the service does not require this permission then any app could
150 // potentially bind to it and cause the atv remote provider service to
151 // misbehave. So we only want to trust providers that require the
152 // correct permissions.
153 Slog.w(TAG, "Ignoring atv remote provider service because it did not "
154 + "require the BIND_TV_REMOTE_SERVICE permission in its manifest: "
155 + serviceInfo.packageName + "/" + serviceInfo.name);
156 return false;
157 }
158
159 // Check if package name is white-listed here.
160 if (!serviceInfo.packageName.equals(mUnbundledServicePackage)) {
161 Slog.w(TAG, "Ignoring atv remote provider service because the package has not "
162 + "been set and/or whitelisted: "
163 + serviceInfo.packageName + "/" + serviceInfo.name);
164 return false;
165 }
166
167 if (!hasNecessaryPermissions(serviceInfo.packageName)) {
168 // If the service does not have permission to be
169 // a virtual tv remote controller, do not trust it.
170 Slog.w(TAG, "Ignoring atv remote provider service because its package does not "
171 + "have TV_VIRTUAL_REMOTE_CONTROLLER permission: " + serviceInfo.packageName);
172 return false;
173 }
174
175 // Looks good.
176 return true;
177 }
178
179 // Returns true only if these permissions are present in calling package.
180 // Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER : virtual remote controller on TV
181 private boolean hasNecessaryPermissions(String packageName) {
182 if ((mPackageManager.checkPermission(Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER,
183 packageName) == PackageManager.PERMISSION_GRANTED)) {
184 return true;
185 }
186 return false;
187 }
188
189 private int findProvider(String packageName, String className) {
190 int count = mProviderProxies.size();
191 for (int i = 0; i < count; i++) {
192 TvRemoteProviderProxy provider = mProviderProxies.get(i);
193 if (provider.hasComponentName(packageName, className)) {
194 return i;
195 }
196 }
197 return -1;
198 }
199
200 private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
201 @Override
202 public void onReceive(Context context, Intent intent) {
203 if (DEBUG) {
204 Slog.d(TAG, "Received package manager broadcast: " + intent);
205 }
206 mHandler.post(mScanPackagesRunnable);
207 }
208 };
209
210 private final Runnable mScanPackagesRunnable = new Runnable() {
211 @Override
212 public void run() {
213 scanPackages();
214 }
215 };
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800216}