1b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann/* 2b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * Copyright (C) 2017 The Android Open Source Project 3b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * 4b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * Licensed under the Apache License, Version 2.0 (the "License"); 5b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * you may not use this file except in compliance with the License. 6b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * You may obtain a copy of the License at 7b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * 8b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * http://www.apache.org/licenses/LICENSE-2.0 9b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * 10b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * Unless required by applicable law or agreed to in writing, software 11b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * distributed under the License is distributed on an "AS IS" BASIS, 12b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * See the License for the specific language governing permissions and 14b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * limitations under the License. 15b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann */ 16b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 17b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannpackage com.android.printservice.recommendation.plugin.google; 18b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 19b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport static com.android.printservice.recommendation.util.MDNSUtils.ATTRIBUTE_TY; 20b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 21b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport android.annotation.NonNull; 22b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport android.annotation.StringRes; 23b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport android.content.Context; 24b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport android.util.ArrayMap; 25b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport android.util.Log; 26b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 27b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport com.android.printservice.recommendation.PrintServicePlugin; 28b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport com.android.printservice.recommendation.R; 29b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport com.android.printservice.recommendation.util.MDNSFilteredDiscovery; 30b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 31b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport java.net.Inet4Address; 32b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport java.net.InetAddress; 33b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport java.nio.charset.StandardCharsets; 34b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport java.util.HashSet; 35b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport java.util.Map; 36b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannimport java.util.Set; 37b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 38b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann/** 39b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * Plugin detecting <a href="https://developers.google.com/cloud-print/docs/privet">Google Cloud 40b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * Print</a> printers. 41b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann */ 42b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmannpublic class CloudPrintPlugin implements PrintServicePlugin { 43b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String LOG_TAG = CloudPrintPlugin.class.getSimpleName(); 44b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final boolean DEBUG = false; 45b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 46b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String ATTRIBUTE_TXTVERS = "txtvers"; 47b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String ATTRIBUTE_URL = "url"; 48b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String ATTRIBUTE_TYPE = "type"; 49b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String ATTRIBUTE_ID = "id"; 50b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String ATTRIBUTE_CS = "cs"; 51b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 52b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String TYPE = "printer"; 53b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 54b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final String PRIVET_SERVICE = "_privet._tcp"; 55b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 56b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann /** The required mDNS service types */ 57b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final Set<String> PRINTER_SERVICE_TYPE = new HashSet<String>() {{ 58b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann // Not checking _printer_._sub 59b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann add(PRIVET_SERVICE); 60b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann }}; 61b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 62b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann /** All possible connection states */ 63b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final Set<String> POSSIBLE_CONNECTION_STATES = new HashSet<String>() {{ 64b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann add("online"); 65b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann add("offline"); 66b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann add("connecting"); 67b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann add("not-configured"); 68b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann }}; 69b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 70b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private static final byte SUPPORTED_TXTVERS = '1'; 71b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 72b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann /** The mDNS filtered discovery */ 73b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann private final MDNSFilteredDiscovery mMDNSFilteredDiscovery; 74b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 75b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann /** 76b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * Create a plugin detecting Google Cloud Print printers. 77b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * 78b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann * @param context The context the plugin runs in 79b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann */ 80b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann public CloudPrintPlugin(@NonNull Context context) { 81b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, PRINTER_SERVICE_TYPE, 82b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann nsdServiceInfo -> { 83b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann // The attributes are case insensitive. For faster searching create a clone of 84b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann // the map with the attribute-keys all in lower case. 85b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann ArrayMap<String, byte[]> caseInsensitiveAttributes = 86b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann new ArrayMap<>(nsdServiceInfo.getAttributes().size()); 87b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann for (Map.Entry<String, byte[]> entry : nsdServiceInfo.getAttributes() 88b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann .entrySet()) { 89b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann caseInsensitiveAttributes.put(entry.getKey().toLowerCase(), 90b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann entry.getValue()); 91b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 92b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 93b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (DEBUG) { 94b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann Log.i(LOG_TAG, nsdServiceInfo.getServiceName() + ":"); 95b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann Log.i(LOG_TAG, "type: " + nsdServiceInfo.getServiceType()); 96b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann Log.i(LOG_TAG, "host: " + nsdServiceInfo.getHost()); 97b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann for (Map.Entry<String, byte[]> entry : caseInsensitiveAttributes.entrySet()) { 98b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (entry.getValue() == null) { 99b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann Log.i(LOG_TAG, entry.getKey() + "= null"); 100b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } else { 101b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann Log.i(LOG_TAG, entry.getKey() + "=" + new String(entry.getValue(), 102b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann StandardCharsets.UTF_8)); 103b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 104b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 105b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 106b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 107b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann byte[] txtvers = caseInsensitiveAttributes.get(ATTRIBUTE_TXTVERS); 108b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (txtvers == null || txtvers.length != 1 || txtvers[0] != SUPPORTED_TXTVERS) { 109b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann // The spec requires this to be the first attribute, but at this time we 110b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann // lost the order of the attributes 111b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 112b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 113b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 114b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (caseInsensitiveAttributes.get(ATTRIBUTE_TY) == null) { 115b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 116b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 117b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 118b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann byte[] url = caseInsensitiveAttributes.get(ATTRIBUTE_URL); 119b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (url == null || url.length == 0) { 120b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 121b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 122b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 123b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann byte[] type = caseInsensitiveAttributes.get(ATTRIBUTE_TYPE); 124b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (type == null || !TYPE.equals( 125b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann new String(type, StandardCharsets.UTF_8).toLowerCase())) { 126b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 127b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 128b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 129b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (caseInsensitiveAttributes.get(ATTRIBUTE_ID) == null) { 130b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 131b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 132b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 133b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann byte[] cs = caseInsensitiveAttributes.get(ATTRIBUTE_CS); 134b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (cs == null || !POSSIBLE_CONNECTION_STATES.contains( 135b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann new String(cs, StandardCharsets.UTF_8).toLowerCase())) { 136b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 137b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 138b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 139b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann InetAddress address = nsdServiceInfo.getHost(); 140b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann if (!(address instanceof Inet4Address)) { 141b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann // Not checking for link local address 142b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return false; 143b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 144b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 145b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return true; 146b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann }); 147b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 148b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 149b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann @Override 150b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann @NonNull public CharSequence getPackageName() { 151b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return "com.google.android.apps.cloudprint"; 152b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 153b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 154b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann @Override 155b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception { 156b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann mMDNSFilteredDiscovery.start(callback); 157b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 158b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 159b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann @Override 160b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann @StringRes public int getName() { 161b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann return R.string.plugin_vendor_google_cloud_print; 162b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 163b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann 164b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann @Override 165b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann public void stop() throws Exception { 166b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann mMDNSFilteredDiscovery.stop(); 167b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann } 168b2565935d1bda310bcde102a89512368f19784bbPhilip P. Moltmann} 169