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

This commit is contained in:
Treehugger Robot
2016-12-03 03:53:17 +00:00
committed by Gerrit Code Review
14 changed files with 544 additions and 770 deletions

View File

@@ -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>

View File

@@ -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);

View File

@@ -16,30 +16,52 @@
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";
/** 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 Set<String> mMDNSNames;
/**
* Filter constructor
*
* @param vendorNames The vendor names to pass
*/
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;
@@ -47,26 +69,8 @@ public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.Discover
/** Package name of the print service this plugin is for */
private final @NonNull CharSequence mPackageName;
/** 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;
/**
* Call back to report the number of mPrinters found.
*
* We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
* safe to not synchronize access to this field.
*/
private @Nullable PrinterDiscoveryCallback mCallback;
/** 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();
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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 + " "));
}
}

View File

@@ -0,0 +1,63 @@
/*
* (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;
}
}

View File

@@ -0,0 +1,117 @@
/*
* (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.annotation.NonNull;
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.Locale;
import java.util.Map;
import java.util.Set;
/**
* Printer filter for Samsung printer models supported by the print service plugin
*/
class PrinterFilterSamsung implements MDNSFilteredDiscovery.PrinterFilter {
private static final String TAG = "PrinterFilterSamsung";
static final Set<String> SAMSUNG_MDNS_SERVICES = new HashSet<String>() {{
add("_pdl-datastream._tcp");
}};
private static final String[] NOT_SUPPORTED_MODELS = new String[]{
"SCX-5x15",
"SF-555P",
"CF-555P",
"SCX-4x16",
"SCX-4214F",
"CLP-500",
"CJX-",
"MJC-"
};
private static final String ATTR_USB_MFG = "usb_MFG";
private static final String ATTR_MFG = "mfg";
private static final String ATTR_USB_MDL = "usb_MDL";
private static final String ATTR_MDL = "mdl";
private static final String ATTR_PRODUCT = "product";
private static final String ATTR_TY = "ty";
private static Set<String> SAMUNG_VENDOR_SET = new HashSet<String>() {{
add("samsung");
}};
@Override
public boolean matchesCriteria(NsdServiceInfo nsdServiceInfo) {
if (!MDNSUtils.isSupportedServiceType(nsdServiceInfo, SAMSUNG_MDNS_SERVICES)) {
return false;
}
if (!MDNSUtils.isVendorPrinter(nsdServiceInfo, SAMUNG_VENDOR_SET)) {
return false;
}
String modelName = getSamsungModelName(nsdServiceInfo);
if (modelName != null && isSupportedSamsungModel(modelName)) {
Log.d(TAG, "Samsung printer found: " + nsdServiceInfo.getServiceName());
return true;
}
return false;
}
private boolean isSupportedSamsungModel(String model) {
if (!TextUtils.isEmpty(model)) {
String modelToUpper = model.toUpperCase(Locale.US);
for (String unSupportedPrinter : NOT_SUPPORTED_MODELS) {
if (modelToUpper.contains(unSupportedPrinter)) {
return false;
}
}
}
return true;
}
private String getSamsungModelName(@NonNull NsdServiceInfo resolvedDevice) {
Map<String,byte[]> attributes = resolvedDevice.getAttributes();
String usb_mfg = MDNSUtils.getString(attributes.get(ATTR_USB_MFG));
if (TextUtils.isEmpty(usb_mfg)) {
usb_mfg = MDNSUtils.getString(attributes.get(ATTR_MFG));
}
String usb_mdl = MDNSUtils.getString(attributes.get(ATTR_USB_MDL));
if (TextUtils.isEmpty(usb_mdl)) {
usb_mdl = MDNSUtils.getString(attributes.get(ATTR_MDL));
}
String modelName;
if (!TextUtils.isEmpty(usb_mfg) && !TextUtils.isEmpty(usb_mdl)) {
modelName = usb_mfg.trim() + " " + usb_mdl.trim();
} else {
modelName = MDNSUtils.getString(attributes.get(ATTR_PRODUCT));
if (TextUtils.isEmpty(modelName)) {
modelName = MDNSUtils.getString(attributes.get(ATTR_TY));
}
}
return modelName;
}
}

