1 /*
2  * Copyright (C) 2009 Google Inc.  All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.polo.pairing.message;
18 
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.Set;
22 
23 /**
24  * Object implementing the internal representation of the protocol message
25  * 'OPTIONS'.
26  */
27 public class OptionsMessage extends PoloMessage {
28 
29   /**
30    * Representation of a specific role type. The numeric values
31    * should be kept synchronized with the constants defined by
32    * Options.Encoding.EncodingType in polo.proto.
33    */
34   public static enum ProtocolRole {
35     UNKNOWN(0),
36     INPUT_DEVICE(1),
37     DISPLAY_DEVICE(2);
38 
39     private final int mIntVal;
40 
ProtocolRole(int intVal)41     private ProtocolRole(int intVal) {
42       mIntVal = intVal;
43     }
44 
fromIntVal(int intVal)45     public static ProtocolRole fromIntVal(int intVal) {
46       for (ProtocolRole role : ProtocolRole.values()) {
47         if (role.getAsInt() == intVal) {
48           return role;
49         }
50       }
51       return ProtocolRole.UNKNOWN;
52     }
53 
getAsInt()54     public int getAsInt() {
55       return mIntVal;
56     }
57   }
58 
59   /**
60    * The preferred protocol role of the sender.
61    */
62   private ProtocolRole mProtocolRolePreference;
63 
64   /**
65    * Sender's supported input encodings.
66    */
67   private Set<EncodingOption> mInputEncodings;
68 
69   /**
70    * Sender's supported output encodings.
71    */
72   private Set<EncodingOption> mOutputEncodings;
73 
OptionsMessage()74   public OptionsMessage() {
75     super(PoloMessage.PoloMessageType.OPTIONS);
76 
77     mProtocolRolePreference = ProtocolRole.UNKNOWN;
78     mInputEncodings = new HashSet<EncodingOption>();
79     mOutputEncodings = new HashSet<EncodingOption>();
80   }
81 
setProtocolRolePreference(ProtocolRole pref)82   public void setProtocolRolePreference(ProtocolRole pref) {
83     mProtocolRolePreference = pref;
84   }
85 
getProtocolRolePreference()86   public ProtocolRole getProtocolRolePreference() {
87     return mProtocolRolePreference;
88   }
89 
addInputEncoding(EncodingOption encoding)90   public void addInputEncoding(EncodingOption encoding) {
91     mInputEncodings.add(encoding);
92   }
93 
addOutputEncoding(EncodingOption encoding)94   public void addOutputEncoding(EncodingOption encoding) {
95     mOutputEncodings.add(encoding);
96   }
97 
supportsInputEncoding(EncodingOption encoding)98   public boolean supportsInputEncoding(EncodingOption encoding) {
99     return mInputEncodings.contains(encoding);
100   }
101 
supportsOutputEncoding(EncodingOption encoding)102   public boolean supportsOutputEncoding(EncodingOption encoding) {
103     return mOutputEncodings.contains(encoding);
104   }
105 
getInputEncodingSet()106   public Set<EncodingOption> getInputEncodingSet() {
107     return new HashSet<EncodingOption>(mInputEncodings);
108   }
109 
getOutputEncodingSet()110   public Set<EncodingOption> getOutputEncodingSet() {
111     return new HashSet<EncodingOption>(mOutputEncodings);
112   }
113 
114   /**
115    * Generates a ConfigurationMessage, or {@code null}, by intersecting the
116    * local message with another message.
117    */
getBestConfiguration( OptionsMessage otherOptions)118   public ConfigurationMessage getBestConfiguration(
119       OptionsMessage otherOptions) {
120     Set<EncodingOption> peerOutputs = otherOptions.getOutputEncodingSet();
121     peerOutputs.retainAll(mInputEncodings);
122 
123     Set<EncodingOption> peerInputs = otherOptions.getInputEncodingSet();
124     peerInputs.retainAll(mOutputEncodings);
125 
126     if (peerOutputs.isEmpty() && peerInputs.isEmpty()) {
127       // No configuration possible: no common encodings.
128       return null;
129     }
130 
131     EncodingOption encoding;
132     ProtocolRole role;
133 
134     EncodingOption bestInput = null;
135     if (!peerInputs.isEmpty()) {
136       bestInput = peerInputs.iterator().next();
137     }
138     EncodingOption bestOutput = null;
139     if (!peerOutputs.isEmpty()) {
140       bestOutput = peerOutputs.iterator().next();
141     }
142 
143     if (mProtocolRolePreference == ProtocolRole.DISPLAY_DEVICE) {
144       // We prefer to be the display device
145       if (bestInput != null) {
146         encoding = bestInput;
147         role = ProtocolRole.DISPLAY_DEVICE;
148       } else {
149         encoding = bestOutput;
150         role = ProtocolRole.INPUT_DEVICE;
151       }
152     } else {
153       // We prefer to be the input device, or have no preference
154       if (!peerOutputs.isEmpty()) {
155         encoding = bestOutput;
156         role = ProtocolRole.INPUT_DEVICE;
157       } else {
158         encoding = bestInput;
159         role = ProtocolRole.DISPLAY_DEVICE;
160       }
161     }
162 
163     return new ConfigurationMessage(encoding, role);
164   }
165 
166   @Override
toString()167   public String toString() {
168     StringBuilder ret = new StringBuilder();
169     ret.append("[" + getType() + " ");
170 
171     ret.append("inputs=");
172     Iterator<EncodingOption> inputIterator = mInputEncodings.iterator();
173     while (inputIterator.hasNext()) {
174       ret.append(inputIterator.next());
175       if (inputIterator.hasNext()) {
176         ret.append(",");
177       }
178     }
179 
180     ret.append(" outputs=");
181     Iterator<EncodingOption> outputIterator = mOutputEncodings.iterator();
182     while (outputIterator.hasNext()) {
183       ret.append(outputIterator.next());
184       if (outputIterator.hasNext()) {
185         ret.append(",");
186       }
187     }
188 
189     ret.append(" pref=" + mProtocolRolePreference);
190     ret.append("]");
191     return ret.toString();
192   }
193 
194   @Override
equals(Object obj)195   public boolean equals(Object obj) {
196     if (this == obj) {
197       return true;
198     }
199 
200     if (!(obj instanceof OptionsMessage)) {
201       return false;
202     }
203 
204     OptionsMessage other = (OptionsMessage) obj;
205 
206     if (mProtocolRolePreference == null) {
207       if (other.mProtocolRolePreference != null) {
208         return false;
209       }
210     } else if (!mProtocolRolePreference.equals(other.mProtocolRolePreference)) {
211         return false;
212     }
213 
214     return mInputEncodings.equals(other.mInputEncodings) &&
215         mOutputEncodings.equals(other.mOutputEncodings);
216   }
217 
218 }
219