blob: f59d431d438281847cbff0d839ae0b4dd15a58d8 [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;
Andrei Litvinc5ed9372020-04-15 16:33:22 -040030import android.text.TextUtils.SimpleStringSplitter;
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080031import android.util.Log;
32import android.util.Slog;
33
Andrei Litvinb298d4c2020-02-27 13:29:20 -050034import com.android.internal.annotations.VisibleForTesting;
35
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080036import java.util.ArrayList;
37import java.util.Collections;
Andrei Litvinc5ed9372020-04-15 16:33:22 -040038import java.util.HashSet;
39import java.util.Set;
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080040
41/**
42 * Watches for emote provider services to be installed.
43 * Adds a provider for each registered service.
44 *
45 * @see TvRemoteProviderProxy
46 */
47final class TvRemoteProviderWatcher {
48
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020049 private static final String TAG = "TvRemoteProviderWatcher";
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080050 private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
51
52 private final Context mContext;
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080053 private final Handler mHandler;
54 private final PackageManager mPackageManager;
55 private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
56 private final int mUserId;
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020057 private final Object mLock;
Andrei Litvinc5ed9372020-04-15 16:33:22 -040058 private final Set<String> mUnbundledServicePackages = new HashSet<>();
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080059
60 private boolean mRunning;
61
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020062 TvRemoteProviderWatcher(Context context, Object lock) {
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080063 mContext = context;
Tom Macieszczakaee0bb22019-09-03 11:41:50 +020064 mHandler = new Handler(true);
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080065 mUserId = UserHandle.myUserId();
66 mPackageManager = context.getPackageManager();
Tom Macieszczak34ba4b12019-09-23 15:24:39 +020067 mLock = lock;
Andrei Litvinc5ed9372020-04-15 16:33:22 -040068
69 // Unbundled package names supports a comma-separated list
70 SimpleStringSplitter splitter = new SimpleStringSplitter(',');
71 splitter.setString(context.getString(
72 com.android.internal.R.string.config_tvRemoteServicePackage));
73
74 splitter.forEach(packageName -> {
75 packageName = packageName.trim();
76 if (!packageName.isEmpty()) {
77 mUnbundledServicePackages.add(packageName);
78 }
79 });
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -080080 }
81
82 public void start() {
83 if (DEBUG) Slog.d(TAG, "start()");
84 if (!mRunning) {
85 mRunning = true;
86
87 IntentFilter filter = new IntentFilter();
88 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
89 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
90 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
91 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
92 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
93 filter.addDataScheme("package");
94 mContext.registerReceiverAsUser(mScanPackagesReceiver,
95 new UserHandle(mUserId), filter, null, mHandler);
96
97 // Scan packages.
98 // Also has the side-effect of restarting providers if needed.
99 mHandler.post(mScanPackagesRunnable);
100 }
101 }
102
103 public void stop() {
104 if (mRunning) {
105 mRunning = false;
106
107 mContext.unregisterReceiver(mScanPackagesReceiver);
108 mHandler.removeCallbacks(mScanPackagesRunnable);
109
110 // Stop all providers.
111 for (int i = mProviderProxies.size() - 1; i >= 0; i--) {
112 mProviderProxies.get(i).stop();
113 }
114 }
115 }
116
117 private void scanPackages() {
118 if (!mRunning) {
119 return;
120 }
121
122 if (DEBUG) Log.d(TAG, "scanPackages()");
123 // Add providers for all new services.
124 // Reorder the list so that providers left at the end will be the ones to remove.
125 int targetIndex = 0;
126 Intent intent = new Intent(TvRemoteProviderProxy.SERVICE_INTERFACE);
127 for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
128 intent, 0, mUserId)) {
129 ServiceInfo serviceInfo = resolveInfo.serviceInfo;
130 if (serviceInfo != null && verifyServiceTrusted(serviceInfo)) {
131 int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
132 if (sourceIndex < 0) {
133 TvRemoteProviderProxy providerProxy =
Tom Macieszczak34ba4b12019-09-23 15:24:39 +0200134 new TvRemoteProviderProxy(mContext, mLock,
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800135 new ComponentName(serviceInfo.packageName, serviceInfo.name),
136 mUserId, serviceInfo.applicationInfo.uid);
137 providerProxy.start();
138 mProviderProxies.add(targetIndex++, providerProxy);
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800139 } else if (sourceIndex >= targetIndex) {
140 TvRemoteProviderProxy provider = mProviderProxies.get(sourceIndex);
141 provider.start(); // restart the provider if needed
142 provider.rebindIfDisconnected();
143 Collections.swap(mProviderProxies, sourceIndex, targetIndex++);
144 }
145 }
146 }
147 if (DEBUG) Log.d(TAG, "scanPackages() targetIndex " + targetIndex);
148 // Remove providers for missing services.
149 if (targetIndex < mProviderProxies.size()) {
150 for (int i = mProviderProxies.size() - 1; i >= targetIndex; i--) {
151 TvRemoteProviderProxy providerProxy = mProviderProxies.get(i);
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800152 mProviderProxies.remove(providerProxy);
153 providerProxy.stop();
154 }
155 }
156 }
157
Andrei Litvinb298d4c2020-02-27 13:29:20 -0500158 @VisibleForTesting
159 boolean verifyServiceTrusted(ServiceInfo serviceInfo) {
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800160 if (serviceInfo.permission == null || !serviceInfo.permission.equals(
161 Manifest.permission.BIND_TV_REMOTE_SERVICE)) {
162 // If the service does not require this permission then any app could
163 // potentially bind to it and cause the atv remote provider service to
164 // misbehave. So we only want to trust providers that require the
165 // correct permissions.
166 Slog.w(TAG, "Ignoring atv remote provider service because it did not "
167 + "require the BIND_TV_REMOTE_SERVICE permission in its manifest: "
168 + serviceInfo.packageName + "/" + serviceInfo.name);
169 return false;
170 }
171
172 // Check if package name is white-listed here.
Andrei Litvinc5ed9372020-04-15 16:33:22 -0400173 if (!mUnbundledServicePackages.contains(serviceInfo.packageName)) {
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800174 Slog.w(TAG, "Ignoring atv remote provider service because the package has not "
175 + "been set and/or whitelisted: "
176 + serviceInfo.packageName + "/" + serviceInfo.name);
177 return false;
178 }
179
180 if (!hasNecessaryPermissions(serviceInfo.packageName)) {
181 // If the service does not have permission to be
182 // a virtual tv remote controller, do not trust it.
183 Slog.w(TAG, "Ignoring atv remote provider service because its package does not "
184 + "have TV_VIRTUAL_REMOTE_CONTROLLER permission: " + serviceInfo.packageName);
185 return false;
186 }
187
188 // Looks good.
189 return true;
190 }
191
192 // Returns true only if these permissions are present in calling package.
193 // Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER : virtual remote controller on TV
194 private boolean hasNecessaryPermissions(String packageName) {
195 if ((mPackageManager.checkPermission(Manifest.permission.TV_VIRTUAL_REMOTE_CONTROLLER,
196 packageName) == PackageManager.PERMISSION_GRANTED)) {
197 return true;
198 }
199 return false;
200 }
201
202 private int findProvider(String packageName, String className) {
203 int count = mProviderProxies.size();
204 for (int i = 0; i < count; i++) {
205 TvRemoteProviderProxy provider = mProviderProxies.get(i);
206 if (provider.hasComponentName(packageName, className)) {
207 return i;
208 }
209 }
210 return -1;
211 }
212
213 private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
214 @Override
215 public void onReceive(Context context, Intent intent) {
216 if (DEBUG) {
217 Slog.d(TAG, "Received package manager broadcast: " + intent);
218 }
219 mHandler.post(mScanPackagesRunnable);
220 }
221 };
222
223 private final Runnable mScanPackagesRunnable = new Runnable() {
224 @Override
225 public void run() {
226 scanPackages();
227 }
228 };
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -0800229}