Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 41f14f47 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Update Samsung Print Recommendation Plugin (to support Mopria printers)"

parents f3019867 989c44be
Loading
Loading
Loading
Loading
+3 −8
Original line number Diff line number Diff line
@@ -32,13 +32,6 @@
        <item>Hewlett Packard</item>
    </string-array>

    <!-- Samsung plugin -->
    <string-array name="known_print_vendor_info_for_samsung" translatable="false">
        <item>com.sec.app.samsungprintservice</item>
        <item>Samsung Electronics</item>
        <item>Samsung</item>
    </string-array>

    <!-- Xerox plugin -->
    <string-array name="known_print_vendor_info_for_xerox" translatable="false">
        <item>com.xerox.printservice</item>
@@ -49,6 +42,8 @@
    <array name="known_print_plugin_vendors" translatable="false">
        <item>@array/known_print_vendor_info_for_mopria</item>
        <item>@array/known_print_vendor_info_for_hp</item>
        <item>@array/known_print_vendor_info_for_samsung</item>
    </array>

    <!-- Samsung plugin -->
    <string name="plugin_package_samsung">com.sec.app.samsungprintservice</string>
</resources>
+1 −1
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ public class RecommendationServiceImpl extends RecommendationService

        try {
            mPlugins.add(new RemotePrintServicePlugin(new SamsungRecommendationPlugin(this), this,
                    false));
                    true));
        } catch (Exception e) {
            Log.e(LOG_TAG, "Could not initiate " + getString(R.string.plugin_vendor_samsung) +
                    " plugin", e);
+43 −129
Original line number Diff line number Diff line
@@ -16,57 +16,61 @@

package com.android.printservice.recommendation.plugin.mdnsFilter;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import android.annotation.NonNull;
import android.annotation.StringRes;

import com.android.printservice.recommendation.PrintServicePlugin;
import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
import com.android.printservice.recommendation.util.NsdResolveQueue;
import com.android.printservice.recommendation.util.MDNSFilteredDiscovery;
import com.android.printservice.recommendation.util.MDNSUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * A plugin listening for mDNS results and only adding the ones that {@link
 * MDNSUtils#isVendorPrinter match} configured list
 */
public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.DiscoveryListener {
    private static final String LOG_TAG = "MDNSFilterPlugin";
public class MDNSFilterPlugin implements PrintServicePlugin {

    private static final String PRINTER_SERVICE_TYPE = "_ipp._tcp";

    /** Name of the print service this plugin is for */
    private final @StringRes int mName;

    /** Package name of the print service this plugin is for */
    private final @NonNull CharSequence mPackageName;
    /** The mDNS service types supported */
    private static final Set<String> PRINTER_SERVICE_TYPES = new HashSet<String>() {{
        add("_ipp._tcp");
    }};

    /**
     * The printer filter for {@link MDNSFilteredDiscovery} passing only mDNS results
     * that {@link MDNSUtils#isVendorPrinter match} configured list
     */
    private static class VendorNameFilter implements MDNSFilteredDiscovery.PrinterFilter {
        /** mDNS names handled by the print service this plugin is for */
    private final @NonNull HashSet<String> mMDNSNames;

    /** Printer identifiers of the mPrinters found. */
    @GuardedBy("mLock")
    private final @NonNull HashSet<String> mPrinters;

    /** Context of the user of this plugin */
    private final @NonNull Context mContext;
        private final @NonNull Set<String> mMDNSNames;

        /**
     * Call back to report the number of mPrinters found.
         * Filter constructor
         *
     * We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
     * safe to not synchronize access to this field.
         * @param vendorNames The vendor names to pass
         */
    private @Nullable PrinterDiscoveryCallback mCallback;
        VendorNameFilter(@NonNull Set<String> vendorNames) {
            mMDNSNames = new HashSet<>(vendorNames);
        }

        @Override
        public boolean matchesCriteria(NsdServiceInfo nsdServiceInfo) {
            return MDNSUtils.isVendorPrinter(nsdServiceInfo, mMDNSNames);
        }
    }

    /** Name of the print service this plugin is for */
    private final @StringRes int mName;

    /** Package name of the print service this plugin is for */
    private final @NonNull CharSequence mPackageName;

    /** Queue used to resolve nsd infos */
    private final @NonNull NsdResolveQueue mResolveQueue;
    /** The mDNS filtered discovery */
    private final MDNSFilteredDiscovery mMDNSFilteredDiscovery;

    /**
     * Create new stub that assumes that a print service can be used to print on all mPrinters
@@ -79,16 +83,11 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover
     */
    public MDNSFilterPlugin(@NonNull Context context, @NonNull String name,
            @NonNull CharSequence packageName, @NonNull List<String> mDNSNames) {
        mContext = Preconditions.checkNotNull(context, "context");
        mName = mContext.getResources().getIdentifier(Preconditions.checkStringNotEmpty(name,
                "name"), null, "com.android.printservice.recommendation");
        mPackageName = Preconditions.checkStringNotEmpty(packageName);
        mMDNSNames = new HashSet<>(Preconditions
                .checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(mDNSNames,
                        "mDNSNames"), "mDNSNames"));

        mResolveQueue = NsdResolveQueue.getInstance();
        mPrinters = new HashSet<>();
        mName = context.getResources().getIdentifier(name, null,
                "com.android.printservice.recommendation");
        mPackageName = packageName;
        mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, PRINTER_SERVICE_TYPES,
                new VendorNameFilter(new HashSet<>(mDNSNames)));
    }

    @Override
@@ -96,18 +95,9 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover
        return mPackageName;
    }

    /**
     * @return The NDS manager
     */
    private NsdManager getNDSManager() {
        return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
    }

    @Override
    public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
        mCallback = callback;

        DiscoveryListenerMultiplexer.addListener(getNDSManager(), PRINTER_SERVICE_TYPE, this);
        mMDNSFilteredDiscovery.start(callback);
    }

    @Override
