Home | History | Annotate | Download | only in util
      1 /*
      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 package com.android.printservice.recommendation.util;
     17 
     18 import android.annotation.NonNull;
     19 import android.annotation.Nullable;
     20 import android.content.Context;
     21 import android.net.nsd.NsdManager;
     22 import android.net.nsd.NsdServiceInfo;
     23 import android.util.Log;
     24 
     25 import com.android.internal.annotations.GuardedBy;
     26 import com.android.internal.util.Preconditions;
     27 import com.android.printservice.recommendation.PrintServicePlugin;
     28 
     29 import java.net.InetAddress;
     30 import java.util.ArrayList;
     31 import java.util.HashSet;
     32 import java.util.Set;
     33 
     34 /**
     35  * A discovery listening for mDNS results and only adding the ones that {@link
     36  * PrinterFilter#matchesCriteria match} configured list
     37  */
     38 public class MDNSFilteredDiscovery implements NsdManager.DiscoveryListener  {
     39     private static final String LOG_TAG = "MDNSFilteredDiscovery";
     40 
     41     /**
     42      * mDNS service filter interface.
     43      * Implement {@link PrinterFilter#matchesCriteria} to filter out supported services
     44      */
     45     public interface PrinterFilter {
     46         /**
     47          * Main filter method. Should return true if mDNS service is supported
     48          * by the print service plugin
     49          *
     50          * @param nsdServiceInfo The service info to check
     51          *
     52          * @return True if service is supported by the print service plugin
     53          */
     54         boolean matchesCriteria(NsdServiceInfo nsdServiceInfo);
     55     }
     56 
     57     /** Printer identifiers of the mPrinters found. */
     58     @GuardedBy("mLock")
     59     private final @NonNull HashSet<InetAddress> mPrinters;
     60 
     61     /** Service types discovered by this plugin */
     62     private final @NonNull HashSet<String> mServiceTypes;
     63 
     64     /** Context of the user of this plugin */
     65     private final @NonNull Context mContext;
     66 
     67     /** mDNS services filter */
     68     private final @NonNull PrinterFilter mPrinterFilter;
     69 
     70     /**
     71      * Call back to report the number of mPrinters found.
     72      *
     73      * We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
     74      * safe to not synchronize access to this field.
     75      */
     76     private @Nullable PrintServicePlugin.PrinterDiscoveryCallback mCallback;
     77 
     78     /** Queue used to resolve nsd infos */
     79     private final @NonNull NsdResolveQueue mResolveQueue;
     80 
     81     /**
     82      * Create new stub that assumes that a print service can be used to print on all mPrinters
     83      * matching some mDNS names.
     84      *
     85      * @param context       The context the plugin runs in
     86      * @param serviceTypes  The mDNS service types to listen to.
     87      * @param printerFilter The filter for mDNS services
     88      */
     89     public MDNSFilteredDiscovery(@NonNull Context context,
     90             @NonNull Set<String> serviceTypes,
     91             @NonNull PrinterFilter printerFilter) {
     92         mContext = Preconditions.checkNotNull(context, "context");
     93         mServiceTypes = new HashSet<>(Preconditions
     94                 .checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(serviceTypes,
     95                         "serviceTypes"), "serviceTypes"));
     96         mPrinterFilter = Preconditions.checkNotNull(printerFilter, "printerFilter");
     97 
     98         mResolveQueue = NsdResolveQueue.getInstance();
     99         mPrinters = new HashSet<>();
    100     }
    101 
    102     /**
    103      * @return The NDS manager
    104      */
    105     private NsdManager getNDSManager() {
    106         return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
    107     }
    108 
    109     /**
    110      * Start the discovery.
    111      *
    112      * @param callback Callbacks used by this plugin.
    113      */
    114     public void start(@NonNull PrintServicePlugin.PrinterDiscoveryCallback callback) {
    115         mCallback = callback;
    116         mCallback.onChanged(new ArrayList<>(mPrinters));
    117 
    118         for (String serviceType : mServiceTypes) {
    119             DiscoveryListenerMultiplexer.addListener(getNDSManager(), serviceType, this);
    120         }
    121     }
    122 
    123     /**
    124      * Stop the discovery. This can only return once the plugin is completely finished and cleaned up.
    125      */
    126     public void stop() {
    127         mCallback.onChanged(null);
    128         mCallback = null;
    129 
    130         for (int i = 0; i < mServiceTypes.size(); ++i) {
    131             DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
    132         }
    133     }
    134 
    135     @Override
    136     public void onStartDiscoveryFailed(String serviceType, int errorCode) {
    137         Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
    138                 + errorCode);
    139     }
    140 
    141     @Override
    142     public void onStopDiscoveryFailed(String serviceType, int errorCode) {
    143         Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
    144                 + errorCode);
    145     }
    146 
    147     @Override
    148     public void onDiscoveryStarted(String serviceType) {
    149         // empty
    150     }
    151 
    152     @Override
    153     public void onDiscoveryStopped(String serviceType) {
    154         mPrinters.clear();
    155     }
    156 
    157     @Override
    158     public void onServiceFound(NsdServiceInfo serviceInfo) {
    159         mResolveQueue.resolve(getNDSManager(), serviceInfo,
    160                 new NsdManager.ResolveListener() {
    161                     @Override
    162                     public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
    163                         Log.w(LOG_TAG, "Service found: could not resolve " + serviceInfo + ": " +
    164                                 errorCode);
    165                     }
    166 
    167                     @Override
    168                     public void onServiceResolved(NsdServiceInfo serviceInfo) {
    169                         if (mPrinterFilter.matchesCriteria(serviceInfo)) {
    170                             if (mCallback != null) {
    171                                 boolean added = mPrinters.add(serviceInfo.getHost());
    172                                 if (added) {
    173                                     mCallback.onChanged(new ArrayList<>(mPrinters));
    174                                 }
    175                             }
    176                         }
    177                     }
    178                 });
    179     }
    180 
    181     @Override
    182     public void onServiceLost(NsdServiceInfo serviceInfo) {
    183         mResolveQueue.resolve(getNDSManager(), serviceInfo,
    184                 new NsdManager.ResolveListener() {
    185                     @Override
    186                     public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
    187                         Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
    188                                 + errorCode);
    189                     }
    190 
    191                     @Override
    192                     public void onServiceResolved(NsdServiceInfo serviceInfo) {
    193                         if (mPrinterFilter.matchesCriteria(serviceInfo)) {
    194                             if (mCallback != null) {
    195                                 boolean removed = mPrinters.remove(serviceInfo.getHost());
    196 
    197                                 if (removed) {
    198                                     mCallback.onChanged(new ArrayList<>(mPrinters));
    199                                 }
    200                             }
    201                         }
    202                     }
    203                 });
    204     }
    205 }