Home | History | Annotate | Download | only in media
      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