View File

@@ -1,33 +0,0 @@
/*
* 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 java.util.HashMap;
final class PrinterHashMap extends HashMap<String, NsdServiceInfo> {
public static String getKey(NsdServiceInfo serviceInfo) {
return serviceInfo.getServiceName();
}
public NsdServiceInfo addPrinter(NsdServiceInfo device) {
return put(getKey(device), device);
}
public NsdServiceInfo removePrinter(NsdServiceInfo device) {
return remove(getKey(device));
}
}

View File

@@ -1,102 +1,69 @@
/*
(c) Copyright 2016 Samsung Electronics..
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.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import java.util.Locale;
import java.util.Map;
import com.android.printservice.recommendation.R;
public class SamsungRecommendationPlugin extends ServiceRecommendationPlugin {
private static final String TAG = "SamsungRecommendation";
private static final String ATTR_USB_MFG = "usb_MFG";
private static final String ATTR_MFG = "mfg";
private static final String ATTR_USB_MDL = "usb_MDL";
private static final String ATTR_MDL = "mdl";
private static final String ATTR_PRODUCT = "product";
private static final String ATTR_TY = "ty";
private static String[] mNotSupportedDevices = new String[]{
"SCX-5x15",
"SF-555P",
"CF-555P",
"SCX-4x16",
"SCX-4214F",
"CLP-500",
"CJX-",
"MJC-"
};
private static boolean isSupportedModel(String model) {
if (!TextUtils.isEmpty(model)) {
String modelToUpper = model.toUpperCase(Locale.US);
for (String unSupportedPrinter : mNotSupportedDevices) {
if (modelToUpper.contains(unSupportedPrinter)) {
return false;
}
}
}
return true;
}
public SamsungRecommendationPlugin(Context context) {
super(context, R.string.plugin_vendor_samsung, new VendorInfo(context.getResources(), R.array.known_print_vendor_info_for_samsung), new String[]{"_pdl-datastream._tcp"});
}
@Override
public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
if (!TextUtils.equals(vendor, mVendorInfo.mVendorID)) return false;
String modelName = getModelName(nsdServiceInfo);
if (modelName != null) {
return (isSupportedModel(modelName));
}
return false;
}
private String getModelName(NsdServiceInfo resolvedDevice) {
Map<String,byte[]> attributes = resolvedDevice.getAttributes();
String usb_mfg = MDnsUtils.getString(attributes.get(ATTR_USB_MFG));
if (TextUtils.isEmpty(usb_mfg)) {
usb_mfg = MDnsUtils.getString(attributes.get(ATTR_MFG));
}
String usb_mdl = MDnsUtils.getString(attributes.get(ATTR_USB_MDL));
if (TextUtils.isEmpty(usb_mdl)) {
usb_mdl = MDnsUtils.getString(attributes.get(ATTR_MDL));
}
String modelName = null;
if (!TextUtils.isEmpty(usb_mfg) && !TextUtils.isEmpty(usb_mdl)) {
modelName = usb_mfg.trim() + " " + usb_mdl.trim();
} else {
modelName = MDnsUtils.getString(attributes.get(ATTR_PRODUCT));
if (TextUtils.isEmpty(modelName)) {
modelName = MDnsUtils.getString(attributes.get(ATTR_TY));
}
}
return modelName;
}
}
/*
* (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.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.annotation.NonNull;
import com.android.printservice.recommendation.PrintServicePlugin;
import com.android.printservice.recommendation.R;
import com.android.printservice.recommendation.util.MDNSFilteredDiscovery;
import java.util.HashSet;
import java.util.Set;
public class SamsungRecommendationPlugin implements PrintServicePlugin {
private static final Set<String> ALL_MDNS_SERVICES = new HashSet<String>() {{
addAll(PrinterFilterMopria.MOPRIA_MDNS_SERVICES);
addAll(PrinterFilterSamsung.SAMSUNG_MDNS_SERVICES);
}};
private final @NonNull Context mContext;
private final @NonNull MDNSFilteredDiscovery mMDNSFilteredDiscovery;
private final @NonNull PrinterFilterSamsung mPrinterFilterSamsung = new PrinterFilterSamsung();
private final @NonNull PrinterFilterMopria mPrinterFilterMopria = new PrinterFilterMopria();
public SamsungRecommendationPlugin(@NonNull Context context) {
mContext = context;
mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, ALL_MDNS_SERVICES,
(NsdServiceInfo nsdServiceInfo) ->
mPrinterFilterSamsung.matchesCriteria(nsdServiceInfo) ||
mPrinterFilterMopria.matchesCriteria(nsdServiceInfo));
}
@Override
public int getName() {
return R.string.plugin_vendor_samsung;
}
@Override
public @NonNull CharSequence getPackageName() {
return mContext.getString(R.string.plugin_package_samsung);
}
@Override
public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
mMDNSFilteredDiscovery.start(callback);
}
@Override
public void stop() throws Exception {
mMDNSFilteredDiscovery.stop();
}
}

View File

@@ -1,186 +0,0 @@
/*
* 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.content.Context;
import android.content.res.TypedArray;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.text.TextUtils;
import android.util.Pair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.android.printservice.recommendation.R;
import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
public class ServiceListener implements ServiceResolveQueue.ResolveCallback {
private final NsdManager mNSDManager;
private final Map<String, VendorInfo> mVendorInfoHashMap;
private final String[] mServiceType;
private final Observer mObserver;
private final ServiceResolveQueue mResolveQueue;
private List<NsdManager.DiscoveryListener> mListeners = new ArrayList<>();
public HashMap<String, PrinterHashMap> mVendorHashMap = new HashMap<>();
public interface Observer {
boolean matchesCriteria(String vendor, NsdServiceInfo serviceInfo);
void dataSetChanged();
}
public ServiceListener(Context context, Observer observer, String[] serviceTypes) {
mObserver = observer;
mServiceType = serviceTypes;
mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
mResolveQueue = ServiceResolveQueue.getInstance(mNSDManager);
Map<String, VendorInfo> vendorInfoMap = new HashMap<>();
TypedArray testArray = context.getResources().obtainTypedArray(R.array.known_print_plugin_vendors);
for(int i = 0; i < testArray.length(); i++) {
int arrayID = testArray.getResourceId(i, 0);
if (arrayID != 0) {
VendorInfo info = new VendorInfo(context.getResources(), arrayID);
vendorInfoMap.put(info.mVendorID, info);
vendorInfoMap.put(info.mPackageName, info);
}
}
testArray.recycle();
mVendorInfoHashMap = vendorInfoMap;
}
@Override
public void serviceResolved(NsdServiceInfo nsdServiceInfo) {
printerFound(nsdServiceInfo);
}
private synchronized void printerFound(NsdServiceInfo nsdServiceInfo) {
if (nsdServiceInfo == null) return;
if (TextUtils.isEmpty(PrinterHashMap.getKey(nsdServiceInfo))) return;
String vendor = MDnsUtils.getVendor(nsdServiceInfo);
if (vendor == null) vendor = "";
for(Map.Entry<String,VendorInfo> entry : mVendorInfoHashMap.entrySet()) {
for(String vendorValues : entry.getValue().mDNSValues) {
if (vendor.equalsIgnoreCase(vendorValues)) {
vendor = entry.getValue().mVendorID;
break;
}
}
// intentional pointer check
//noinspection StringEquality
if ((vendor != entry.getValue().mVendorID) &&
MDnsUtils.isVendorPrinter(nsdServiceInfo, entry.getValue().mDNSValues)) {
vendor = entry.getValue().mVendorID;
}
// intentional pointer check
//noinspection StringEquality
if (vendor == entry.getValue().mVendorID) break;
}
if (TextUtils.isEmpty(vendor)) {
return;
}
if (!mObserver.matchesCriteria(vendor, nsdServiceInfo))
return;
boolean mapsChanged;
PrinterHashMap vendorHash = mVendorHashMap.get(vendor);
if (vendorHash == null) {
vendorHash = new PrinterHashMap();
}
mapsChanged = (vendorHash.addPrinter(nsdServiceInfo) == null);
mVendorHashMap.put(vendor, vendorHash);
if (mapsChanged) {
mObserver.dataSetChanged();
}
}
private synchronized void printerRemoved(NsdServiceInfo nsdServiceInfo) {
boolean wasRemoved = false;
Set<String> vendors = mVendorHashMap.keySet();
for(String vendor : vendors) {
PrinterHashMap map = mVendorHashMap.get(vendor);
wasRemoved |= (map.removePrinter(nsdServiceInfo) != null);
if (map.isEmpty()) wasRemoved |= (mVendorHashMap.remove(vendor) != null);
}
if (wasRemoved) {
mObserver.dataSetChanged();
}
}
public void start() {
stop();
for(final String service :mServiceType) {
NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
@Override
public void onStartDiscoveryFailed(String s, int i) {
}
@Override
public void onStopDiscoveryFailed(String s, int i) {
}
@Override
public void onDiscoveryStarted(String s) {
}
@Override
public void onDiscoveryStopped(String s) {
}
@Override
public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
mResolveQueue.queueRequest(nsdServiceInfo, ServiceListener.this);
}
@Override
public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
mResolveQueue.removeRequest(nsdServiceInfo, ServiceListener.this);
printerRemoved(nsdServiceInfo);
}
};
DiscoveryListenerMultiplexer.addListener(mNSDManager, service, listener);
mListeners.add(listener);
}
}
public void stop() {
for(NsdManager.DiscoveryListener listener : mListeners) {
DiscoveryListenerMultiplexer.removeListener(mNSDManager, listener);
}
mVendorHashMap.clear();
mListeners.clear();
}
public Pair<Integer, Integer> getCount() {
int count = 0;
for (PrinterHashMap map : mVendorHashMap.values()) {
count += map.size();
}
return Pair.create(mVendorHashMap.size(), count);
}
}

View File

@@ -1,86 +0,0 @@
/*
* 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.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.annotation.NonNull;
import android.text.TextUtils;
import com.android.printservice.recommendation.PrintServicePlugin;
public abstract class ServiceRecommendationPlugin implements PrintServicePlugin, ServiceListener.Observer {
protected static final String PDL_ATTRIBUTE = "pdl";
protected final Object mLock = new Object();
protected PrinterDiscoveryCallback mCallback = null;
protected final ServiceListener mListener;
protected final NsdManager mNSDManager;
protected final VendorInfo mVendorInfo;
private final int mVendorStringID;
protected ServiceRecommendationPlugin(Context context, int vendorStringID, VendorInfo vendorInfo, String[] services) {
mNSDManager = (NsdManager)context.getSystemService(Context.NSD_SERVICE);
mVendorStringID = vendorStringID;
mVendorInfo = vendorInfo;
mListener = new ServiceListener(context, this, services);
}
@Override
public int getName() {
return mVendorStringID;
}
@NonNull
@Override
public CharSequence getPackageName() {
return mVendorInfo.mPackageName;
}
@Override
public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
synchronized (mLock) {
mCallback = callback;
}
mListener.start();
}
@Override
public void stop() throws Exception {
synchronized (mLock) {
mCallback = null;
}
mListener.stop();
}
@Override
public void dataSetChanged() {
synchronized (mLock) {
if (mCallback != null) mCallback.onChanged(getCount());
}
}
@Override
public boolean matchesCriteria(String vendor, NsdServiceInfo nsdServiceInfo) {
return TextUtils.equals(vendor, mVendorInfo.mVendorID);
}
public int getCount() {
return mListener.getCount().second;
}
}

View File

@@ -1,109 +0,0 @@
/*
* 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.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Pair;
import com.android.printservice.recommendation.util.NsdResolveQueue;
import java.util.LinkedList;
final class ServiceResolveQueue {
private final NsdManager mNsdManager;
private final LinkedList<Pair<NsdServiceInfo, ResolveCallback>> mQueue = new LinkedList<>();
private final Object mLock = new Object();
private static Object sLock = new Object();
private static ServiceResolveQueue sInstance = null;
private final NsdResolveQueue mNsdResolveQueue;
private Pair<NsdServiceInfo, ResolveCallback> mCurrentRequest = null;
public static void createInstance(NsdManager nsdManager) {
if (sInstance == null) sInstance = new ServiceResolveQueue(nsdManager);
}
public static ServiceResolveQueue getInstance(NsdManager nsdManager) {
synchronized (sLock) {
createInstance(nsdManager);
return sInstance;
}
}
public static void destroyInstance() {
sInstance = null;
}
public interface ResolveCallback {
void serviceResolved(NsdServiceInfo nsdServiceInfo);
}
public ServiceResolveQueue(NsdManager nsdManager) {
mNsdManager = nsdManager;
mNsdResolveQueue = NsdResolveQueue.getInstance();
}
public void queueRequest(NsdServiceInfo serviceInfo, ResolveCallback callback) {
synchronized (mLock) {
Pair<NsdServiceInfo, ResolveCallback> newRequest = Pair.create(serviceInfo, callback);
if (mQueue.contains(newRequest)) return;
mQueue.add(newRequest);
makeNextRequest();
}
}
public void removeRequest(NsdServiceInfo serviceInfo, ResolveCallback callback) {
synchronized (mLock) {
Pair<NsdServiceInfo, ResolveCallback> newRequest = Pair.create(serviceInfo, callback);
mQueue.remove(newRequest);
if ((mCurrentRequest != null) && newRequest.equals(mCurrentRequest)) mCurrentRequest = null;
}
}
private void makeNextRequest() {
synchronized (mLock) {
if (mCurrentRequest != null) return;
if (mQueue.isEmpty()) return;
mCurrentRequest = mQueue.removeFirst();
mNsdResolveQueue.resolve(mNsdManager, mCurrentRequest.first,
new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
synchronized (mLock) {
if (mCurrentRequest != null) mQueue.add(mCurrentRequest);
makeNextRequest();
}
}
@Override
public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
synchronized (mLock) {
if (mCurrentRequest != null) {
mCurrentRequest.second.serviceResolved(nsdServiceInfo);
mCurrentRequest = null;
}
makeNextRequest();
}
}
});
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* 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.content.res.Resources;
import java.util.Arrays;
public final class VendorInfo {
public final String mPackageName;
public final String mVendorID;
public final String[] mDNSValues;
public final int mID;
public VendorInfo(Resources resources, int vendor_info_id) {
mID = vendor_info_id;
String[] data = resources.getStringArray(vendor_info_id);
if ((data == null) || (data.length < 2)) {
data = new String[] { null, null };
}
mPackageName = data[0];
mVendorID = data[1];
mDNSValues = (data.length > 2) ? Arrays.copyOfRange(data, 2, data.length) : new String[]{};
}
}

View File

@@ -0,0 +1,212 @@
/*
* 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.util;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.printservice.recommendation.PrintServicePlugin;
import java.util.HashSet;
import java.util.Set;
/**
* A discovery listening for mDNS results and only adding the ones that {@link
* PrinterFilter#matchesCriteria match} configured list
*/
public class MDNSFilteredDiscovery implements NsdManager.DiscoveryListener {
private static final String LOG_TAG = "MDNSFilteredDiscovery";
/**
* mDNS service filter interface.
* Implement {@link PrinterFilter#matchesCriteria} to filter out supported services
*/
public interface PrinterFilter {
/**
* Main filter method. Should return true if mDNS service is supported
* by the print service plugin
*
* @param nsdServiceInfo The service info to check
*
* @return True if service is supported by the print service plugin
*/
boolean matchesCriteria(NsdServiceInfo nsdServiceInfo);
}
/** Printer identifiers of the mPrinters found. */
@GuardedBy("mLock")
private final @NonNull HashSet<String> mPrinters;
/** Service types discovered by this plugin */
private final @NonNull HashSet<String> mServiceTypes;
/** Context of the user of this plugin */
private final @NonNull Context mContext;
/** mDNS services filter */
private final @NonNull PrinterFilter mPrinterFilter;
/**
* Call back to report the number of mPrinters found.
*
* We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
* safe to not synchronize access to this field.
*/
private @Nullable PrintServicePlugin.PrinterDiscoveryCallback mCallback;
/** Queue used to resolve nsd infos */
private final @NonNull NsdResolveQueue mResolveQueue;
/**
* Create new stub that assumes that a print service can be used to print on all mPrinters
* matching some mDNS names.
*
* @param context The context the plugin runs in
* @param serviceTypes The mDNS service types to listen to.
* @param printerFilter The filter for mDNS services
*/
public MDNSFilteredDiscovery(@NonNull Context context,
@NonNull Set<String> serviceTypes,
@NonNull PrinterFilter printerFilter) {
mContext = Preconditions.checkNotNull(context, "context");
mServiceTypes = new HashSet<>(Preconditions
.checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(serviceTypes,
"serviceTypes"), "serviceTypes"));
mPrinterFilter = Preconditions.checkNotNull(printerFilter, "printerFilter");
mResolveQueue = NsdResolveQueue.getInstance();
mPrinters = new HashSet<>();
}
/**
* @return The NDS manager
*/
private NsdManager getNDSManager() {
return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
}
/**
* Start the discovery.
*
* @param callback Callbacks used by this plugin.
*/
public void start(@NonNull PrintServicePlugin.PrinterDiscoveryCallback callback) {
mCallback = callback;
mCallback.onChanged(mPrinters.size());
for (String serviceType : mServiceTypes) {
DiscoveryListenerMultiplexer.addListener(getNDSManager(), serviceType, this);
}
}
/**
* Stop the discovery. This can only return once the plugin is completely finished and cleaned up.
*/
public void stop() {
mCallback.onChanged(0);
mCallback = null;
for (int i = 0; i < mServiceTypes.size(); ++i) {
DiscoveryListenerMultiplexer.removeListener(getNDSManager(), this);
}
}
/**
*
* @return The number of discovered printers
*/
public int getCount() {
return mPrinters.size();
}
@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 (mPrinterFilter.matchesCriteria(serviceInfo)) {
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 (mPrinterFilter.matchesCriteria(serviceInfo)) {
if (mCallback != null) {
boolean removed = mPrinters
.remove(serviceInfo.getHost().getHostAddress());
if (removed) {
mCallback.onChanged(mPrinters.size());
}
}
}
}
});
}
}

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
package com.android.printservice.recommendation.plugin.mdnsFilter;
package com.android.printservice.recommendation.util;
import android.annotation.NonNull;
import android.net.nsd.NsdServiceInfo;
@@ -27,12 +27,15 @@ import java.util.Set;
/**
* Utils for dealing with mDNS attributes
*/
class MDNSUtils {
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";
private MDNSUtils() {
}
/**
* Check if the service has any of a set of vendor names.
*
@@ -95,4 +98,35 @@ class MDNSUtils {
private static boolean containsString(@NonNull String container, @NonNull String contained) {
return container.equalsIgnoreCase(contained) || container.contains(contained + " ");
}
/**
* Return String from mDNS attribute byte array
*
* @param value the byte array with string data
*
* @return constructed string
*/
public static String getString(byte[] value) {
if (value != null) return new String(value, StandardCharsets.UTF_8);
return null;
}
/**
* Check if service has a type of supported types set
*
* @param serviceInfo The service
* @param serviceTypes The supported service types set
*
* @return true if service has a type of supported types set
*/
public static boolean isSupportedServiceType(@NonNull NsdServiceInfo serviceInfo,
@NonNull Set<String> serviceTypes) {
String curType = serviceInfo.getServiceType().toLowerCase();
for (String type : serviceTypes) {
if (curType.contains(type.toLowerCase())) {
return true;
}
}
return false;
}
}