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