@@ -117,82 +107,6 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover

    @Override
    public void stop() throws Exception {
        mCallback.onChanged(0);
        mCallback = null;

        DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
    }

    @Override
    public void onStartDiscoveryFailed(String serviceType, int errorCode) {
        Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
                + errorCode);
    }

    @Override
    public void onStopDiscoveryFailed(String serviceType, int errorCode) {
        Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
                + errorCode);
    }

    @Override
    public void onDiscoveryStarted(String serviceType) {
        // empty
    }

    @Override
    public void onDiscoveryStopped(String serviceType) {
        mPrinters.clear();
    }

    @Override
    public void onServiceFound(NsdServiceInfo serviceInfo) {
        mResolveQueue.resolve(getNDSManager(), serviceInfo,
                new NsdManager.ResolveListener() {
            @Override
            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.w(LOG_TAG, "Service found: could not resolve " + serviceInfo + ": " +
                        errorCode);
            }

            @Override
            public void onServiceResolved(NsdServiceInfo serviceInfo) {
                if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
                    if (mCallback != null) {
                        boolean added = mPrinters.add(serviceInfo.getHost().getHostAddress());

                        if (added) {
                            mCallback.onChanged(mPrinters.size());
                        }
                    }
                }
            }
        });
    }

    @Override
    public void onServiceLost(NsdServiceInfo serviceInfo) {
        mResolveQueue.resolve(getNDSManager(), serviceInfo,
                new NsdManager.ResolveListener() {
            @Override
            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
                        + errorCode);
            }

            @Override
            public void onServiceResolved(NsdServiceInfo serviceInfo) {
                if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
                    if (mCallback != null) {
                        boolean removed = mPrinters
                                .remove(serviceInfo.getHost().getHostAddress());

                        if (removed) {
                            mCallback.onChanged(mPrinters.size());
                        }
                    }
                }
            }
        });
        mMDNSFilteredDiscovery.stop();
    }
}
+0 −74
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.printservice.recommendation.plugin.samsung;

