Home | History | Annotate | Download | only in chromoting
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.chromoting;
      6 
      7 import android.app.Activity;
      8 import android.text.TextUtils;
      9 import android.util.Log;
     10 
     11 import java.util.ArrayList;
     12 import java.util.Arrays;
     13 import java.util.List;
     14 
     15 /**
     16  * A manager for the capabilities of the Android client. Based on the negotiated set of
     17  * capabilities, it creates the associated ClientExtensions, and enables their communication with
     18  * the Chromoting host by dispatching extension messages appropriately.
     19  *
     20  * The CapabilityManager mirrors how the Chromoting host handles extension messages. For each
     21  * incoming extension message, runs through a list of HostExtensionSession objects, giving each one
     22  * a chance to handle the message.
     23  *
     24  * The CapabilityManager is a singleton class so we can manage client extensions on an application
     25  * level. The singleton object may be used from multiple Activities, thus allowing it to support
     26  * different capabilities at different stages of the application.
     27  */
     28 public class CapabilityManager {
     29 
     30     /** Lazily-initialized singleton object that can be used from different Activities. */
     31     private static CapabilityManager sInstance;
     32 
     33     /** Protects access to |sInstance|. */
     34     private static final Object sInstanceLock = new Object();
     35 
     36     /** List of all capabilities that are supported by the application. */
     37     private List<String> mLocalCapabilities;
     38 
     39     /** List of negotiated capabilities received from the host. */
     40     private List<String> mNegotiatedCapabilities;
     41 
     42     /** List of extensions to the client based on capabilities negotiated with the host. */
     43     private List<ClientExtension> mClientExtensions;
     44 
     45     private CapabilityManager() {
     46         mLocalCapabilities = new ArrayList<String>();
     47         mClientExtensions = new ArrayList<ClientExtension>();
     48 
     49         mLocalCapabilities.add(Capabilities.CAST_CAPABILITY);
     50     }
     51 
     52     /**
     53      * Returns the singleton object. Thread-safe.
     54      */
     55     public static CapabilityManager getInstance() {
     56         synchronized (sInstanceLock) {
     57             if (sInstance == null) {
     58                 sInstance = new CapabilityManager();
     59             }
     60             return sInstance;
     61         }
     62     }
     63 
     64     /**
     65      * Returns a space-separated list (required by host) of the capabilities supported by
     66      * this client.
     67      */
     68     public String getLocalCapabilities() {
     69         return TextUtils.join(" ", mLocalCapabilities);
     70     }
     71 
     72     /**
     73      * Returns the ActivityLifecycleListener associated with the specified capability, if
     74      * |capability| is enabled and such a listener exists.
     75      *
     76      * Activities that call this method agree to appropriately notify the listener of lifecycle
     77      * events., thus supporting |capability|. This allows extensions like the CastExtensionHandler
     78      * to hook into an existing activity's lifecycle.
     79      */
     80     public ActivityLifecycleListener onActivityAcceptingListener(
     81             Activity activity, String capability) {
     82 
     83         ActivityLifecycleListener listener;
     84 
     85         if (isCapabilityEnabled(capability)) {
     86             for (ClientExtension ext : mClientExtensions) {
     87                 if (ext.getCapability().equals(capability)) {
     88                     listener = ext.onActivityAcceptingListener(activity);
     89                     if (listener != null)
     90                         return listener;
     91                 }
     92             }
     93         }
     94 
     95         return new DummyActivityLifecycleListener();
     96     }
     97 
     98     /**
     99      * Receives the capabilities negotiated between client and host and creates the appropriate
    100      * extension handlers.
    101      *
    102      * Currently only the CAST_CAPABILITY exists, so that is the only extension constructed.
    103      */
    104     public void setNegotiatedCapabilities(String capabilities) {
    105         mNegotiatedCapabilities = Arrays.asList(capabilities.split(" "));
    106         mClientExtensions.clear();
    107         if (isCapabilityEnabled(Capabilities.CAST_CAPABILITY)) {
    108             mClientExtensions.add(maybeCreateCastExtensionHandler());
    109         }
    110     }
    111 
    112     /**
    113      * Passes the deconstructed extension message to each ClientExtension in turn until the message
    114      * is handled or none remain. Returns true if the message was handled.
    115      */
    116     public boolean onExtensionMessage(String type, String data) {
    117         if (type == null || type.isEmpty()) {
    118             return false;
    119         }
    120 
    121         for (ClientExtension ext : mClientExtensions) {
    122             if (ext.onExtensionMessage(type, data)) {
    123                 return true;
    124             }
    125         }
    126         return false;
    127     }
    128 
    129     /**
    130      * Return true if the capability is enabled for this connection with the host.
    131      */
    132     private boolean isCapabilityEnabled(String capability) {
    133         return (mNegotiatedCapabilities != null && mNegotiatedCapabilities.contains(capability));
    134     }
    135 
    136     /**
    137      * Tries to reflectively instantiate a CastExtensionHandler object.
    138      *
    139      * Note: The ONLY reason this is done is that by default, the regular android application
    140      * will be built, without this experimental extension.
    141      */
    142     private ClientExtension maybeCreateCastExtensionHandler() {
    143         try {
    144             Class<?> cls = Class.forName("org.chromium.chromoting.CastExtensionHandler");
    145             return (ClientExtension) cls.newInstance();
    146         } catch (ClassNotFoundException e) {
    147             Log.w("CapabilityManager", "Failed to create CastExtensionHandler.");
    148             return new DummyClientExtension();
    149         } catch (InstantiationException e) {
    150             Log.w("CapabilityManager", "Failed to create CastExtensionHandler.");
    151             return new DummyClientExtension();
    152         } catch (IllegalAccessException e) {
    153             Log.w("CapabilityManager", "Failed to create CastExtensionHandler.");
    154             return new DummyClientExtension();
    155         }
    156     }
    157 }
    158