1 /* 2 * Copyright (C) 2013 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 17 package com.android.server.media; 18 19 import android.Manifest; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.content.pm.ServiceInfo; 28 import android.media.RemoteDisplayState; 29 import android.os.Handler; 30 import android.os.UserHandle; 31 import android.util.Log; 32 import android.util.Slog; 33 34 import java.io.PrintWriter; 35 import java.util.ArrayList; 36 import java.util.Collections; 37 38 /** 39 * Watches for remote display provider services to be installed. 40 * Adds a provider to the media router for each registered service. 41 * 42 * @see RemoteDisplayProviderProxy 43 */ 44 public final class RemoteDisplayProviderWatcher { 45 private static final String TAG = "RemoteDisplayProvider"; // max. 23 chars 46 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 47 48 private final Context mContext; 49 private final Callback mCallback; 50 private final Handler mHandler; 51 private final int mUserId; 52 private final PackageManager mPackageManager; 53 54 private final ArrayList<RemoteDisplayProviderProxy> mProviders = 55 new ArrayList<RemoteDisplayProviderProxy>(); 56 private boolean mRunning; 57 58 public RemoteDisplayProviderWatcher(Context context, 59 Callback callback, Handler handler, int userId) { 60 mContext = context; 61 mCallback = callback; 62 mHandler = handler; 63 mUserId = userId; 64 mPackageManager = context.getPackageManager(); 65 } 66 67 public void dump(PrintWriter pw, String prefix) { 68 pw.println(prefix + "Watcher"); 69 pw.println(prefix + " mUserId=" + mUserId); 70 pw.println(prefix + " mRunning=" + mRunning); 71 pw.println(prefix + " mProviders.size()=" + mProviders.size()); 72 } 73 74 public void start() { 75 if (!mRunning) { 76 mRunning = true; 77 78 IntentFilter filter = new IntentFilter(); 79 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 80 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 81 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 82 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 83 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 84 filter.addDataScheme("package"); 85 mContext.registerReceiverAsUser(mScanPackagesReceiver, 86 new UserHandle(mUserId), filter, null, mHandler); 87 88 // Scan packages. 89 // Also has the side-effect of restarting providers if needed. 90 mHandler.post(mScanPackagesRunnable); 91 } 92 } 93 94 public void stop() { 95 if (mRunning) { 96 mRunning = false; 97 98 mContext.unregisterReceiver(mScanPackagesReceiver); 99 mHandler.removeCallbacks(mScanPackagesRunnable); 100 101 // Stop all providers. 102 for (int i = mProviders.size() - 1; i >= 0; i--) { 103 mProviders.get(i).stop(); 104 } 105 } 106 } 107 108 private void scanPackages() { 109 if (!mRunning) { 110 return; 111 } 112 113 // Add providers for all new services. 114 // Reorder the list so that providers left at the end will be the ones to remove. 115 int targetIndex = 0; 116 Intent intent = new Intent(RemoteDisplayState.SERVICE_INTERFACE); 117 for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser( 118 intent, 0, mUserId)) { 119 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 120 if (serviceInfo != null && verifyServiceTrusted(serviceInfo)) { 121 int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name); 122 if (sourceIndex < 0) { 123 RemoteDisplayProviderProxy provider = 124 new RemoteDisplayProviderProxy(mContext, 125 new ComponentName(serviceInfo.packageName, serviceInfo.name), 126 mUserId); 127 provider.start(); 128 mProviders.add(targetIndex++, provider); 129 mCallback.addProvider(provider); 130 } else if (sourceIndex >= targetIndex) { 131 RemoteDisplayProviderProxy provider = mProviders.get(sourceIndex); 132 provider.start(); // restart the provider if needed 133 provider.rebindIfDisconnected(); 134 Collections.swap(mProviders, sourceIndex, targetIndex++); 135 } 136 } 137 } 138 139 // Remove providers for missing services. 140 if (targetIndex < mProviders.size()) { 141 for (int i = mProviders.size() - 1; i >= targetIndex; i--) { 142 RemoteDisplayProviderProxy provider = mProviders.get(i); 143 mCallback.removeProvider(provider); 144 mProviders.remove(provider); 145 provider.stop(); 146 } 147 } 148 } 149 150 private boolean verifyServiceTrusted(ServiceInfo serviceInfo) { 151 if (serviceInfo.permission == null || !serviceInfo.permission.equals( 152 Manifest.permission.BIND_REMOTE_DISPLAY)) { 153 // If the service does not require this permission then any app could 154 // potentially bind to it and cause the remote display service to 155 // misbehave. So we only want to trust providers that require the 156 // correct permissions. 157 Slog.w(TAG, "Ignoring remote display provider service because it did not " 158 + "require the BIND_REMOTE_DISPLAY permission in its manifest: " 159 + serviceInfo.packageName + "/" + serviceInfo.name); 160 return false; 161 } 162 if (!hasCaptureVideoPermission(serviceInfo.packageName)) { 163 // If the service does not have permission to capture video then it 164 // isn't going to be terribly useful as a remote display, is it? 165 // Kind of makes you wonder what it's doing there in the first place. 166 Slog.w(TAG, "Ignoring remote display provider service because it does not " 167 + "have the CAPTURE_VIDEO_OUTPUT or CAPTURE_SECURE_VIDEO_OUTPUT " 168 + "permission: " + serviceInfo.packageName + "/" + serviceInfo.name); 169 return false; 170 } 171 // Looks good. 172 return true; 173 } 174 175 private boolean hasCaptureVideoPermission(String packageName) { 176 if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT, 177 packageName) == PackageManager.PERMISSION_GRANTED) { 178 return true; 179 } 180 if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT, 181 packageName) == PackageManager.PERMISSION_GRANTED) { 182 return true; 183 } 184 return false; 185 } 186 187 private int findProvider(String packageName, String className) { 188 int count = mProviders.size(); 189 for (int i = 0; i < count; i++) { 190 RemoteDisplayProviderProxy provider = mProviders.get(i); 191 if (provider.hasComponentName(packageName, className)) { 192 return i; 193 } 194 } 195 return -1; 196 } 197 198 private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() { 199 @Override 200 public void onReceive(Context context, Intent intent) { 201 if (DEBUG) { 202 Slog.d(TAG, "Received package manager broadcast: " + intent); 203 } 204 scanPackages(); 205 } 206 }; 207 208 private final Runnable mScanPackagesRunnable = new Runnable() { 209 @Override 210 public void run() { 211 scanPackages(); 212 } 213 }; 214 215 public interface Callback { 216 void addProvider(RemoteDisplayProviderProxy provider); 217 void removeProvider(RemoteDisplayProviderProxy provider); 218 } 219 } 220