import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;

import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;

public class MDnsUtils {
    public static final String ATTRIBUTE__TY = "ty";
    public static final String ATTRIBUTE__PRODUCT = "product";
    public static final String ATTRIBUTE__USB_MFG = "usb_MFG";
    public static final String ATTRIBUTE__MFG = "mfg";

    public static String getString(byte[] value) {
        if (value != null) return new String(value,StandardCharsets.UTF_8);
        return null;
    }

    public static boolean isVendorPrinter(NsdServiceInfo networkDevice, String[] vendorValues) {

        Map<String,byte[]> attributes = networkDevice.getAttributes();
        String product = getString(attributes.get(ATTRIBUTE__PRODUCT));
        String ty = getString(attributes.get(ATTRIBUTE__TY));
        String usbMfg = getString(attributes.get(ATTRIBUTE__USB_MFG));
        String mfg = getString(attributes.get(ATTRIBUTE__MFG));
        return containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) || containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues);

    }

    public static String getVendor(NsdServiceInfo networkDevice) {
        String vendor;

        Map<String,byte[]> attributes = networkDevice.getAttributes();
        vendor = getString(attributes.get(ATTRIBUTE__MFG));
        if (!TextUtils.isEmpty(vendor)) return vendor;
        vendor = getString(attributes.get(ATTRIBUTE__USB_MFG));
        if (!TextUtils.isEmpty(vendor)) return vendor;

        return null;
    }

    private static boolean containsVendor(String container, String[] vendorValues) {
        if ((container == null) || (vendorValues == null)) return false;
        for (String value : vendorValues) {
            if (containsString(container, value)
                || containsString(container.toLowerCase(Locale.US), value.toLowerCase(Locale.US))
                || containsString(container.toUpperCase(Locale.US), value.toUpperCase(Locale.US)))
                return true;
        }
        return false;
    }

    private static boolean containsString(String container, String contained) {
        return (container != null) && (contained != null) && (container.equalsIgnoreCase(contained) || container.contains(contained + " "));
    }
}
+63 −0
Original line number Diff line number Diff line
/*
 * (c) Copyright 2016 Samsung Electronics
 * (c) Copyright 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.printservice.recommendation.plugin.samsung;

import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import android.util.Log;

import com.android.printservice.recommendation.util.MDNSFilteredDiscovery;
import com.android.printservice.recommendation.util.MDNSUtils;

import java.util.HashSet;
import java.util.Set;

/**
 * Printer filter for Mopria printer models supported by the print service plugin
 */
class PrinterFilterMopria implements MDNSFilteredDiscovery.PrinterFilter {
    private static final String TAG = "PrinterFilterMopria";

    static final Set<String> MOPRIA_MDNS_SERVICES = new HashSet<String>() {{
        add("_ipp._tcp");
        add("_ipps._tcp");
    }};

    private static final String PDL__PDF = "application/pdf";
    private static final String PDL__PCLM = "application/PCLm";
    private static final String PDL__PWG_RASTER = "image/pwg-raster";

    private static final String PDL_ATTRIBUTE = "pdl";

    @Override
    public boolean matchesCriteria(NsdServiceInfo nsdServiceInfo) {
        if (!MDNSUtils.isSupportedServiceType(nsdServiceInfo, MOPRIA_MDNS_SERVICES)) {
            return false;
        }

        String pdls = MDNSUtils.getString(nsdServiceInfo.getAttributes().get(PDL_ATTRIBUTE));
        boolean isMatch = !TextUtils.isEmpty(pdls)
                && (pdls.contains(PDL__PDF)
                || pdls.contains(PDL__PCLM)
                || pdls.contains(PDL__PWG_RASTER));

        if (isMatch) {
            Log.d(TAG, "Mopria printer found: " + nsdServiceInfo.getServiceName());
        }
        return isMatch;
    }
}
Loading