19dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann/*
29dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * Copyright (C) 2016 The Android Open Source Project
39dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann *
49dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * Licensed under the Apache License, Version 2.0 (the "License");
59dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * you may not use this file except in compliance with the License.
69dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * You may obtain a copy of the License at
79dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann *
89dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann *      http://www.apache.org/licenses/LICENSE-2.0
99dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann *
109dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * Unless required by applicable law or agreed to in writing, software
119dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * distributed under the License is distributed on an "AS IS" BASIS,
129dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * See the License for the specific language governing permissions and
149dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * limitations under the License.
159dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann */
169dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
179dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannpackage com.android.printservice.recommendation.plugin.mdnsFilter;
189dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
199dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.annotation.NonNull;
209dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.annotation.Nullable;
219dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.annotation.StringRes;
229dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.content.Context;
239dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.net.nsd.NsdManager;
249dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.net.nsd.NsdServiceInfo;
259dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport android.util.Log;
269dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport com.android.internal.annotations.GuardedBy;
279dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport com.android.internal.util.Preconditions;
289dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport com.android.printservice.recommendation.PrintServicePlugin;
29bfaa47233215996b8554a4b7a1a7b36bb3eaf607Philip P. Moltmannimport com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
309dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport com.android.printservice.recommendation.util.NsdResolveQueue;
319dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
329dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport java.util.HashSet;
339dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannimport java.util.List;
349dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
359dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann/**
369dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * A plugin listening for mDNS results and only adding the ones that {@link
379dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann * MDNSUtils#isVendorPrinter match} configured list
389dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann */
399dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmannpublic class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.DiscoveryListener {
409dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private static final String LOG_TAG = "MDNSFilterPlugin";
419dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
429dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private static final String PRINTER_SERVICE_TYPE = "_ipp._tcp";
439dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
449dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /** Name of the print service this plugin is for */
459dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private final @StringRes int mName;
469dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
479dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /** Package name of the print service this plugin is for */
489dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private final @NonNull CharSequence mPackageName;
499dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
509dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /** mDNS names handled by the print service this plugin is for */
519dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private final @NonNull HashSet<String> mMDNSNames;
529dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
539dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /** Printer identifiers of the mPrinters found. */
549dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @GuardedBy("mLock")
559dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private final @NonNull HashSet<String> mPrinters;
569dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
579dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /** Context of the user of this plugin */
589dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private final @NonNull Context mContext;
599dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
609dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /**
619dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * Call back to report the number of mPrinters found.
629dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     *
639dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
649dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * safe to not synchronize access to this field.
659dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     */
669dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private @Nullable PrinterDiscoveryCallback mCallback;
679dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
689dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /** Queue used to resolve nsd infos */
699dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private final @NonNull NsdResolveQueue mResolveQueue;
709dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
719dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /**
729dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * Create new stub that assumes that a print service can be used to print on all mPrinters
739dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * matching some mDNS names.
749dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     *
759dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * @param context     The context the plugin runs in
769dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * @param name        The user friendly name of the print service
779dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * @param packageName The package name of the print service
789dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * @param mDNSNames   The mDNS names of the printer.
799dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     */
809dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public MDNSFilterPlugin(@NonNull Context context, @NonNull String name,
819dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            @NonNull CharSequence packageName, @NonNull List<String> mDNSNames) {
829dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mContext = Preconditions.checkNotNull(context, "context");
839dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mName = mContext.getResources().getIdentifier(Preconditions.checkStringNotEmpty(name,
84bfaa47233215996b8554a4b7a1a7b36bb3eaf607Philip P. Moltmann                "name"), null, "com.android.printservice.recommendation");
859dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mPackageName = Preconditions.checkStringNotEmpty(packageName);
869dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mMDNSNames = new HashSet<>(Preconditions
879dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                .checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(mDNSNames,
889dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        "mDNSNames"), "mDNSNames"));
899dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
909dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mResolveQueue = NsdResolveQueue.getInstance();
919dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mPrinters = new HashSet<>();
929dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
939dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
949dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
959dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public @NonNull CharSequence getPackageName() {
969dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        return mPackageName;
979dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
989dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
999dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    /**
1009dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     * @return The NDS manager
1019dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann     */
1029dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    private NsdManager getNDSManager() {
1039dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
1049dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1059dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1069dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1079dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
1089dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mCallback = callback;
1099dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
110bfaa47233215996b8554a4b7a1a7b36bb3eaf607Philip P. Moltmann        DiscoveryListenerMultiplexer.addListener(getNDSManager(), PRINTER_SERVICE_TYPE, this);
1119dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1129dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1139dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1149dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public @StringRes int getName() {
1159dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        return mName;
1169dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1179dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1189dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1199dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void stop() throws Exception {
1209dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mCallback.onChanged(0);
1219dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mCallback = null;
1229dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
123bfaa47233215996b8554a4b7a1a7b36bb3eaf607Philip P. Moltmann        DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
1249dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1259dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1269dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1279dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void onStartDiscoveryFailed(String serviceType, int errorCode) {
1289dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
1299dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                + errorCode);
1309dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1319dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1329dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1339dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void onStopDiscoveryFailed(String serviceType, int errorCode) {
1349dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
1359dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                + errorCode);
1369dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1379dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1389dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1399dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void onDiscoveryStarted(String serviceType) {
1409dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        // empty
1419dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1429dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1439dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1449dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void onDiscoveryStopped(String serviceType) {
1459dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mPrinters.clear();
1469dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1479dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1489dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1499dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void onServiceFound(NsdServiceInfo serviceInfo) {
1509dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mResolveQueue.resolve(getNDSManager(), serviceInfo,
1519dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                new NsdManager.ResolveListener() {
1529dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            @Override
1539dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
1549dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                Log.w(LOG_TAG, "Service found: could not resolve " + serviceInfo + ": " +
1559dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        errorCode);
1569dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            }
1579dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1589dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            @Override
1599dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            public void onServiceResolved(NsdServiceInfo serviceInfo) {
1609dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
1619dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                    if (mCallback != null) {
1629dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        boolean added = mPrinters.add(serviceInfo.getHost().getHostAddress());
1639dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1649dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        if (added) {
1659dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                            mCallback.onChanged(mPrinters.size());
1669dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        }
1679dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                    }
1689dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                }
1699dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            }
1709dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        });
1719dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1729dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1739dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    @Override
1749dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    public void onServiceLost(NsdServiceInfo serviceInfo) {
1759dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        mResolveQueue.resolve(getNDSManager(), serviceInfo,
1769dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                new NsdManager.ResolveListener() {
1779dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            @Override
1789dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
1799dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
1809dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        + errorCode);
1819dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            }
1829dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1839dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            @Override
1849dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            public void onServiceResolved(NsdServiceInfo serviceInfo) {
1859dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
1869dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                    if (mCallback != null) {
1879dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        boolean removed = mPrinters
1889dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                                .remove(serviceInfo.getHost().getHostAddress());
1899dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann
1909dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        if (removed) {
1919dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                            mCallback.onChanged(mPrinters.size());
1929dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                        }
1939dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                    }
1949dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann                }
1959dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann            }
1969dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann        });
1979dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann    }
1989dcb86a48d73f399fb1b5c020005d76d350eeac2Philip P. Moltmann}
199