1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  * Copyright (C) 2016 Mopria Alliance, Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bips.discovery;
19 
20 import android.net.Uri;
21 import android.print.PrinterId;
22 import android.printservice.PrintService;
23 import android.text.TextUtils;
24 import android.util.JsonReader;
25 import android.util.JsonWriter;
26 
27 import java.io.IOException;
28 import java.io.StringWriter;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Objects;
32 
33 /** Represents a network-visible printer */
34 public class DiscoveredPrinter {
35     /** UUID (RFC4122) uniquely identifying the print service, or null if not reported */
36     public final Uri uuid;
37 
38     /** User-visible name for the print service */
39     public final String name;
40 
41     /** Location of the device or null of not reported */
42     public final String location;
43 
44     /** Resource path at which the print service can be reached */
45     public final Uri path;
46 
47     /** All paths at which this this printer can be reached. Includes "path". */
48     public final List<Uri> paths;
49 
50     /** Lazily-created printer id. */
51     private PrinterId mPrinterId;
52 
53     /**
54      * Construct minimal information about a network printer
55      *
56      * @param uuid     Unique identification of the network printer, if known
57      * @param name     Self-identified printer or service name
58      * @param paths    One or more network paths at which the printer is currently available
59      * @param location Self-advertised location of the printer, if known
60      */
DiscoveredPrinter(Uri uuid, String name, List<Uri> paths, String location)61     public DiscoveredPrinter(Uri uuid, String name, List<Uri> paths, String location) {
62         this.uuid = uuid;
63         this.name = name;
64         this.path = paths.get(0);
65         this.paths = Collections.unmodifiableList(paths);
66         this.location = location;
67     }
68 
69     /**
70      * Construct minimal information about a network printer
71      *
72      * @param uuid     Unique identification of the network printer, if known
73      * @param name     Self-identified printer or service name
74      * @param path     Network path at which the printer is currently available
75      * @param location Self-advertised location of the printer, if known
76      */
DiscoveredPrinter(Uri uuid, String name, Uri path, String location)77     public DiscoveredPrinter(Uri uuid, String name, Uri path, String location) {
78         this(uuid, name, Collections.singletonList(path), location);
79     }
80 
81     /** Construct an object based on field values of an JSON object found next in the JsonReader */
DiscoveredPrinter(JsonReader reader)82     public DiscoveredPrinter(JsonReader reader) throws IOException {
83         String printerName = null, location = null;
84         Uri uuid = null, path = null;
85 
86         reader.beginObject();
87         while (reader.hasNext()) {
88             String itemName = reader.nextName();
89             switch (itemName) {
90                 case "uuid":
91                     uuid = Uri.parse(reader.nextString());
92                     break;
93                 case "name":
94                     printerName = reader.nextString();
95                     break;
96                 case "path":
97                     path = Uri.parse(reader.nextString());
98                     break;
99                 case "location":
100                     location = reader.nextString();
101                     break;
102             }
103         }
104         reader.endObject();
105 
106         if (printerName == null || path == null) {
107             throw new IOException("Missing name or path");
108         }
109         this.uuid = uuid;
110         this.name = printerName;
111         this.path = path;
112         this.paths = Collections.singletonList(path);
113         this.location = location;
114     }
115 
116     /**
117      * Return the best URI describing this printer: from the UUID (if present) or
118      * from the path (if UUID is missing)
119      */
getUri()120     public Uri getUri() {
121         return uuid != null ? uuid : path;
122     }
123 
124     /**
125      * Return true if this printer has a secure (encrypted) path.
126      */
isSecure()127     public boolean isSecure() {
128         for (Uri path : paths) {
129             if (path.getScheme().equals("ipps")) {
130                 return true;
131             }
132         }
133         return false;
134     }
135 
136     /**
137      * Return a host string for the user to see (an IP address or hostname without port number)
138      */
getHost()139     public String getHost() {
140         return path.getHost().replaceAll(":[0-9]+", "");
141     }
142 
143     /** Return a generated printer ID based on uuid or (if uuid is missing) its path */
getId(PrintService printService)144     public PrinterId getId(PrintService printService) {
145         if (mPrinterId == null) {
146             mPrinterId = printService.generatePrinterId(getUri().toString());
147         }
148         return mPrinterId;
149     }
150 
151     /** Writes all serializable fields into JSON form */
write(JsonWriter writer)152     void write(JsonWriter writer) throws IOException {
153         writer.beginObject();
154         writer.name("name").value(name);
155         writer.name("path").value(path.toString());
156         if (uuid != null) {
157             writer.name("uuid").value(uuid.toString());
158         }
159         if (!TextUtils.isEmpty(location)) {
160             writer.name("location").value(location);
161         }
162         writer.endObject();
163     }
164 
165     @Override
equals(Object obj)166     public boolean equals(Object obj) {
167         if (!(obj instanceof DiscoveredPrinter)) {
168             return false;
169         }
170         DiscoveredPrinter other = (DiscoveredPrinter) obj;
171         return Objects.equals(uuid, other.uuid)
172                 && Objects.equals(name, other.name)
173                 && Objects.equals(paths, other.paths)
174                 && Objects.equals(location, other.location);
175     }
176 
177     @Override
hashCode()178     public int hashCode() {
179         int result = 17;
180         result = 31 * result + name.hashCode();
181         result = 31 * result + (uuid != null ? uuid.hashCode() : 0);
182         result = 31 * result + paths.hashCode();
183         result = 31 * result + (location != null ? location.hashCode() : 0);
184         return result;
185     }
186 
187     @Override
toString()188     public String toString() {
189         StringWriter sw = new StringWriter();
190         try {
191             write(new JsonWriter(sw));
192         } catch (IOException ignored) {
193         }
194         return "DiscoveredPrinter" + sw.toString();
195     }
196 }
197