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

Commit 55b91fec authored by Sergey Yakovlev's avatar Sergey Yakovlev Committed by android-build-merger
Browse files

Merge "Update Samsung Print Recommendation Plugin (to support Mopria printers)" am: 41f14f47

am: 3a1cf228

Change-Id: Id94c02dc6106b5eb15274926ecd24e43c0ffc76f
parents e3c4a3bc 3a1cf228
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