1 /*
2  * Copyright (C) 2017 The Android Open Source Project
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 android.net.wifi.rtt;
18 
19 import static android.net.wifi.ScanResult.InformationElement.EID_EXTENSION_PRESENT;
20 import static android.net.wifi.ScanResult.InformationElement.EID_EXT_EHT_CAPABILITIES;
21 import static android.net.wifi.ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES;
22 import static android.net.wifi.ScanResult.InformationElement.EID_HT_CAPABILITIES;
23 import static android.net.wifi.ScanResult.InformationElement.EID_VHT_CAPABILITIES;
24 
25 import android.annotation.FlaggedApi;
26 import android.annotation.IntDef;
27 import android.annotation.IntRange;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.SystemApi;
31 import android.net.MacAddress;
32 import android.net.wifi.ScanResult;
33 import android.net.wifi.WifiAnnotations;
34 import android.net.wifi.aware.PeerHandle;
35 import android.os.Parcel;
36 import android.os.Parcelable;
37 import android.util.Log;
38 
39 import com.android.wifi.flags.Flags;
40 
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.util.Objects;
44 
45 /**
46  * Defines the configuration of an IEEE 802.11mc Responder. The Responder may be an Access Point
47  * (AP), a Wi-Fi Aware device, or a manually configured Responder.
48  * <p>
49  * A Responder configuration may be constructed from a {@link ScanResult} or manually (with the
50  * data obtained out-of-band from a peer).
51  */
52 public final class ResponderConfig implements Parcelable {
53     private static final String TAG = "ResponderConfig";
54     private static final int AWARE_BAND_2_DISCOVERY_CHANNEL = 2437;
55 
56     /** @hide */
57     @IntDef({RESPONDER_AP, RESPONDER_STA, RESPONDER_P2P_GO, RESPONDER_P2P_CLIENT, RESPONDER_AWARE})
58     @Retention(RetentionPolicy.SOURCE)
59     public @interface ResponderType {
60     }
61 
62     /**
63      * Responder is an access point(AP).
64      */
65 
66     public static final int RESPONDER_AP = 0;
67 
68     /**
69      * Responder is a client device(STA).
70      */
71     public static final int RESPONDER_STA = 1;
72 
73     /**
74      * Responder is a Wi-Fi Direct Group Owner (GO).
75      * @hide
76      */
77     @SystemApi
78     public static final int RESPONDER_P2P_GO = 2;
79 
80     /**
81      * Responder is a Wi-Fi Direct Group Client.
82      * @hide
83      */
84     @SystemApi
85     public static final int RESPONDER_P2P_CLIENT = 3;
86 
87     /**
88      * Responder is a Wi-Fi Aware device.
89      * @hide
90      */
91     @SystemApi
92     public static final int RESPONDER_AWARE = 4;
93 
94     /** @hide */
95     @IntDef({
96             CHANNEL_WIDTH_20MHZ, CHANNEL_WIDTH_40MHZ, CHANNEL_WIDTH_80MHZ, CHANNEL_WIDTH_160MHZ,
97             CHANNEL_WIDTH_80MHZ_PLUS_MHZ, CHANNEL_WIDTH_320MHZ})
98     @Retention(RetentionPolicy.SOURCE)
99     public @interface ChannelWidth {
100     }
101 
102 
103     /**
104      * Channel bandwidth is 20 MHZ
105      * @hide
106      */
107     @SystemApi
108     public static final int CHANNEL_WIDTH_20MHZ = 0;
109 
110     /**
111      * Channel bandwidth is 40 MHZ
112      * @hide
113      */
114     @SystemApi
115     public static final int CHANNEL_WIDTH_40MHZ = 1;
116 
117     /**
118      * Channel bandwidth is 80 MHZ
119      * @hide
120      */
121     @SystemApi
122     public static final int CHANNEL_WIDTH_80MHZ = 2;
123 
124     /**
125      * Channel bandwidth is 160 MHZ
126      * @hide
127      */
128     @SystemApi
129     public static final int CHANNEL_WIDTH_160MHZ = 3;
130 
131     /**
132      * Channel bandwidth is 160 MHZ, but 80MHZ + 80MHZ
133      * @hide
134      */
135     @SystemApi
136     public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4;
137 
138     /**
139      * Channel bandwidth is 320 MHZ
140      * @hide
141      */
142     @SystemApi
143     public static final int CHANNEL_WIDTH_320MHZ = 5;
144 
145     /** @hide */
146     @IntDef({PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT, PREAMBLE_HE, PREAMBLE_EHT})
147     @Retention(RetentionPolicy.SOURCE)
148     public @interface PreambleType {
149     }
150 
151     /**
152      * Preamble type: Legacy.
153      * @hide
154      */
155     @SystemApi
156     public static final int PREAMBLE_LEGACY = 0;
157 
158     /**
159      * Preamble type: HT.
160      * @hide
161      */
162     @SystemApi
163     public static final int PREAMBLE_HT = 1;
164 
165     /**
166      * Preamble type: VHT.
167      * @hide
168      */
169     @SystemApi
170     public static final int PREAMBLE_VHT = 2;
171 
172     /**
173      * Preamble type: HE.
174      * @hide
175      */
176     @SystemApi
177     public static final int PREAMBLE_HE = 3;
178 
179     /**
180      * Preamble type: EHT.
181      * @hide
182      */
183     @SystemApi
184     public static final int PREAMBLE_EHT = 4;
185 
186     private static final long DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS = 250000;
187     private static final long DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS = 15000000;
188 
189     /**
190      * The MAC address of the Responder. Will be null if a Wi-Fi Aware peer identifier (the
191      * peerHandle field) ise used to identify the Responder.
192      * @hide
193      */
194     @SystemApi
195     @Nullable public final MacAddress macAddress;
196 
197     /**
198      * The peer identifier of a Wi-Fi Aware Responder. Will be null if a MAC Address (the macAddress
199      * field) is used to identify the Responder.
200      * @hide
201      */
202     @SystemApi
203     @Nullable public final PeerHandle peerHandle;
204 
205     /**
206      * The device type of the Responder.
207      * @hide
208      */
209     @SystemApi
210     public final int responderType;
211 
212     /**
213      * Indicates whether the Responder device supports IEEE 802.11mc.
214      * @hide
215      */
216     @SystemApi
217     public final boolean supports80211mc;
218 
219     /**
220      * Indicates whether the Responder device supports IEEE 802.11az non-trigger based ranging.
221      * @hide
222      */
223     @SystemApi
224     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
225     public final boolean supports80211azNtb;
226 
227     /**
228      * Responder channel bandwidth, specified using {@link ChannelWidth}.
229      * @hide
230      */
231     @SystemApi
232     public final int channelWidth;
233 
234     /**
235      * The primary 20 MHz frequency (in MHz) of the channel of the Responder.
236      * @hide
237      */
238     @SystemApi
239     public final int frequency;
240 
241     /**
242      * Not used if the {@link #channelWidth} is 20 MHz. If the Responder uses 40, 80, 160 or
243      * 320 MHz, this is the center frequency (in MHz), if the Responder uses 80 + 80 MHz,
244      * this is the center frequency of the first segment (in MHz).
245      * @hide
246      */
247     @SystemApi
248     public final int centerFreq0;
249 
250     /**
251      * Only used if the {@link #channelWidth} is 80 + 80 MHz. If the Responder uses 80 + 80 MHz,
252      * this is the center frequency of the second segment (in MHz).
253      * @hide
254      */
255     @SystemApi
256     public final int centerFreq1;
257 
258     /**
259      * The preamble used by the Responder, specified using {@link PreambleType}.
260      * @hide
261      */
262     @SystemApi
263     public final int preamble;
264 
265     private long mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS;
266     private long mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS;
267 
268     /**
269      * Constructs Responder configuration from the builder
270      * @param builder See {@link Builder}
271      * @hide
272      */
ResponderConfig(Builder builder)273     public ResponderConfig(Builder builder) {
274         if (builder.mMacAddress == null && builder.mPeerHandle == null) {
275             throw new IllegalArgumentException(
276                     "Invalid ResponderConfig - must specify a MAC address or Peer handle");
277         }
278         this.macAddress = builder.mMacAddress;
279         this.peerHandle = builder.mPeerHandle;
280         this.responderType = builder.mResponderType;
281         this.supports80211mc = builder.mSupports80211Mc;
282         this.supports80211azNtb = builder.mSupports80211azNtb;
283         this.channelWidth = builder.mChannelWidth;
284         this.frequency = builder.mFrequency;
285         this.centerFreq0 = builder.mCenterFreq0;
286         this.centerFreq1 = builder.mCenterFreq1;
287         this.preamble = builder.mPreamble;
288         this.mNtbMinMeasurementTime = builder.mNtbMinMeasurementTime;
289         this.mNtbMaxMeasurementTime = builder.mNtbMaxMeasurementTime;
290     }
291 
292     /**
293      * Constructs Responder configuration, using a MAC address to identify the Responder.
294      *
295      * @param macAddress      The MAC address of the Responder.
296      * @param responderType   The type of the responder device, specified using
297      *                        {@link ResponderType}.
298      *                        For an access point (AP) use {@code RESPONDER_AP}.
299      * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc.
300      * @param channelWidth    Responder channel bandwidth, specified using {@link ChannelWidth}.
301      * @param frequency       The primary 20 MHz frequency (in MHz) of the channel of the Responder.
302      * @param centerFreq0     Not used if the {@code channelWidth} is 20 MHz. If the Responder uses
303      *                        40, 80, 160 or 320 MHz, this is the center frequency (in MHz), if the
304      *                        Responder uses 80 + 80 MHz, this is the center frequency of the first
305      *                        segment (in MHz).
306      * @param centerFreq1     Only used if the {@code channelWidth} is 80 + 80 MHz. If the
307      *                        Responder
308      *                        uses 80 + 80 MHz, this is the center frequency of the second segment
309      *                        (in
310      *                        MHz).
311      * @param preamble        The preamble used by the Responder, specified using
312      *                        {@link PreambleType}.
313      * @hide
314      */
315     @SystemApi
ResponderConfig(@onNull MacAddress macAddress, @ResponderType int responderType, boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0, int centerFreq1, @PreambleType int preamble)316     public ResponderConfig(@NonNull MacAddress macAddress, @ResponderType int responderType,
317             boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0,
318             int centerFreq1, @PreambleType int preamble) {
319         if (macAddress == null) {
320             throw new IllegalArgumentException(
321                     "Invalid ResponderConfig - must specify a MAC address");
322         }
323         this.macAddress = macAddress;
324         this.peerHandle = null;
325         this.responderType = responderType;
326         this.supports80211mc = supports80211mc;
327         this.channelWidth = channelWidth;
328         this.frequency = frequency;
329         this.centerFreq0 = centerFreq0;
330         this.centerFreq1 = centerFreq1;
331         this.preamble = preamble;
332         this.supports80211azNtb = false;
333         this.mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS;
334         this.mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS;
335     }
336 
337     /**
338      * Constructs Responder configuration, using a Wi-Fi Aware PeerHandle to identify the Responder.
339      *
340      * @param peerHandle      The Wi-Fi Aware peer identifier of the Responder.
341      * @param responderType   The type of the responder device, specified using
342      *                        {@link ResponderType}.
343      * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc.
344      * @param channelWidth    Responder channel bandwidth, specified using {@link ChannelWidth}.
345      * @param frequency       The primary 20 MHz frequency (in MHz) of the channel of the Responder.
346      * @param centerFreq0     Not used if the {@code channelWidth} is 20 MHz. If the Responder uses
347      *                        40, 80, 160, or 320 MHz, this is the center frequency (in MHz), if the
348      *                        Responder uses 80 + 80 MHz, this is the center frequency of the first
349      *                        segment (in MHz).
350      * @param centerFreq1     Only used if the {@code channelWidth} is 80 + 80 MHz. If the
351      *                        Responder
352      *                        uses 80 + 80 MHz, this is the center frequency of the second segment
353      *                        (in
354      *                        MHz).
355      * @param preamble        The preamble used by the Responder, specified using
356      *                        {@link PreambleType}.
357      * @hide
358      */
359     @SystemApi
ResponderConfig(@onNull PeerHandle peerHandle, @ResponderType int responderType, boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0, int centerFreq1, @PreambleType int preamble)360     public ResponderConfig(@NonNull PeerHandle peerHandle, @ResponderType int responderType,
361             boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0,
362             int centerFreq1, @PreambleType int preamble) {
363         this.macAddress = null;
364         this.peerHandle = peerHandle;
365         this.responderType = responderType;
366         this.supports80211mc = supports80211mc;
367         this.channelWidth = channelWidth;
368         this.frequency = frequency;
369         this.centerFreq0 = centerFreq0;
370         this.centerFreq1 = centerFreq1;
371         this.preamble = preamble;
372         this.supports80211azNtb = false;
373         this.mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS;
374         this.mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS;
375     }
376 
377     /**
378      * Constructs Responder configuration. This is a constructor which specifies both
379      * a MAC address and a Wi-Fi PeerHandle to identify the Responder. For an RTT RangingRequest
380      * the Wi-Fi Aware peer identifier can be constructed using an Identifier set to zero.
381      *
382      * @param macAddress      The MAC address of the Responder.
383      * @param peerHandle      The Wi-Fi Aware peer identifier of the Responder.
384      * @param responderType   The type of the responder device, specified using
385      *                        {@link ResponderType}.
386      * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc.
387      * @param channelWidth    Responder channel bandwidth, specified using {@link ChannelWidth}.
388      * @param frequency       The primary 20 MHz frequency (in MHz) of the channel of the Responder.
389      * @param centerFreq0     Not used if the {@code channelWidth} is 20 MHz. If the Responder uses
390      *                        40, 80, 160 or 320 MHz, this is the center frequency (in MHz), if the
391      *                        Responder uses 80 + 80 MHz, this is the center frequency of the first
392      *                        segment (in MHz).
393      * @param centerFreq1     Only used if the {@code channelWidth} is 80 + 80 MHz. If the
394      *                        Responder
395      *                        uses 80 + 80 MHz, this is the center frequency of the second segment
396      *                        (in
397      *                        MHz).
398      * @param preamble        The preamble used by the Responder, specified using
399      *                        {@link PreambleType}.
400      *
401      * @hide
402      */
ResponderConfig(@onNull MacAddress macAddress, @NonNull PeerHandle peerHandle, @ResponderType int responderType, boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0, int centerFreq1, @PreambleType int preamble)403     public ResponderConfig(@NonNull MacAddress macAddress, @NonNull PeerHandle peerHandle,
404             @ResponderType int responderType, boolean supports80211mc,
405             @ChannelWidth int channelWidth, int frequency, int centerFreq0, int centerFreq1,
406             @PreambleType int preamble) {
407         this.macAddress = macAddress;
408         this.peerHandle = peerHandle;
409         this.responderType = responderType;
410         this.supports80211mc = supports80211mc;
411         this.channelWidth = channelWidth;
412         this.frequency = frequency;
413         this.centerFreq0 = centerFreq0;
414         this.centerFreq1 = centerFreq1;
415         this.preamble = preamble;
416         this.supports80211azNtb = false;
417         this.mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS;
418         this.mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS;
419     }
420 
421     /**
422      * Creates a Responder configuration from a {@link ScanResult} corresponding to an Access
423      * Point (AP), which can be obtained from {@link android.net.wifi.WifiManager#getScanResults()}.
424      */
425     @NonNull
fromScanResult(@onNull ScanResult scanResult)426     public static ResponderConfig fromScanResult(@NonNull ScanResult scanResult) {
427         MacAddress macAddress = MacAddress.fromString(scanResult.BSSID);
428         int responderType = RESPONDER_AP;
429         boolean supports80211mc = scanResult.is80211mcResponder();
430         boolean supports80211azNtbRanging = scanResult.is80211azNtbResponder();
431         int channelWidth = scanResult.channelWidth;
432         int frequency = scanResult.frequency;
433         int centerFreq0 = scanResult.centerFreq0;
434         int centerFreq1 = scanResult.centerFreq1;
435 
436         int preamble;
437         if (scanResult.informationElements != null && scanResult.informationElements.length != 0) {
438             boolean htCapabilitiesPresent = false;
439             boolean vhtCapabilitiesPresent = false;
440             boolean heCapabilitiesPresent = false;
441             boolean ehtCapabilitiesPresent = false;
442 
443             for (ScanResult.InformationElement ie : scanResult.informationElements) {
444                 if (ie.id == EID_HT_CAPABILITIES) {
445                     htCapabilitiesPresent = true;
446                 } else if (ie.id == EID_VHT_CAPABILITIES) {
447                     vhtCapabilitiesPresent = true;
448                 } else if (ie.id == EID_EXTENSION_PRESENT && ie.idExt == EID_EXT_HE_CAPABILITIES) {
449                     heCapabilitiesPresent = true;
450                 } else if (ie.id == EID_EXTENSION_PRESENT && ie.idExt == EID_EXT_EHT_CAPABILITIES) {
451                     ehtCapabilitiesPresent = true;
452                 }
453             }
454 
455             if (ehtCapabilitiesPresent && ScanResult.is6GHz(frequency)) {
456                 preamble = ScanResult.PREAMBLE_EHT;
457             } else if (heCapabilitiesPresent && ScanResult.is6GHz(frequency)) {
458                 preamble = ScanResult.PREAMBLE_HE;
459             } else if (vhtCapabilitiesPresent) {
460                 preamble = ScanResult.PREAMBLE_VHT;
461             } else if (htCapabilitiesPresent) {
462                 preamble = ScanResult.PREAMBLE_HT;
463             } else {
464                 preamble = ScanResult.PREAMBLE_LEGACY;
465             }
466         } else {
467             Log.e(TAG, "Scan Results do not contain IEs - using backup method to select preamble");
468             if (channelWidth == ScanResult.CHANNEL_WIDTH_320MHZ) {
469                 preamble = ScanResult.PREAMBLE_EHT;
470             } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ
471                     || channelWidth == ScanResult.CHANNEL_WIDTH_160MHZ) {
472                 preamble = ScanResult.PREAMBLE_VHT;
473             } else {
474                 preamble = ScanResult.PREAMBLE_HT;
475             }
476         }
477 
478         return new ResponderConfig.Builder()
479                 .setMacAddress(macAddress)
480                 .setResponderType(responderType)
481                 .set80211mcSupported(supports80211mc)
482                 .set80211azNtbSupported(supports80211azNtbRanging)
483                 .setChannelWidth(channelWidth)
484                 .setFrequencyMhz(frequency)
485                 .setCenterFreq0Mhz(centerFreq0)
486                 .setCenterFreq1Mhz(centerFreq1)
487                 .setPreamble(preamble)
488                 .build();
489     }
490 
491     /**
492      * Creates a Responder configuration from a MAC address corresponding to a Wi-Fi Aware
493      * Responder. The Responder parameters are set to defaults.
494      * @hide
495      */
496     @SystemApi
497     @NonNull
fromWifiAwarePeerMacAddressWithDefaults( @onNull MacAddress macAddress)498     public static ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(
499             @NonNull MacAddress macAddress) {
500         /* Note: the parameters are those of the Aware discovery channel (channel 6). A Responder
501          * is expected to be brought up and available to negotiate a maximum accuracy channel
502          * (i.e. Band 5 @ 80MHz). A Responder is brought up on the peer by starting an Aware
503          * Unsolicited Publisher with Ranging enabled.
504          */
505         return new ResponderConfig.Builder()
506                 .setMacAddress(macAddress)
507                 .setResponderType(RESPONDER_AWARE)
508                 .build();
509     }
510 
511     /**
512      * Creates a Responder configuration from a {@link PeerHandle} corresponding to a Wi-Fi Aware
513      * Responder. The Responder parameters are set to defaults.
514      * @hide
515      */
516     @SystemApi
517     @NonNull
fromWifiAwarePeerHandleWithDefaults( @onNull PeerHandle peerHandle)518     public static ResponderConfig fromWifiAwarePeerHandleWithDefaults(
519             @NonNull PeerHandle peerHandle) {
520         /* Note: the parameters are those of the Aware discovery channel (channel 6). A Responder
521          * is expected to be brought up and available to negotiate a maximum accuracy channel
522          * (i.e. Band 5 @ 80MHz). A Responder is brought up on the peer by starting an Aware
523          * Unsolicited Publisher with Ranging enabled.
524          */
525         return new ResponderConfig.Builder()
526                 .setPeerHandle(peerHandle)
527                 .setResponderType(RESPONDER_AWARE)
528                 .build();
529     }
530 
isResponderTypeSupported(@esponderType int responderType)531     private static boolean isResponderTypeSupported(@ResponderType int responderType) {
532         switch (responderType) {
533             case RESPONDER_AP:
534             case RESPONDER_STA:
535             case RESPONDER_AWARE:
536                 break;
537             case RESPONDER_P2P_GO:
538             case RESPONDER_P2P_CLIENT:
539             default:
540                 return false;
541         }
542         return true;
543     }
544 
545     /**
546      * Check whether the Responder configuration is valid.
547      *
548      * @return true if valid, false otherwise.
549      *
550      * @hide
551      */
isValid(boolean awareSupported)552     public boolean isValid(boolean awareSupported) {
553         if (!isResponderTypeSupported(responderType)) return false;
554         if (macAddress == null && peerHandle == null || macAddress != null && peerHandle != null) {
555             return false;
556         }
557         if (!awareSupported && responderType == RESPONDER_AWARE) {
558             return false;
559         }
560         return true;
561     }
562 
563     /**
564      * @return the MAC address of the responder
565      */
566     @Nullable
getMacAddress()567     public MacAddress getMacAddress() {
568         return macAddress;
569     }
570 
571     /**
572      * @return the peer handle of the responder
573      *
574      * @hide
575      */
576     @Nullable
getPeerHandle()577     public PeerHandle getPeerHandle() {
578         return peerHandle;
579     }
580 
581     /**
582      * @return true if the Responder supports the 802.11mc protocol, false otherwise.
583      */
is80211mcSupported()584     public boolean is80211mcSupported() {
585         return supports80211mc;
586     }
587 
588     /**
589      * @return true if the Responder supports the 802.11az non-trigger based ranging protocol,
590      * false otherwise.
591      */
592     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
is80211azNtbSupported()593     public boolean is80211azNtbSupported() {
594         return supports80211azNtb;
595     }
596 
597     /**
598      * AP Channel bandwidth; one of {@link ScanResult#CHANNEL_WIDTH_20MHZ},
599      * {@link ScanResult#CHANNEL_WIDTH_40MHZ},
600      * {@link ScanResult#CHANNEL_WIDTH_80MHZ}, {@link ScanResult#CHANNEL_WIDTH_160MHZ},
601      * {@link ScanResult #CHANNEL_WIDTH_80MHZ_PLUS_MHZ} or {@link ScanResult#CHANNEL_WIDTH_320MHZ}.
602      *
603      * @return the bandwidth repsentation of the Wi-Fi channel
604      */
getChannelWidth()605     public @WifiAnnotations.ChannelWidth int getChannelWidth() {
606         return translateFromLocalToScanResultChannelWidth(channelWidth);
607     }
608 
609     /**
610      * @return the frequency in MHz of the Wi-Fi channel
611      */
612     @IntRange(from = 0)
getFrequencyMhz()613     public int getFrequencyMhz() {
614         return frequency;
615     }
616 
617     /**
618      * If the Access Point (AP) bandwidth is 20 MHz, 0 MHz is returned.
619      * If the AP use 40, 80 or 160 MHz, this is the center frequency (in MHz).
620      * if the AP uses 80 + 80 MHz, this is the center frequency of the first segment (in MHz).
621      *
622      * @return the center frequency in MHz of the first channel segment
623      */
624     @IntRange(from = 0)
getCenterFreq0Mhz()625     public int getCenterFreq0Mhz() {
626         return centerFreq0;
627     }
628 
629     /**
630      * If the Access Point (AP) bandwidth is 80 + 80 MHz, this param is not used and returns 0.
631      * If the AP uses 80 + 80 MHz, this is the center frequency of the second segment in MHz.
632      *
633      * @return the center frequency in MHz of the second channel segment (if used)
634      */
635     @IntRange(from = 0)
getCenterFreq1Mhz()636     public int getCenterFreq1Mhz() {
637         return centerFreq1;
638     }
639 
640     /**
641      * Get the preamble type of the channel.
642      *
643      * @return the preamble used for this channel
644      */
getPreamble()645     public @WifiAnnotations.PreambleType int getPreamble() {
646         return translateFromLocalToScanResultPreamble(preamble);
647     }
648 
649     /**
650      * Get responder type.
651      * @see Builder#setResponderType(int)
652      * @return The type of this responder
653      */
getResponderType()654     public @ResponderType int getResponderType() {
655         return responderType;
656     }
657 
658     /**
659      * Gets the minimum time between IEEE 802.11az non-trigger based ranging measurements in
660      * microseconds for the responder.
661      *
662      * @hide
663      */
getNtbMinTimeBetweenMeasurementsMicros()664     public long getNtbMinTimeBetweenMeasurementsMicros() {
665         return mNtbMinMeasurementTime;
666     }
667 
668     /**
669      * Gets the maximum time between IEEE 802.11az non-trigger based ranging measurements in
670      * microseconds for the responder.
671      *
672      * @hide
673      */
getNtbMaxTimeBetweenMeasurementsMicros()674     public long getNtbMaxTimeBetweenMeasurementsMicros() {
675         return mNtbMaxMeasurementTime;
676     }
677 
678     /**
679      * @hide
680      */
setNtbMinTimeBetweenMeasurementsMicros(long ntbMinMeasurementTime)681     public void setNtbMinTimeBetweenMeasurementsMicros(long ntbMinMeasurementTime) {
682         this.mNtbMinMeasurementTime = ntbMinMeasurementTime;
683     }
684 
685     /**
686      * @hide
687      */
setNtbMaxTimeBetweenMeasurementsMicros(long ntbMaxMeasurementTime)688     public void setNtbMaxTimeBetweenMeasurementsMicros(long ntbMaxMeasurementTime) {
689         this.mNtbMaxMeasurementTime = ntbMaxMeasurementTime;
690     }
691 
692     /**
693      * Builder class used to construct {@link ResponderConfig} objects.
694      */
695     public static final class Builder {
696         private MacAddress mMacAddress;
697         private PeerHandle mPeerHandle;
698         private @ResponderType int mResponderType = RESPONDER_AP;
699         private boolean mSupports80211Mc = true;
700         private boolean mSupports80211azNtb = false;
701         private @ChannelWidth int mChannelWidth = CHANNEL_WIDTH_20MHZ;
702         private int mFrequency = 0;
703         private int mCenterFreq0 = 0;
704         private int mCenterFreq1 = 0;
705         private @PreambleType int mPreamble = PREAMBLE_LEGACY;
706         private long mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS;
707         private long mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS;
708 
709         /**
710          * Sets the Responder MAC Address.
711          *
712          * @param macAddress the phyical address of the responder
713          * @return the builder to facilitate chaining
714          *         {@code builder.setXXX(..).setXXX(..)}.
715          */
716         @NonNull
setMacAddress(@onNull MacAddress macAddress)717         public Builder setMacAddress(@NonNull MacAddress macAddress) {
718             this.mMacAddress = macAddress;
719             return this;
720         }
721 
722         /**
723          * Sets the Responder Peer handle.
724          *
725          * @param peerHandle Peer handle of the resposnde
726          * @return the builder to facilitate chaining
727          *         {@code builder.setXXX(..).setXXX(..)}.
728          * @hide
729          */
730         @NonNull
setPeerHandle(@onNull PeerHandle peerHandle)731         public Builder setPeerHandle(@NonNull PeerHandle peerHandle) {
732             this.mPeerHandle = peerHandle;
733             return this;
734         }
735 
736         /**
737          * Sets an indication the access point can to respond to the two-sided Wi-Fi RTT protocol,
738          * but, if false, indicates only one-sided Wi-Fi RTT is possible.
739          *
740          * @param supports80211mc the ability to support the Wi-Fi RTT protocol
741          * @return the builder to facilitate chaining
742          *         {@code builder.setXXX(..).setXXX(..)}.
743          */
744         @NonNull
set80211mcSupported(boolean supports80211mc)745         public Builder set80211mcSupported(boolean supports80211mc) {
746             this.mSupports80211Mc = supports80211mc;
747             return this;
748         }
749 
750         /**
751          * Sets an indication the access point can to respond to the IEEE 802.11az non-trigger
752          * based ranging protocol, but, if false, indicates only IEEE 802.11mc or one-sided Wi-Fi
753          * RTT is possible.
754          *
755          * @param supports80211azNtb the ability to support the IEEE 802.11az non-trigger based
756          *                           ranging protocol
757          * @return the builder to facilitate chaining
758          *         {@code builder.setXXX(..).setXXX(..)}.
759          */
760         @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
761         @NonNull
set80211azNtbSupported(boolean supports80211azNtb)762         public Builder set80211azNtbSupported(boolean supports80211azNtb) {
763             this.mSupports80211azNtb = supports80211azNtb;
764             return this;
765         }
766 
767         /**
768          * Sets the channel bandwidth in MHz.
769          *
770          * @param channelWidth the bandwidth of the channel in MHz
771          * @return the builder to facilitate chaining
772          *         {@code builder.setXXX(..).setXXX(..)}.
773          */
774         @NonNull
setChannelWidth(@ifiAnnotations.ChannelWidth int channelWidth)775         public Builder setChannelWidth(@WifiAnnotations.ChannelWidth int channelWidth) {
776             this.mChannelWidth = translateFromScanResultToLocalChannelWidth(channelWidth);
777             return this;
778         }
779 
780         /**
781          * Sets the frequency of the channel in MHz.
782          * <p>
783          * Note: The frequency is used as a hint, and the underlying WiFi subsystem may use it, or
784          * select an alternate if its own connectivity scans have determined the frequency of the
785          * access point has changed.
786          * </p>
787          *
788          * @param frequency the frequency of the channel in MHz
789          * @return the builder to facilitate chaining
790          *         {@code builder.setXXX(..).setXXX(..)}.
791          */
792         @NonNull
setFrequencyMhz(@ntRangefrom = 0) int frequency)793         public Builder setFrequencyMhz(@IntRange(from = 0) int frequency) {
794             this.mFrequency = frequency;
795             return this;
796         }
797 
798         /**
799          * Sets the center frequency in MHz of the first segment of the channel.
800          * <p>
801          * Note: The frequency is used as a hint, and the underlying WiFi subsystem may use it, or
802          * select an alternate if its own connectivity scans have determined the frequency of the
803          * access point has changed.
804          * </p>
805          *
806          * @param centerFreq0 the center frequency in MHz of first channel segment
807          * @return the builder to facilitate chaining
808          *         {@code builder.setXXX(..).setXXX(..)}.
809          */
810         @NonNull
setCenterFreq0Mhz(@ntRangefrom = 0) int centerFreq0)811         public Builder setCenterFreq0Mhz(@IntRange(from = 0) int centerFreq0) {
812             this.mCenterFreq0 = centerFreq0;
813             return this;
814         }
815 
816         /**
817          * Sets the center frequency in MHz of the second segment of the channel, if used.
818          * <p>
819          * Note: The frequency is used as a hint, and the underlying WiFi subsystem may use it, or
820          * select an alternate if its own connectivity scans have determined the frequency of the
821          * access point has changed.
822          * </p>
823          *
824          * @param centerFreq1 the center frequency in MHz of second channel segment
825          * @return the builder to facilitate chaining
826          *         {@code builder.setXXX(..).setXXX(..)}.
827          */
828         @NonNull
setCenterFreq1Mhz(@ntRangefrom = 0) int centerFreq1)829         public Builder setCenterFreq1Mhz(@IntRange(from = 0) int centerFreq1) {
830             this.mCenterFreq1 = centerFreq1;
831             return this;
832         }
833 
834         /**
835          * Sets the preamble encoding for the protocol.
836          *
837          * @param preamble the preamble encoding
838          * @return the builder to facilitate chaining
839          *         {@code builder.setXXX(..).setXXX(..)}.
840          */
841         @NonNull
setPreamble(@ifiAnnotations.PreambleType int preamble)842         public Builder setPreamble(@WifiAnnotations.PreambleType int preamble) {
843             this.mPreamble = translateFromScanResultToLocalPreamble(preamble);
844             return this;
845         }
846 
847         /**
848          * Sets the responder type, can be {@link #RESPONDER_AP} or {@link #RESPONDER_STA} or
849          * {@link #RESPONDER_AWARE}
850          *
851          * @param responderType the type of the responder, if not set defaults to
852          * {@link #RESPONDER_AP}
853          * @return the builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
854          */
855         @NonNull
setResponderType(@esponderType int responderType)856         public Builder setResponderType(@ResponderType int responderType) {
857             if (!isResponderTypeSupported(responderType)) {
858                 throw new IllegalArgumentException("invalid responder type " + responderType);
859             }
860             mResponderType = responderType;
861             return this;
862         }
863 
864         /**
865          * Sets the minimum time between IEEE 802.11az non-trigger based ranging measurements in
866          * microseconds for the responder.
867          *
868          * Note: This should be a multiple of 100 microseconds as per IEEE 802.11 az standard.
869          *
870          * @param ntbMinMeasurementTime Minimum time between non-trigger based IEEE 802.11az
871          *                              ranging measurements in units of  100 microseconds. Range of
872          *                              values (0, 419430400).
873          * @hide
874          */
setNtbMinTimeBetweenMeasurementsMicros(long ntbMinMeasurementTime)875         public Builder setNtbMinTimeBetweenMeasurementsMicros(long ntbMinMeasurementTime) {
876             if (mNtbMinMeasurementTime == 0 || mNtbMinMeasurementTime >= 419430400) {
877                 throw new IllegalArgumentException(
878                         "Should be a non-zero number less than 419430400 microseconds");
879             }
880             if (mNtbMinMeasurementTime % 100 != 0) {
881                 throw new IllegalArgumentException("Should be a multiple of 100 microseconds");
882             }
883             mNtbMinMeasurementTime = ntbMinMeasurementTime;
884             return  this;
885         }
886 
887         /**
888          * Sets the maximum time between IEEE 802.11az non-trigger based ranging measurements in
889          * microseconds for the responder.
890          *
891          * Note: This should be a multiple of 10000 microseconds (10 milliseconds) as per
892          * IEEE 802.11 az standard.
893          *
894          * @param ntbMaxMeasurementTime Maximum time between non-trigger based IEEE 802.11az
895          *                              ranging measurements in units of  10000 microseconds. Range
896          *                              of values (0, 5242880000).
897          * @hide
898          */
setNtbMaxTimeBetweenMeasurementsMicros(long ntbMaxMeasurementTime)899         public Builder setNtbMaxTimeBetweenMeasurementsMicros(long ntbMaxMeasurementTime) {
900             if (mNtbMaxMeasurementTime % 10000 != 0) {
901                 throw new IllegalArgumentException("Should be a multiple of 10000 microseconds");
902             }
903             if (mNtbMaxMeasurementTime == 0 || mNtbMaxMeasurementTime >= 5242880000L) {
904                 throw new IllegalArgumentException(
905                         "Should be a non-zero number less than 5242880000 microseconds");
906             }
907             mNtbMaxMeasurementTime = ntbMaxMeasurementTime;
908             return  this;
909         }
910 
911         /**
912          * Build {@link ResponderConfig} given the current configurations made on the builder.
913          * @return an instance of {@link ResponderConfig}
914          */
915         @NonNull
build()916         public ResponderConfig build() {
917             if ((mMacAddress == null && mPeerHandle == null)
918                     || (mMacAddress != null && mPeerHandle != null)) {
919                 throw new IllegalArgumentException(
920                         "Invalid ResponderConfig - must specify a MAC address or peer handle but "
921                                 + "not both");
922             }
923             // For Aware, use supported default values
924             if (mResponderType == RESPONDER_AWARE) {
925                 mSupports80211Mc = true;
926                 mFrequency = AWARE_BAND_2_DISCOVERY_CHANNEL;
927                 mChannelWidth = CHANNEL_WIDTH_20MHZ;
928                 mPreamble = PREAMBLE_HT;
929             }
930             return new ResponderConfig(this);
931         }
932     }
933 
934     @Override
describeContents()935     public int describeContents() {
936         return 0;
937     }
938 
939     @Override
writeToParcel(@onNull Parcel dest, int flags)940     public void writeToParcel(@NonNull Parcel dest, int flags) {
941         if (macAddress == null) {
942             dest.writeBoolean(false);
943         } else {
944             dest.writeBoolean(true);
945             macAddress.writeToParcel(dest, flags);
946         }
947         if (peerHandle == null) {
948             dest.writeBoolean(false);
949         } else {
950             dest.writeBoolean(true);
951             dest.writeInt(peerHandle.peerId);
952         }
953         dest.writeInt(responderType);
954         dest.writeBoolean(supports80211mc);
955         dest.writeBoolean(supports80211azNtb);
956         dest.writeInt(channelWidth);
957         dest.writeInt(frequency);
958         dest.writeInt(centerFreq0);
959         dest.writeInt(centerFreq1);
960         dest.writeInt(preamble);
961         dest.writeLong(mNtbMinMeasurementTime);
962         dest.writeLong(mNtbMaxMeasurementTime);
963     }
964 
965     public static final @android.annotation.NonNull Creator<ResponderConfig> CREATOR = new Creator<ResponderConfig>() {
966         @Override
967         public ResponderConfig[] newArray(int size) {
968             return new ResponderConfig[size];
969         }
970 
971         @Override
972         public ResponderConfig createFromParcel(Parcel in) {
973             boolean macAddressPresent = in.readBoolean();
974             MacAddress macAddress = null;
975             if (macAddressPresent) {
976                 macAddress = MacAddress.CREATOR.createFromParcel(in);
977             }
978             boolean peerHandlePresent = in.readBoolean();
979             PeerHandle peerHandle = null;
980             if (peerHandlePresent) {
981                 peerHandle = new PeerHandle(in.readInt());
982             }
983 
984             return new ResponderConfig.Builder()
985                     .setMacAddress(macAddress)
986                     .setPeerHandle(peerHandle)
987                     .setResponderType(in.readInt())
988                     .set80211mcSupported(in.readBoolean())
989                     .set80211azNtbSupported(in.readBoolean())
990                     .setChannelWidth(in.readInt())
991                     .setFrequencyMhz(in.readInt())
992                     .setCenterFreq0Mhz(in.readInt())
993                     .setCenterFreq1Mhz(in.readInt())
994                     .setPreamble(in.readInt())
995                     .setNtbMinTimeBetweenMeasurementsMicros(in.readLong())
996                     .setNtbMaxTimeBetweenMeasurementsMicros(in.readLong())
997                     .build();
998         }
999     };
1000 
1001     @Override
equals(@ullable Object o)1002     public boolean equals(@Nullable Object o) {
1003         if (this == o) {
1004             return true;
1005         }
1006 
1007         if (!(o instanceof ResponderConfig)) {
1008             return false;
1009         }
1010 
1011         ResponderConfig lhs = (ResponderConfig) o;
1012 
1013         return Objects.equals(macAddress, lhs.macAddress) && Objects.equals(peerHandle,
1014                 lhs.peerHandle) && responderType == lhs.responderType
1015                 && supports80211mc == lhs.supports80211mc && channelWidth == lhs.channelWidth
1016                 && frequency == lhs.frequency && centerFreq0 == lhs.centerFreq0
1017                 && centerFreq1 == lhs.centerFreq1 && preamble == lhs.preamble
1018                 && supports80211azNtb == lhs.supports80211azNtb
1019                 && mNtbMinMeasurementTime == lhs.mNtbMinMeasurementTime
1020                 && mNtbMaxMeasurementTime == lhs.mNtbMaxMeasurementTime;
1021     }
1022 
1023     @Override
hashCode()1024     public int hashCode() {
1025         return Objects.hash(macAddress, peerHandle, responderType, supports80211mc, channelWidth,
1026                 frequency, centerFreq0, centerFreq1, preamble, supports80211azNtb,
1027                 mNtbMinMeasurementTime, mNtbMaxMeasurementTime);
1028     }
1029 
1030     @Override
toString()1031     public String toString() {
1032         return new StringBuffer("ResponderConfig: macAddress=").append(macAddress)
1033                 .append(", peerHandle=").append(peerHandle == null ? "<null>" : peerHandle.peerId)
1034                 .append(", responderType=").append(responderType)
1035                 .append(", supports80211mc=").append(supports80211mc)
1036                 .append(", channelWidth=").append(channelWidth)
1037                 .append(", frequency=").append(frequency)
1038                 .append(", centerFreq0=").append(centerFreq0)
1039                 .append(", centerFreq1=").append(centerFreq1)
1040                 .append(", preamble=").append(preamble)
1041                 .append(", supports80211azNtb=").append(supports80211azNtb)
1042                 .append(", mNtbMinMeasurementTime ").append(mNtbMinMeasurementTime)
1043                 .append(", mNtbMaxMeasurementTime ").append(mNtbMaxMeasurementTime)
1044                 .toString();
1045     }
1046 
1047     /**
1048      * Translate an SDK channel width encoding to a local channel width encoding
1049      *
1050      * @param scanResultChannelWidth the {@link ScanResult} defined channel width encoding
1051      * @return the translated channel width encoding
1052      *
1053      * @hide
1054      */
translateFromScanResultToLocalChannelWidth( @ifiAnnotations.ChannelWidth int scanResultChannelWidth)1055     static int translateFromScanResultToLocalChannelWidth(
1056             @WifiAnnotations.ChannelWidth int scanResultChannelWidth) {
1057         switch (scanResultChannelWidth) {
1058             case ScanResult.CHANNEL_WIDTH_20MHZ:
1059                 return CHANNEL_WIDTH_20MHZ;
1060             case ScanResult.CHANNEL_WIDTH_40MHZ:
1061                 return CHANNEL_WIDTH_40MHZ;
1062             case ScanResult.CHANNEL_WIDTH_80MHZ:
1063                 return CHANNEL_WIDTH_80MHZ;
1064             case ScanResult.CHANNEL_WIDTH_160MHZ:
1065                 return CHANNEL_WIDTH_160MHZ;
1066             case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
1067                 return CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
1068             case ScanResult.CHANNEL_WIDTH_320MHZ:
1069                 return CHANNEL_WIDTH_320MHZ;
1070             default:
1071                 throw new IllegalArgumentException(
1072                         "translateFromScanResultChannelWidth: bad " + scanResultChannelWidth);
1073         }
1074     }
1075 
1076     /**
1077      * Translate the local channel width encoding to the SDK channel width encoding.
1078      *
1079      * @param localChannelWidth the locally defined channel width encoding
1080      * @return the translated channel width encoding
1081      *
1082      * @hide
1083      */
translateFromLocalToScanResultChannelWidth(@hannelWidth int localChannelWidth)1084     static int translateFromLocalToScanResultChannelWidth(@ChannelWidth int localChannelWidth) {
1085         switch (localChannelWidth) {
1086             case CHANNEL_WIDTH_20MHZ:
1087                 return ScanResult.CHANNEL_WIDTH_20MHZ;
1088             case CHANNEL_WIDTH_40MHZ:
1089                 return ScanResult.CHANNEL_WIDTH_40MHZ;
1090             case CHANNEL_WIDTH_80MHZ:
1091                 return ScanResult.CHANNEL_WIDTH_80MHZ;
1092             case CHANNEL_WIDTH_160MHZ:
1093                 return ScanResult.CHANNEL_WIDTH_160MHZ;
1094             case CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
1095                 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
1096             case CHANNEL_WIDTH_320MHZ:
1097                 return ScanResult.CHANNEL_WIDTH_320MHZ;
1098             default:
1099                 throw new IllegalArgumentException(
1100                         "translateFromLocalChannelWidth: bad " + localChannelWidth);
1101         }
1102     }
1103 
1104     /**
1105      * Translate the {@link ScanResult} preamble encoding to the local preamble encoding.
1106      *
1107      * @param scanResultPreamble the channel width supplied
1108      * @return the local encoding of the Preamble
1109      *
1110      * @hide
1111      */
translateFromScanResultToLocalPreamble( @ifiAnnotations.PreambleType int scanResultPreamble)1112     static int translateFromScanResultToLocalPreamble(
1113             @WifiAnnotations.PreambleType int scanResultPreamble) {
1114         switch (scanResultPreamble) {
1115             case ScanResult.PREAMBLE_LEGACY:
1116                 return PREAMBLE_LEGACY;
1117             case ScanResult.PREAMBLE_HT:
1118                 return PREAMBLE_HT;
1119             case ScanResult.PREAMBLE_VHT:
1120                 return PREAMBLE_VHT;
1121             case ScanResult.PREAMBLE_HE:
1122                 return PREAMBLE_HE;
1123             case ScanResult.PREAMBLE_EHT:
1124                 return PREAMBLE_EHT;
1125             default:
1126                 throw new IllegalArgumentException(
1127                         "translateFromScanResultPreamble: bad " + scanResultPreamble);
1128         }
1129     }
1130 
1131     /**
1132      * Translate the local preamble encoding to the {@link ScanResult} preamble encoding.
1133      *
1134      * @param localPreamble the local preamble encoding
1135      * @return the {@link ScanResult} encoding of the Preamble
1136      *
1137      * @hide
1138      */
translateFromLocalToScanResultPreamble(@reambleType int localPreamble)1139     static int translateFromLocalToScanResultPreamble(@PreambleType int localPreamble) {
1140         switch (localPreamble) {
1141             case PREAMBLE_LEGACY:
1142                 return ScanResult.PREAMBLE_LEGACY;
1143             case PREAMBLE_HT:
1144                 return ScanResult.PREAMBLE_HT;
1145             case PREAMBLE_VHT:
1146                 return ScanResult.PREAMBLE_VHT;
1147             case PREAMBLE_HE:
1148                 return ScanResult.PREAMBLE_HE;
1149             case PREAMBLE_EHT:
1150                 return ScanResult.PREAMBLE_EHT;
1151             default:
1152                 throw new IllegalArgumentException(
1153                         "translateFromLocalPreamble: bad " + localPreamble);
1154         }
1155     }
1156 }
1157