1 /*
2  * Copyright (C) 2019 The Linux Foundation
3  * Copyright (C) 2023 The Android Open Source Project
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 android.bluetooth;
19 
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.Log;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.nio.ByteBuffer;
31 import java.nio.ByteOrder;
32 import java.util.Objects;
33 
34 /**
35  * This class provides the System APIs to access the data of BQR event reported from firmware side.
36  * Currently it supports five event types: Quality monitor event, Approaching LSTO event, A2DP
37  * choppy event, SCO choppy event and Connect fail event. To know which kind of event is wrapped in
38  * this {@link BluetoothQualityReport} object, you need to call {@link #getQualityReportId}.
39  *
40  * <ul>
41  *   <li>For Quality monitor event, you can call {@link #getBqrCommon} to get a {@link
42  *       BluetoothQualityReport.BqrCommon} object.
43  *   <li>For Approaching LSTO event, you can call {@link #getBqrCommon} to get a {@link
44  *       BluetoothQualityReport.BqrCommon} object, and call {@link #getBqrEvent} to get a {@link
45  *       BluetoothQualityReport.BqrVsLsto} object.
46  *   <li>For A2DP choppy event, you can call {@link #getBqrCommon} to get a {@link
47  *       BluetoothQualityReport.BqrCommon} object, and call {@link #getBqrEvent} to get a {@link
48  *       BluetoothQualityReport.BqrVsA2dpChoppy} object.
49  *   <li>For SCO choppy event, you can call {@link #getBqrCommon} to get a {@link
50  *       BluetoothQualityReport.BqrCommon} object, and call {@link #getBqrEvent} to get a {@link
51  *       BluetoothQualityReport.BqrVsScoChoppy} object.
52  *   <li>For Connect fail event, you can call {@link #getBqrCommon} to get a {@link
53  *       BluetoothQualityReport.BqrCommon} object, and call {@link #getBqrEvent} to get a {@link
54  *       BluetoothQualityReport.BqrConnectFail} object.
55  * </ul>
56  *
57  * @hide
58  */
59 @SystemApi
60 public final class BluetoothQualityReport implements Parcelable {
61     private static final String TAG = BluetoothQualityReport.class.getSimpleName();
62 
63     /**
64      * Quality report ID: Monitor.
65      *
66      * @hide
67      */
68     @SystemApi public static final int QUALITY_REPORT_ID_MONITOR = 0x01;
69 
70     /**
71      * Quality report ID: Approaching LSTO.
72      *
73      * @hide
74      */
75     @SystemApi public static final int QUALITY_REPORT_ID_APPROACH_LSTO = 0x02;
76 
77     /**
78      * Quality report ID: A2DP choppy.
79      *
80      * @hide
81      */
82     @SystemApi public static final int QUALITY_REPORT_ID_A2DP_CHOPPY = 0x03;
83 
84     /**
85      * Quality report ID: SCO choppy.
86      *
87      * @hide
88      */
89     @SystemApi public static final int QUALITY_REPORT_ID_SCO_CHOPPY = 0x04;
90 
91     /**
92      * Quality report ID: Connect Fail.
93      *
94      * @hide
95      */
96     @SystemApi public static final int QUALITY_REPORT_ID_CONN_FAIL = 0x08;
97 
98     /** @hide */
99     @Retention(RetentionPolicy.SOURCE)
100     @IntDef(
101             prefix = {"QUALITY_REPORT_ID"},
102             value = {
103                 QUALITY_REPORT_ID_MONITOR,
104                 QUALITY_REPORT_ID_APPROACH_LSTO,
105                 QUALITY_REPORT_ID_A2DP_CHOPPY,
106                 QUALITY_REPORT_ID_SCO_CHOPPY,
107                 QUALITY_REPORT_ID_CONN_FAIL,
108             })
109     public @interface QualityReportId {}
110 
111     private String mAddr;
112     private int mLmpVer;
113     private int mLmpSubVer;
114     private int mManufacturerId;
115     private String mName;
116     private BluetoothClass mBluetoothClass;
117 
118     private BqrCommon mBqrCommon;
119     private BqrVsLsto mBqrVsLsto;
120     private BqrVsA2dpChoppy mBqrVsA2dpChoppy;
121     private BqrVsScoChoppy mBqrVsScoChoppy;
122     private BqrConnectFail mBqrConnectFail;
123 
124     enum PacketType {
125         INVALID,
126         TYPE_ID,
127         TYPE_NULL,
128         TYPE_POLL,
129         TYPE_FHS,
130         TYPE_HV1,
131         TYPE_HV2,
132         TYPE_HV3,
133         TYPE_DV,
134         TYPE_EV3,
135         TYPE_EV4,
136         TYPE_EV5,
137         TYPE_2EV3,
138         TYPE_2EV5,
139         TYPE_3EV3,
140         TYPE_3EV5,
141         TYPE_DM1,
142         TYPE_DH1,
143         TYPE_DM3,
144         TYPE_DH3,
145         TYPE_DM5,
146         TYPE_DH5,
147         TYPE_AUX1,
148         TYPE_2DH1,
149         TYPE_2DH3,
150         TYPE_2DH5,
151         TYPE_3DH1,
152         TYPE_3DH3,
153         TYPE_3DH5;
154 
155         private static PacketType[] sAllValues = values();
156 
fromOrdinal(int n)157         static PacketType fromOrdinal(int n) {
158             if (n < sAllValues.length) {
159                 return sAllValues[n];
160             }
161             return INVALID;
162         }
163     }
164 
165     enum ConnState {
166         CONN_IDLE(0x00),
167         CONN_ACTIVE(0x81),
168         CONN_HOLD(0x02),
169         CONN_SNIFF_IDLE(0x03),
170         CONN_SNIFF_ACTIVE(0x84),
171         CONN_SNIFF_MASTER_TRANSITION(0x85),
172         CONN_PARK(0x06),
173         CONN_PARK_PEND(0x47),
174         CONN_UNPARK_PEND(0x08),
175         CONN_UNPARK_ACTIVE(0x89),
176         CONN_DISCONNECT_PENDING(0x4A),
177         CONN_PAGING(0x0B),
178         CONN_PAGE_SCAN(0x0C),
179         CONN_LOCAL_LOOPBACK(0x0D),
180         CONN_LE_ACTIVE(0x0E),
181         CONN_ANT_ACTIVE(0x0F),
182         CONN_TRIGGER_SCAN(0x10),
183         CONN_RECONNECTING(0x11),
184         CONN_SEMI_CONN(0x12);
185 
186         private final int mValue;
187         private static ConnState[] sAllStates = values();
188 
ConnState(int val)189         ConnState(int val) {
190             mValue = val;
191         }
192 
toString(int val)193         public static String toString(int val) {
194             for (ConnState state : sAllStates) {
195                 if (state.mValue == val) {
196                     return state.toString();
197                 }
198             }
199             return "INVALID";
200         }
201     }
202 
203     enum LinkQuality {
204         ULTRA_HIGH,
205         HIGH,
206         STANDARD,
207         MEDIUM,
208         LOW,
209         INVALID;
210 
211         private static LinkQuality[] sAllValues = values();
212 
fromOrdinal(int n)213         static LinkQuality fromOrdinal(int n) {
214             if (n < sAllValues.length - 1) {
215                 return sAllValues[n];
216             }
217             return INVALID;
218         }
219     }
220 
221     enum AirMode {
222         uLaw,
223         aLaw,
224         CVSD,
225         transparent_msbc,
226         INVALID;
227 
228         private static AirMode[] sAllValues = values();
229 
fromOrdinal(int n)230         static AirMode fromOrdinal(int n) {
231             if (n < sAllValues.length - 1) {
232                 return sAllValues[n];
233             }
234             return INVALID;
235         }
236     }
237 
BluetoothQualityReport( String remoteAddr, int lmpVer, int lmpSubVer, int manufacturerId, String remoteName, BluetoothClass bluetoothClass, byte[] rawData)238     private BluetoothQualityReport(
239             String remoteAddr,
240             int lmpVer,
241             int lmpSubVer,
242             int manufacturerId,
243             String remoteName,
244             BluetoothClass bluetoothClass,
245             byte[] rawData) {
246         mAddr = remoteAddr;
247         mLmpVer = lmpVer;
248         mLmpSubVer = lmpSubVer;
249         mManufacturerId = manufacturerId;
250         mName = remoteName;
251         mBluetoothClass = bluetoothClass;
252 
253         mBqrCommon = new BqrCommon(rawData, 0);
254         int id = mBqrCommon.getQualityReportId();
255         if (id == QUALITY_REPORT_ID_MONITOR) return;
256 
257         int vsPartOffset = BqrCommon.BQR_COMMON_LEN;
258         if (id == QUALITY_REPORT_ID_APPROACH_LSTO) {
259             mBqrVsLsto = new BqrVsLsto(rawData, vsPartOffset);
260         } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) {
261             mBqrVsA2dpChoppy = new BqrVsA2dpChoppy(rawData, vsPartOffset);
262         } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) {
263             mBqrVsScoChoppy = new BqrVsScoChoppy(rawData, vsPartOffset);
264         } else if (id == QUALITY_REPORT_ID_CONN_FAIL) {
265             mBqrConnectFail = new BqrConnectFail(rawData, vsPartOffset);
266         } else {
267             throw new IllegalArgumentException(TAG + ": unknown quality report id:" + id);
268         }
269     }
270 
BluetoothQualityReport(Parcel in)271     private BluetoothQualityReport(Parcel in) {
272         mAddr = in.readString();
273         mLmpVer = in.readInt();
274         mLmpSubVer = in.readInt();
275         mManufacturerId = in.readInt();
276         mName = in.readString();
277         mBluetoothClass = new BluetoothClass(in.readInt());
278 
279         mBqrCommon = new BqrCommon(in);
280         int id = mBqrCommon.getQualityReportId();
281         if (id == QUALITY_REPORT_ID_APPROACH_LSTO) {
282             mBqrVsLsto = new BqrVsLsto(in);
283         } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) {
284             mBqrVsA2dpChoppy = new BqrVsA2dpChoppy(in);
285         } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) {
286             mBqrVsScoChoppy = new BqrVsScoChoppy(in);
287         } else if (id == QUALITY_REPORT_ID_CONN_FAIL) {
288             mBqrConnectFail = new BqrConnectFail(in);
289         }
290     }
291 
292     /**
293      * Get the quality report id.
294      *
295      * @hide
296      */
297     @SystemApi
298     @QualityReportId
getQualityReportId()299     public int getQualityReportId() {
300         return mBqrCommon.getQualityReportId();
301     }
302 
303     /**
304      * Get the string of the quality report id.
305      *
306      * @return the string of the id
307      * @hide
308      */
309     @SystemApi
qualityReportIdToString(@ualityReportId int id)310     public static @NonNull String qualityReportIdToString(@QualityReportId int id) {
311         return BqrCommon.qualityReportIdToString(id);
312     }
313 
314     /**
315      * Get bluetooth address of remote device in this report.
316      *
317      * @return bluetooth address of remote device
318      * @hide
319      */
320     @SystemApi
getRemoteAddress()321     public @Nullable String getRemoteAddress() {
322         return mAddr;
323     }
324 
325     /**
326      * Get LMP version of remote device in this report.
327      *
328      * @return LMP version of remote device
329      * @hide
330      */
331     @SystemApi
getLmpVersion()332     public int getLmpVersion() {
333         return mLmpVer;
334     }
335 
336     /**
337      * Get LMP subVersion of remote device in this report.
338      *
339      * @return LMP subVersion of remote device
340      * @hide
341      */
342     @SystemApi
getLmpSubVersion()343     public int getLmpSubVersion() {
344         return mLmpSubVer;
345     }
346 
347     /**
348      * Get manufacturer id of remote device in this report.
349      *
350      * @return manufacturer id of remote device
351      * @hide
352      */
353     @SystemApi
getManufacturerId()354     public int getManufacturerId() {
355         return mManufacturerId;
356     }
357 
358     /**
359      * Get the name of remote device in this report.
360      *
361      * @return the name of remote device
362      * @hide
363      */
364     @SystemApi
getRemoteName()365     public @Nullable String getRemoteName() {
366         return mName;
367     }
368 
369     /**
370      * Get the class of remote device in this report.
371      *
372      * @return the class of remote device
373      * @hide
374      */
375     @SystemApi
getBluetoothClass()376     public @Nullable BluetoothClass getBluetoothClass() {
377         return mBluetoothClass;
378     }
379 
380     /**
381      * Get the {@link BluetoothQualityReport.BqrCommon} object.
382      *
383      * @return the {@link BluetoothQualityReport.BqrCommon} object.
384      * @hide
385      */
386     @SystemApi
getBqrCommon()387     public @Nullable BqrCommon getBqrCommon() {
388         return mBqrCommon;
389     }
390 
391     /**
392      * Get the event data object based on current Quality Report Id. If the report id is {@link
393      * #QUALITY_REPORT_ID_MONITOR}, this returns a {@link BluetoothQualityReport.BqrCommon} object.
394      * If the report id is {@link #QUALITY_REPORT_ID_APPROACH_LSTO}, this returns a {@link
395      * BluetoothQualityReport.BqrVsLsto} object. If the report id is {@link
396      * #QUALITY_REPORT_ID_A2DP_CHOPPY}, this returns a {@link
397      * BluetoothQualityReport.BqrVsA2dpChoppy} object. If the report id is {@link
398      * #QUALITY_REPORT_ID_SCO_CHOPPY}, this returns a {@link BluetoothQualityReport.BqrVsScoChoppy}
399      * object. If the report id is {@link #QUALITY_REPORT_ID_CONN_FAIL}, this returns a {@link
400      * BluetoothQualityReport.BqrConnectFail} object. If the report id is none of the above, this
401      * returns {@code null}.
402      *
403      * @return the event data object based on the quality report id
404      * @hide
405      */
406     @SystemApi
getBqrEvent()407     public @Nullable Parcelable getBqrEvent() {
408         if (mBqrCommon == null) {
409             return null;
410         }
411         switch (mBqrCommon.getQualityReportId()) {
412             case QUALITY_REPORT_ID_MONITOR:
413                 return mBqrCommon;
414             case QUALITY_REPORT_ID_APPROACH_LSTO:
415                 return mBqrVsLsto;
416             case QUALITY_REPORT_ID_A2DP_CHOPPY:
417                 return mBqrVsA2dpChoppy;
418             case QUALITY_REPORT_ID_SCO_CHOPPY:
419                 return mBqrVsScoChoppy;
420             case QUALITY_REPORT_ID_CONN_FAIL:
421                 return mBqrConnectFail;
422             default:
423                 return null;
424         }
425     }
426 
427     /** @hide */
428     @SystemApi
429     public static final @NonNull Parcelable.Creator<BluetoothQualityReport> CREATOR =
430             new Parcelable.Creator<BluetoothQualityReport>() {
431                 public BluetoothQualityReport createFromParcel(Parcel in) {
432                     return new BluetoothQualityReport(in);
433                 }
434 
435                 public BluetoothQualityReport[] newArray(int size) {
436                     return new BluetoothQualityReport[size];
437                 }
438             };
439 
440     /**
441      * Describe contents.
442      *
443      * @return 0
444      * @hide
445      */
describeContents()446     public int describeContents() {
447         return 0;
448     }
449 
450     /**
451      * Write BluetoothQualityReport to parcel.
452      *
453      * @hide
454      */
455     @SystemApi
456     @Override
writeToParcel(@onNull Parcel out, int flags)457     public void writeToParcel(@NonNull Parcel out, int flags) {
458         out.writeString(mAddr);
459         out.writeInt(mLmpVer);
460         out.writeInt(mLmpSubVer);
461         out.writeInt(mManufacturerId);
462         out.writeString(mName);
463         out.writeInt(mBluetoothClass.getClassOfDevice());
464         mBqrCommon.writeToParcel(out, flags);
465         int id = mBqrCommon.getQualityReportId();
466         if (id == QUALITY_REPORT_ID_APPROACH_LSTO) {
467             mBqrVsLsto.writeToParcel(out, flags);
468         } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) {
469             mBqrVsA2dpChoppy.writeToParcel(out, flags);
470         } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) {
471             mBqrVsScoChoppy.writeToParcel(out, flags);
472         } else if (id == QUALITY_REPORT_ID_CONN_FAIL) {
473             mBqrConnectFail.writeToParcel(out, flags);
474         }
475     }
476 
477     /** BluetoothQualityReport to String. */
478     @Override
479     @NonNull
toString()480     public String toString() {
481         String str;
482         str =
483                 "BQR: {\n"
484                         + "  mAddr: "
485                         + mAddr
486                         + ", mLmpVer: "
487                         + String.format("0x%02X", mLmpVer)
488                         + ", mLmpSubVer: "
489                         + String.format("0x%04X", mLmpSubVer)
490                         + ", mManufacturerId: "
491                         + String.format("0x%04X", mManufacturerId)
492                         + ", mName: "
493                         + mName
494                         + ", mBluetoothClass: "
495                         + mBluetoothClass.toString()
496                         + ",\n"
497                         + mBqrCommon
498                         + "\n";
499 
500         int id = mBqrCommon.getQualityReportId();
501         if (id == QUALITY_REPORT_ID_APPROACH_LSTO) {
502             str += mBqrVsLsto + "\n}";
503         } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) {
504             str += mBqrVsA2dpChoppy + "\n}";
505         } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) {
506             str += mBqrVsScoChoppy + "\n}";
507         } else if (id == QUALITY_REPORT_ID_CONN_FAIL) {
508             str += mBqrConnectFail + "\n}";
509         } else if (id == QUALITY_REPORT_ID_MONITOR) {
510             str += "}";
511         }
512 
513         return str;
514     }
515 
516     /**
517      * Builder for new instances of {@link BluetoothQualityReport}.
518      *
519      * @hide
520      */
521     @SystemApi
522     public static final class Builder {
523         private String remoteAddr = "00:00:00:00:00:00";
524         private int lmpVer;
525         private int lmpSubVer;
526         private int manufacturerId;
527         private String remoteName = "";
528         private BluetoothClass bluetoothClass = new BluetoothClass(0);
529         private byte[] rawData;
530 
531         /**
532          * Creates a new instance of {@link Builder}.
533          *
534          * @return The new instance
535          * @throws NullPointerException if rawData is null
536          * @hide
537          */
538         @SystemApi
Builder(@onNull byte[] rawData)539         public Builder(@NonNull byte[] rawData) {
540             this.rawData = Objects.requireNonNull(rawData);
541         }
542 
543         /**
544          * Sets the Remote Device Address (big-endian) attribute for the new instance of {@link
545          * BluetoothQualityReport}.
546          *
547          * @param remoteAddr the Remote Device Address (big-endian) attribute
548          * @hide
549          */
550         @NonNull
551         @SystemApi
setRemoteAddress(@ullable String remoteAddr)552         public Builder setRemoteAddress(@Nullable String remoteAddr) {
553             if (!BluetoothAdapter.checkBluetoothAddress(remoteAddr)) {
554                 Log.d(TAG, "remote address is not a valid bluetooth address: " + remoteAddr);
555             } else {
556                 this.remoteAddr = remoteAddr;
557             }
558             return this;
559         }
560 
561         /**
562          * Sets the Link Manager Protocol Version attribute for the new instance of {@link
563          * BluetoothQualityReport}.
564          *
565          * @param lmpVer the Link Manager Protocol Version attribute
566          * @hide
567          */
568         @NonNull
569         @SystemApi
setLmpVersion(int lmpVer)570         public Builder setLmpVersion(int lmpVer) {
571             this.lmpVer = lmpVer;
572             return this;
573         }
574 
575         /**
576          * Sets the Link Manager Protocol SubVersion attribute for the new instance of {@link
577          * BluetoothQualityReport}.
578          *
579          * @param lmpSubVer the Link Manager Protocol SubVersion attribute
580          * @hide
581          */
582         @NonNull
583         @SystemApi
setLmpSubVersion(int lmpSubVer)584         public Builder setLmpSubVersion(int lmpSubVer) {
585             this.lmpSubVer = lmpSubVer;
586             return this;
587         }
588 
589         /**
590          * Sets the Manufacturer Id attribute for the new instance of {@link
591          * BluetoothQualityReport}.
592          *
593          * @param manufacturerId the Manufacturer Id attribute
594          * @hide
595          */
596         @NonNull
597         @SystemApi
setManufacturerId(int manufacturerId)598         public Builder setManufacturerId(int manufacturerId) {
599             this.manufacturerId = manufacturerId;
600             return this;
601         }
602 
603         /**
604          * Sets the Remote Device Name attribute for the new instance of {@link
605          * BluetoothQualityReport}.
606          *
607          * @param remoteName the Remote Device Name attribute
608          * @hide
609          */
610         @NonNull
611         @SystemApi
setRemoteName(@ullable String remoteName)612         public Builder setRemoteName(@Nullable String remoteName) {
613             if (remoteName == null) {
614                 Log.d(TAG, "remote name is null");
615             } else {
616                 this.remoteName = remoteName;
617             }
618             return this;
619         }
620 
621         /**
622          * Sets the Bluetooth Class of Remote Device attribute for the new instance of {@link
623          * BluetoothQualityReport}.
624          *
625          * @param bluetoothClass the Remote Class of Device attribute
626          * @hide
627          */
628         @NonNull
629         @SystemApi
setBluetoothClass(@ullable BluetoothClass bluetoothClass)630         public Builder setBluetoothClass(@Nullable BluetoothClass bluetoothClass) {
631             if (bluetoothClass == null) {
632                 Log.d(TAG, "remote bluetooth class is null");
633             } else {
634                 this.bluetoothClass = bluetoothClass;
635             }
636             return this;
637         }
638 
639         /**
640          * Creates a new instance of {@link BluetoothQualityReport}.
641          *
642          * @return The new instance
643          * @throws IllegalArgumentException Unsupported Quality Report Id or invalid raw data
644          * @hide
645          */
646         @NonNull
647         @SystemApi
build()648         public BluetoothQualityReport build() {
649             return new BluetoothQualityReport(
650                     remoteAddr,
651                     lmpVer,
652                     lmpSubVer,
653                     manufacturerId,
654                     remoteName,
655                     bluetoothClass,
656                     rawData);
657         }
658     }
659 
660     /**
661      * This class provides the System APIs to access the common part of BQR event.
662      *
663      * @hide
664      */
665     @SystemApi
666     public static final class BqrCommon implements Parcelable {
667         private static final String TAG = BluetoothQualityReport.TAG + ".BqrCommon";
668         static final int BQR_COMMON_LEN = 55;
669 
670         private int mQualityReportId;
671         private int mPacketType;
672         private int mConnectionHandle;
673         private int mConnectionRole;
674         private int mTxPowerLevel;
675         private int mRssi;
676         private int mSnr;
677         private int mUnusedAfhChannelCount;
678         private int mAfhSelectUnidealChannelCount;
679         private int mLsto;
680         private long mPiconetClock;
681         private long mRetransmissionCount;
682         private long mNoRxCount;
683         private long mNakCount;
684         private long mLastTxAckTimestamp;
685         private long mFlowOffCount;
686         private long mLastFlowOnTimestamp;
687         private long mOverflowCount;
688         private long mUnderflowCount;
689         private String mAddr;
690         private int mCalFailedItemCount;
691 
BqrCommon(byte[] rawData, int offset)692         private BqrCommon(byte[] rawData, int offset) {
693             if (rawData == null || rawData.length < offset + BQR_COMMON_LEN) {
694                 throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal.");
695             }
696 
697             ByteBuffer bqrBuf =
698                     ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer();
699             bqrBuf.order(ByteOrder.LITTLE_ENDIAN);
700 
701             mQualityReportId = bqrBuf.get() & 0xFF;
702             mPacketType = bqrBuf.get() & 0xFF;
703             mConnectionHandle = bqrBuf.getShort() & 0xFFFF;
704             mConnectionRole = bqrBuf.get() & 0xFF;
705             mTxPowerLevel = bqrBuf.get() & 0xFF;
706             mRssi = bqrBuf.get();
707             mSnr = bqrBuf.get();
708             mUnusedAfhChannelCount = bqrBuf.get() & 0xFF;
709             mAfhSelectUnidealChannelCount = bqrBuf.get() & 0xFF;
710             mLsto = bqrBuf.getShort() & 0xFFFF;
711             mPiconetClock = bqrBuf.getInt() & 0xFFFFFFFFL;
712             mRetransmissionCount = bqrBuf.getInt() & 0xFFFFFFFFL;
713             mNoRxCount = bqrBuf.getInt() & 0xFFFFFFFFL;
714             mNakCount = bqrBuf.getInt() & 0xFFFFFFFFL;
715             mLastTxAckTimestamp = bqrBuf.getInt() & 0xFFFFFFFFL;
716             mFlowOffCount = bqrBuf.getInt() & 0xFFFFFFFFL;
717             mLastFlowOnTimestamp = bqrBuf.getInt() & 0xFFFFFFFFL;
718             mOverflowCount = bqrBuf.getInt() & 0xFFFFFFFFL;
719             mUnderflowCount = bqrBuf.getInt() & 0xFFFFFFFFL;
720             int currentOffset = bqrBuf.position();
721             mAddr =
722                     String.format(
723                             "%02X:%02X:%02X:%02X:%02X:%02X",
724                             bqrBuf.get(currentOffset + 5),
725                             bqrBuf.get(currentOffset + 4),
726                             bqrBuf.get(currentOffset + 3),
727                             bqrBuf.get(currentOffset + 2),
728                             bqrBuf.get(currentOffset + 1),
729                             bqrBuf.get(currentOffset + 0));
730             bqrBuf.position(currentOffset + 6);
731             mCalFailedItemCount = bqrBuf.get() & 0xFF;
732         }
733 
BqrCommon(Parcel in)734         private BqrCommon(Parcel in) {
735             mQualityReportId = in.readInt();
736             mPacketType = in.readInt();
737             mConnectionHandle = in.readInt();
738             mConnectionRole = in.readInt();
739             mTxPowerLevel = in.readInt();
740             mRssi = in.readInt();
741             mSnr = in.readInt();
742             mUnusedAfhChannelCount = in.readInt();
743             mAfhSelectUnidealChannelCount = in.readInt();
744             mLsto = in.readInt();
745             mPiconetClock = in.readLong();
746             mRetransmissionCount = in.readLong();
747             mNoRxCount = in.readLong();
748             mNakCount = in.readLong();
749             mLastTxAckTimestamp = in.readLong();
750             mFlowOffCount = in.readLong();
751             mLastFlowOnTimestamp = in.readLong();
752             mOverflowCount = in.readLong();
753             mUnderflowCount = in.readLong();
754             mAddr = in.readString();
755             mCalFailedItemCount = in.readInt();
756         }
757 
getQualityReportId()758         int getQualityReportId() {
759             return mQualityReportId;
760         }
761 
qualityReportIdToString(@ualityReportId int id)762         static String qualityReportIdToString(@QualityReportId int id) {
763             switch (id) {
764                 case QUALITY_REPORT_ID_MONITOR:
765                     return "Quality monitor";
766                 case QUALITY_REPORT_ID_APPROACH_LSTO:
767                     return "Approaching LSTO";
768                 case QUALITY_REPORT_ID_A2DP_CHOPPY:
769                     return "A2DP choppy";
770                 case QUALITY_REPORT_ID_SCO_CHOPPY:
771                     return "SCO choppy";
772                 case QUALITY_REPORT_ID_CONN_FAIL:
773                     return "Connect fail";
774                 default:
775                     return "INVALID";
776             }
777         }
778 
779         /**
780          * Get the packet type of the connection.
781          *
782          * @return the packet type
783          * @hide
784          */
785         @SystemApi
getPacketType()786         public int getPacketType() {
787             return mPacketType;
788         }
789 
790         /**
791          * Get the string of packet type.
792          *
793          * @param packetType packet type of the connection
794          * @return the string of packet type
795          * @hide
796          */
797         @SystemApi
packetTypeToString(int packetType)798         public static @Nullable String packetTypeToString(int packetType) {
799             PacketType type = PacketType.fromOrdinal(packetType);
800             return type.toString();
801         }
802 
803         /**
804          * Get the connection handle of the connection.
805          *
806          * @return the connection handle
807          * @hide
808          */
809         @SystemApi
getConnectionHandle()810         public int getConnectionHandle() {
811             return mConnectionHandle;
812         }
813 
814         /**
815          * Connection role: central.
816          *
817          * @hide
818          */
819         @SystemApi public static final int CONNECTION_ROLE_CENTRAL = 0;
820 
821         /**
822          * Connection role: peripheral.
823          *
824          * @hide
825          */
826         @SystemApi public static final int CONNECTION_ROLE_PERIPHERAL = 1;
827 
828         /** @hide */
829         @Retention(RetentionPolicy.SOURCE)
830         @IntDef(
831                 prefix = {"CONNECTION_ROLE"},
832                 value = {
833                     CONNECTION_ROLE_CENTRAL,
834                     CONNECTION_ROLE_PERIPHERAL,
835                 })
836         public @interface ConnectionRole {}
837 
838         /**
839          * Get the connection Role of the connection.
840          *
841          * @return the connection Role
842          * @hide
843          */
844         @SystemApi
845         @ConnectionRole
getConnectionRole()846         public int getConnectionRole() {
847             return mConnectionRole;
848         }
849 
850         /**
851          * Get the connection Role of the connection, "Central" or "Peripheral".
852          *
853          * @param connectionRole connection Role of the connection
854          * @return the connection Role String
855          * @hide
856          */
857         @SystemApi
connectionRoleToString(int connectionRole)858         public static @NonNull String connectionRoleToString(int connectionRole) {
859             if (connectionRole == CONNECTION_ROLE_CENTRAL) {
860                 return "Central";
861             } else if (connectionRole == CONNECTION_ROLE_PERIPHERAL) {
862                 return "Peripheral";
863             } else {
864                 return "INVALID:" + connectionRole;
865             }
866         }
867 
868         /**
869          * Get the current transmit power level for the connection.
870          *
871          * @return the TX power level
872          * @hide
873          */
874         @SystemApi
getTxPowerLevel()875         public int getTxPowerLevel() {
876             return mTxPowerLevel;
877         }
878 
879         /**
880          * Get the Received Signal Strength Indication (RSSI) value for the connection.
881          *
882          * @return the RSSI
883          * @hide
884          */
885         @SystemApi
getRssi()886         public int getRssi() {
887             return mRssi;
888         }
889 
890         /**
891          * Get the Signal-to-Noise Ratio (SNR) value for the connection.
892          *
893          * @return the SNR
894          * @hide
895          */
896         @SystemApi
getSnr()897         public int getSnr() {
898             return mSnr;
899         }
900 
901         /**
902          * Get the number of unused channels in AFH_channel_map.
903          *
904          * @return the number of unused channels
905          * @hide
906          */
907         @SystemApi
getUnusedAfhChannelCount()908         public int getUnusedAfhChannelCount() {
909             return mUnusedAfhChannelCount;
910         }
911 
912         /**
913          * Get the number of the channels which are interfered and quality is bad but are still
914          * selected for AFH.
915          *
916          * @return the number of the selected unideal channels
917          * @hide
918          */
919         @SystemApi
getAfhSelectUnidealChannelCount()920         public int getAfhSelectUnidealChannelCount() {
921             return mAfhSelectUnidealChannelCount;
922         }
923 
924         /**
925          * Get the current link supervision timeout setting. time_ms: N * 0.625 ms (1 slot).
926          *
927          * @return link supervision timeout value
928          * @hide
929          */
930         @SystemApi
getLsto()931         public int getLsto() {
932             return mLsto;
933         }
934 
935         /**
936          * Get the piconet clock for the specified Connection_Handle. time_ms: N * 0.3125 ms (1
937          * Bluetooth Clock).
938          *
939          * @return the piconet clock
940          * @hide
941          */
942         @SystemApi
getPiconetClock()943         public long getPiconetClock() {
944             return mPiconetClock;
945         }
946 
947         /**
948          * Get the count of retransmission.
949          *
950          * @return the count of retransmission
951          * @hide
952          */
953         @SystemApi
getRetransmissionCount()954         public long getRetransmissionCount() {
955             return mRetransmissionCount;
956         }
957 
958         /**
959          * Get the count of no RX.
960          *
961          * @return the count of no RX
962          * @hide
963          */
964         @SystemApi
getNoRxCount()965         public long getNoRxCount() {
966             return mNoRxCount;
967         }
968 
969         /**
970          * Get the count of NAK(Negative Acknowledge).
971          *
972          * @return the count of NAK
973          * @hide
974          */
975         @SystemApi
getNakCount()976         public long getNakCount() {
977             return mNakCount;
978         }
979 
980         /**
981          * Get the timestamp of last TX ACK. time_ms: N * 0.3125 ms (1 Bluetooth Clock).
982          *
983          * @return the timestamp of last TX ACK
984          * @hide
985          */
986         @SystemApi
getLastTxAckTimestamp()987         public long getLastTxAckTimestamp() {
988             return mLastTxAckTimestamp;
989         }
990 
991         /**
992          * Get the count of flow-off.
993          *
994          * @return the count of flow-off
995          * @hide
996          */
997         @SystemApi
getFlowOffCount()998         public long getFlowOffCount() {
999             return mFlowOffCount;
1000         }
1001 
1002         /**
1003          * Get the timestamp of last flow-on.
1004          *
1005          * @return the timestamp of last flow-on
1006          * @hide
1007          */
1008         @SystemApi
getLastFlowOnTimestamp()1009         public long getLastFlowOnTimestamp() {
1010             return mLastFlowOnTimestamp;
1011         }
1012 
1013         /**
1014          * Get the buffer overflow count (how many bytes of TX data are dropped) since the last
1015          * event.
1016          *
1017          * @return the buffer overflow count
1018          * @hide
1019          */
1020         @SystemApi
getOverflowCount()1021         public long getOverflowCount() {
1022             return mOverflowCount;
1023         }
1024 
1025         /**
1026          * Get the buffer underflow count (in byte).
1027          *
1028          * @return the buffer underflow count
1029          * @hide
1030          */
1031         @SystemApi
getUnderflowCount()1032         public long getUnderflowCount() {
1033             return mUnderflowCount;
1034         }
1035 
1036         /**
1037          * Get the count of calibration failed items.
1038          *
1039          * @return the count of calibration failure
1040          * @hide
1041          */
1042         @SystemApi
getCalFailedItemCount()1043         public int getCalFailedItemCount() {
1044             return mCalFailedItemCount;
1045         }
1046 
1047         /**
1048          * Describe contents.
1049          *
1050          * @return 0
1051          * @hide
1052          */
describeContents()1053         public int describeContents() {
1054             return 0;
1055         }
1056 
1057         /**
1058          * Write BqrCommon to parcel.
1059          *
1060          * @hide
1061          */
1062         @SystemApi
1063         @Override
writeToParcel(@onNull Parcel dest, int flags)1064         public void writeToParcel(@NonNull Parcel dest, int flags) {
1065             dest.writeInt(mQualityReportId);
1066             dest.writeInt(mPacketType);
1067             dest.writeInt(mConnectionHandle);
1068             dest.writeInt(mConnectionRole);
1069             dest.writeInt(mTxPowerLevel);
1070             dest.writeInt(mRssi);
1071             dest.writeInt(mSnr);
1072             dest.writeInt(mUnusedAfhChannelCount);
1073             dest.writeInt(mAfhSelectUnidealChannelCount);
1074             dest.writeInt(mLsto);
1075             dest.writeLong(mPiconetClock);
1076             dest.writeLong(mRetransmissionCount);
1077             dest.writeLong(mNoRxCount);
1078             dest.writeLong(mNakCount);
1079             dest.writeLong(mLastTxAckTimestamp);
1080             dest.writeLong(mFlowOffCount);
1081             dest.writeLong(mLastFlowOnTimestamp);
1082             dest.writeLong(mOverflowCount);
1083             dest.writeLong(mUnderflowCount);
1084             dest.writeString(mAddr);
1085             dest.writeInt(mCalFailedItemCount);
1086         }
1087 
1088         /** @hide */
1089         @SystemApi
1090         public static final @NonNull Parcelable.Creator<BqrCommon> CREATOR =
1091                 new Parcelable.Creator<BqrCommon>() {
1092                     public BqrCommon createFromParcel(Parcel in) {
1093                         return new BqrCommon(in);
1094                     }
1095 
1096                     public BqrCommon[] newArray(int size) {
1097                         return new BqrCommon[size];
1098                     }
1099                 };
1100 
1101         /** BqrCommon to String. */
1102         @Override
1103         @NonNull
toString()1104         public String toString() {
1105             String str;
1106             str =
1107                     "  BqrCommon: {\n"
1108                             + "    mQualityReportId: "
1109                             + qualityReportIdToString(getQualityReportId())
1110                             + "("
1111                             + String.format("0x%02X", mQualityReportId)
1112                             + ")"
1113                             + ", mPacketType: "
1114                             + packetTypeToString(mPacketType)
1115                             + "("
1116                             + String.format("0x%02X", mPacketType)
1117                             + ")"
1118                             + ", mConnectionHandle: "
1119                             + String.format("0x%04X", mConnectionHandle)
1120                             + ", mConnectionRole: "
1121                             + getConnectionRole()
1122                             + "("
1123                             + mConnectionRole
1124                             + ")"
1125                             + ", mTxPowerLevel: "
1126                             + mTxPowerLevel
1127                             + ", mRssi: "
1128                             + mRssi
1129                             + ", mSnr: "
1130                             + mSnr
1131                             + ", mUnusedAfhChannelCount: "
1132                             + mUnusedAfhChannelCount
1133                             + ",\n"
1134                             + "    mAfhSelectUnidealChannelCount: "
1135                             + mAfhSelectUnidealChannelCount
1136                             + ", mLsto: "
1137                             + mLsto
1138                             + ", mPiconetClock: "
1139                             + String.format("0x%08X", mPiconetClock)
1140                             + ", mRetransmissionCount: "
1141                             + mRetransmissionCount
1142                             + ", mNoRxCount: "
1143                             + mNoRxCount
1144                             + ", mNakCount: "
1145                             + mNakCount
1146                             + ", mLastTxAckTimestamp: "
1147                             + String.format("0x%08X", mLastTxAckTimestamp)
1148                             + ", mFlowOffCount: "
1149                             + mFlowOffCount
1150                             + ",\n"
1151                             + "    mLastFlowOnTimestamp: "
1152                             + String.format("0x%08X", mLastFlowOnTimestamp)
1153                             + ", mOverflowCount: "
1154                             + mOverflowCount
1155                             + ", mUnderflowCount: "
1156                             + mUnderflowCount
1157                             + ", mAddr: "
1158                             + mAddr
1159                             + ", mCalFailedItemCount: "
1160                             + mCalFailedItemCount
1161                             + "\n  }";
1162 
1163             return str;
1164         }
1165     }
1166 
1167     /**
1168      * This class provides the System APIs to access the vendor specific part of Approaching LSTO
1169      * event.
1170      *
1171      * @hide
1172      */
1173     @SystemApi
1174     public static final class BqrVsLsto implements Parcelable {
1175         private static final String TAG = BluetoothQualityReport.TAG + ".BqrVsLsto";
1176 
1177         private int mConnState;
1178         private long mBasebandStats;
1179         private long mSlotsUsed;
1180         private int mCxmDenials;
1181         private int mTxSkipped;
1182         private int mRfLoss;
1183         private long mNativeClock;
1184         private long mLastTxAckTimestamp;
1185 
BqrVsLsto(byte[] rawData, int offset)1186         private BqrVsLsto(byte[] rawData, int offset) {
1187             if (rawData == null || rawData.length <= offset) {
1188                 throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal.");
1189             }
1190 
1191             ByteBuffer bqrBuf =
1192                     ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer();
1193             bqrBuf.order(ByteOrder.LITTLE_ENDIAN);
1194 
1195             mConnState = bqrBuf.get() & 0xFF;
1196             mBasebandStats = bqrBuf.getInt() & 0xFFFFFFFFL;
1197             mSlotsUsed = bqrBuf.getInt() & 0xFFFFFFFFL;
1198             mCxmDenials = bqrBuf.getShort() & 0xFFFF;
1199             mTxSkipped = bqrBuf.getShort() & 0xFFFF;
1200             mRfLoss = bqrBuf.getShort() & 0xFFFF;
1201             mNativeClock = bqrBuf.getInt() & 0xFFFFFFFFL;
1202             mLastTxAckTimestamp = bqrBuf.getInt() & 0xFFFFFFFFL;
1203         }
1204 
BqrVsLsto(Parcel in)1205         private BqrVsLsto(Parcel in) {
1206             mConnState = in.readInt();
1207             mBasebandStats = in.readLong();
1208             mSlotsUsed = in.readLong();
1209             mCxmDenials = in.readInt();
1210             mTxSkipped = in.readInt();
1211             mRfLoss = in.readInt();
1212             mNativeClock = in.readLong();
1213             mLastTxAckTimestamp = in.readLong();
1214         }
1215 
1216         /**
1217          * Get the conn state of sco.
1218          *
1219          * @return the conn state
1220          * @hide
1221          */
1222         @SystemApi
getConnState()1223         public int getConnState() {
1224             return mConnState;
1225         }
1226 
1227         /**
1228          * Get the string of conn state of sco.
1229          *
1230          * @param connectionState connection state of sco
1231          * @return the string of conn state
1232          * @hide
1233          */
1234         @SystemApi
connStateToString(int connectionState)1235         public static @Nullable String connStateToString(int connectionState) {
1236             return ConnState.toString(connectionState);
1237         }
1238 
1239         /**
1240          * Get the baseband statistics.
1241          *
1242          * @return the baseband statistics
1243          * @hide
1244          */
1245         @SystemApi
getBasebandStats()1246         public long getBasebandStats() {
1247             return mBasebandStats;
1248         }
1249 
1250         /**
1251          * Get the count of slots allocated for current connection.
1252          *
1253          * @return the count of slots allocated for current connection
1254          * @hide
1255          */
1256         @SystemApi
getSlotsUsed()1257         public long getSlotsUsed() {
1258             return mSlotsUsed;
1259         }
1260 
1261         /**
1262          * Get the count of Coex denials.
1263          *
1264          * @return the count of CXM denials
1265          * @hide
1266          */
1267         @SystemApi
getCxmDenials()1268         public int getCxmDenials() {
1269             return mCxmDenials;
1270         }
1271 
1272         /**
1273          * Get the count of TX skipped when no poll from remote device.
1274          *
1275          * @return the count of TX skipped
1276          * @hide
1277          */
1278         @SystemApi
getTxSkipped()1279         public int getTxSkipped() {
1280             return mTxSkipped;
1281         }
1282 
1283         /**
1284          * Get the count of RF loss.
1285          *
1286          * @return the count of RF loss
1287          * @hide
1288          */
1289         @SystemApi
getRfLoss()1290         public int getRfLoss() {
1291             return mRfLoss;
1292         }
1293 
1294         /**
1295          * Get the timestamp when issue happened. time_ms: N * 0.3125 ms (1 Bluetooth Clock).
1296          *
1297          * @return the timestamp when issue happened
1298          * @hide
1299          */
1300         @SystemApi
getNativeClock()1301         public long getNativeClock() {
1302             return mNativeClock;
1303         }
1304 
1305         /**
1306          * Get the timestamp of last TX ACK. time_ms: N * 0.3125 ms (1 Bluetooth Clock).
1307          *
1308          * @return the timestamp of last TX ACK
1309          * @hide
1310          */
1311         @SystemApi
getLastTxAckTimestamp()1312         public long getLastTxAckTimestamp() {
1313             return mLastTxAckTimestamp;
1314         }
1315 
1316         /**
1317          * Describe contents.
1318          *
1319          * @return 0
1320          * @hide
1321          */
describeContents()1322         public int describeContents() {
1323             return 0;
1324         }
1325 
1326         /**
1327          * Write BqrVsLsto to parcel.
1328          *
1329          * @hide
1330          */
1331         @SystemApi
1332         @Override
writeToParcel(@onNull Parcel dest, int flags)1333         public void writeToParcel(@NonNull Parcel dest, int flags) {
1334             dest.writeInt(mConnState);
1335             dest.writeLong(mBasebandStats);
1336             dest.writeLong(mSlotsUsed);
1337             dest.writeInt(mCxmDenials);
1338             dest.writeInt(mTxSkipped);
1339             dest.writeInt(mRfLoss);
1340             dest.writeLong(mNativeClock);
1341             dest.writeLong(mLastTxAckTimestamp);
1342         }
1343 
1344         /** @hide */
1345         @SystemApi
1346         public static final @NonNull Parcelable.Creator<BqrVsLsto> CREATOR =
1347                 new Parcelable.Creator<BqrVsLsto>() {
1348                     public BqrVsLsto createFromParcel(Parcel in) {
1349                         return new BqrVsLsto(in);
1350                     }
1351 
1352                     public BqrVsLsto[] newArray(int size) {
1353                         return new BqrVsLsto[size];
1354                     }
1355                 };
1356 
1357         /** BqrVsLsto to String. */
1358         @Override
1359         @NonNull
toString()1360         public String toString() {
1361             String str;
1362             str =
1363                     "  BqrVsLsto: {\n"
1364                             + "    mConnState: "
1365                             + connStateToString(getConnState())
1366                             + "("
1367                             + String.format("0x%02X", mConnState)
1368                             + ")"
1369                             + ", mBasebandStats: "
1370                             + String.format("0x%08X", mBasebandStats)
1371                             + ", mSlotsUsed: "
1372                             + mSlotsUsed
1373                             + ", mCxmDenials: "
1374                             + mCxmDenials
1375                             + ", mTxSkipped: "
1376                             + mTxSkipped
1377                             + ", mRfLoss: "
1378                             + mRfLoss
1379                             + ", mNativeClock: "
1380                             + String.format("0x%08X", mNativeClock)
1381                             + ", mLastTxAckTimestamp: "
1382                             + String.format("0x%08X", mLastTxAckTimestamp)
1383                             + "\n  }";
1384 
1385             return str;
1386         }
1387     }
1388 
1389     /**
1390      * This class provides the System APIs to access the vendor specific part of A2dp choppy event.
1391      *
1392      * @hide
1393      */
1394     @SystemApi
1395     public static final class BqrVsA2dpChoppy implements Parcelable {
1396         private static final String TAG = BluetoothQualityReport.TAG + ".BqrVsA2dpChoppy";
1397 
1398         private long mArrivalTime;
1399         private long mScheduleTime;
1400         private int mGlitchCount;
1401         private int mTxCxmDenials;
1402         private int mRxCxmDenials;
1403         private int mAclTxQueueLength;
1404         private int mLinkQuality;
1405 
BqrVsA2dpChoppy(byte[] rawData, int offset)1406         private BqrVsA2dpChoppy(byte[] rawData, int offset) {
1407             if (rawData == null || rawData.length <= offset) {
1408                 throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal.");
1409             }
1410 
1411             ByteBuffer bqrBuf =
1412                     ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer();
1413             bqrBuf.order(ByteOrder.LITTLE_ENDIAN);
1414 
1415             mArrivalTime = bqrBuf.getInt() & 0xFFFFFFFFL;
1416             mScheduleTime = bqrBuf.getInt() & 0xFFFFFFFFL;
1417             mGlitchCount = bqrBuf.getShort() & 0xFFFF;
1418             mTxCxmDenials = bqrBuf.getShort() & 0xFFFF;
1419             mRxCxmDenials = bqrBuf.getShort() & 0xFFFF;
1420             mAclTxQueueLength = bqrBuf.get() & 0xFF;
1421             mLinkQuality = bqrBuf.get() & 0xFF;
1422         }
1423 
BqrVsA2dpChoppy(Parcel in)1424         private BqrVsA2dpChoppy(Parcel in) {
1425             mArrivalTime = in.readLong();
1426             mScheduleTime = in.readLong();
1427             mGlitchCount = in.readInt();
1428             mTxCxmDenials = in.readInt();
1429             mRxCxmDenials = in.readInt();
1430             mAclTxQueueLength = in.readInt();
1431             mLinkQuality = in.readInt();
1432         }
1433 
1434         /**
1435          * Get the timestamp of a2dp packet arrived. time_ms: N * 0.3125 ms (1 Bluetooth Clock).
1436          *
1437          * @return the timestamp of a2dp packet arrived
1438          * @hide
1439          */
1440         @SystemApi
getArrivalTime()1441         public long getArrivalTime() {
1442             return mArrivalTime;
1443         }
1444 
1445         /**
1446          * Get the timestamp of a2dp packet scheduled. time_ms: N * 0.3125 ms (1 Bluetooth Clock).
1447          *
1448          * @return the timestamp of a2dp packet scheduled
1449          * @hide
1450          */
1451         @SystemApi
getScheduleTime()1452         public long getScheduleTime() {
1453             return mScheduleTime;
1454         }
1455 
1456         /**
1457          * Get the a2dp glitch count since the last event.
1458          *
1459          * @return the a2dp glitch count
1460          * @hide
1461          */
1462         @SystemApi
getGlitchCount()1463         public int getGlitchCount() {
1464             return mGlitchCount;
1465         }
1466 
1467         /**
1468          * Get the count of Coex TX denials.
1469          *
1470          * @return the count of Coex TX denials
1471          * @hide
1472          */
1473         @SystemApi
getTxCxmDenials()1474         public int getTxCxmDenials() {
1475             return mTxCxmDenials;
1476         }
1477 
1478         /**
1479          * Get the count of Coex RX denials.
1480          *
1481          * @return the count of Coex RX denials
1482          * @hide
1483          */
1484         @SystemApi
getRxCxmDenials()1485         public int getRxCxmDenials() {
1486             return mRxCxmDenials;
1487         }
1488 
1489         /**
1490          * Get the ACL queue length which are pending TX in FW.
1491          *
1492          * @return the ACL queue length
1493          * @hide
1494          */
1495         @SystemApi
getAclTxQueueLength()1496         public int getAclTxQueueLength() {
1497             return mAclTxQueueLength;
1498         }
1499 
1500         /**
1501          * Get the link quality for the current connection.
1502          *
1503          * @return the link quality
1504          * @hide
1505          */
1506         @SystemApi
getLinkQuality()1507         public int getLinkQuality() {
1508             return mLinkQuality;
1509         }
1510 
1511         /**
1512          * Get the string of link quality for the current connection.
1513          *
1514          * @param linkQuality link quality for the current connection
1515          * @return the string of link quality
1516          * @hide
1517          */
1518         @SystemApi
linkQualityToString(int linkQuality)1519         public static @Nullable String linkQualityToString(int linkQuality) {
1520             LinkQuality q = LinkQuality.fromOrdinal(linkQuality);
1521             return q.toString();
1522         }
1523 
1524         /**
1525          * Describe contents.
1526          *
1527          * @return 0
1528          * @hide
1529          */
describeContents()1530         public int describeContents() {
1531             return 0;
1532         }
1533 
1534         /**
1535          * Write BqrVsA2dpChoppy to parcel.
1536          *
1537          * @hide
1538          */
1539         @SystemApi
1540         @Override
writeToParcel(@onNull Parcel dest, int flags)1541         public void writeToParcel(@NonNull Parcel dest, int flags) {
1542             dest.writeLong(mArrivalTime);
1543             dest.writeLong(mScheduleTime);
1544             dest.writeInt(mGlitchCount);
1545             dest.writeInt(mTxCxmDenials);
1546             dest.writeInt(mRxCxmDenials);
1547             dest.writeInt(mAclTxQueueLength);
1548             dest.writeInt(mLinkQuality);
1549         }
1550 
1551         /** @hide */
1552         @SystemApi
1553         public static final @NonNull Parcelable.Creator<BqrVsA2dpChoppy> CREATOR =
1554                 new Parcelable.Creator<BqrVsA2dpChoppy>() {
1555                     public BqrVsA2dpChoppy createFromParcel(Parcel in) {
1556                         return new BqrVsA2dpChoppy(in);
1557                     }
1558 
1559                     public BqrVsA2dpChoppy[] newArray(int size) {
1560                         return new BqrVsA2dpChoppy[size];
1561                     }
1562                 };
1563 
1564         /** BqrVsA2dpChoppy to String. */
1565         @Override
1566         @NonNull
toString()1567         public String toString() {
1568             String str;
1569             str =
1570                     "  BqrVsA2dpChoppy: {\n"
1571                             + "    mArrivalTime: "
1572                             + String.format("0x%08X", mArrivalTime)
1573                             + ", mScheduleTime: "
1574                             + String.format("0x%08X", mScheduleTime)
1575                             + ", mGlitchCount: "
1576                             + mGlitchCount
1577                             + ", mTxCxmDenials: "
1578                             + mTxCxmDenials
1579                             + ", mRxCxmDenials: "
1580                             + mRxCxmDenials
1581                             + ", mAclTxQueueLength: "
1582                             + mAclTxQueueLength
1583                             + ", mLinkQuality: "
1584                             + linkQualityToString(mLinkQuality)
1585                             + "("
1586                             + String.format("0x%02X", mLinkQuality)
1587                             + ")"
1588                             + "\n  }";
1589 
1590             return str;
1591         }
1592     }
1593 
1594     /**
1595      * This class provides the System APIs to access the vendor specific part of SCO choppy event.
1596      *
1597      * @hide
1598      */
1599     @SystemApi
1600     public static final class BqrVsScoChoppy implements Parcelable {
1601         private static final String TAG = BluetoothQualityReport.TAG + ".BqrVsScoChoppy";
1602 
1603         private int mGlitchCount;
1604         private int mIntervalEsco;
1605         private int mWindowEsco;
1606         private int mAirFormat;
1607         private int mInstanceCount;
1608         private int mTxCxmDenials;
1609         private int mRxCxmDenials;
1610         private int mTxAbortCount;
1611         private int mLateDispatch;
1612         private int mMicIntrMiss;
1613         private int mLpaIntrMiss;
1614         private int mSprIntrMiss;
1615         private int mPlcFillCount;
1616         private int mPlcDiscardCount;
1617         private int mMissedInstanceCount;
1618         private int mTxRetransmitSlotCount;
1619         private int mRxRetransmitSlotCount;
1620         private int mGoodRxFrameCount;
1621 
BqrVsScoChoppy(byte[] rawData, int offset)1622         private BqrVsScoChoppy(byte[] rawData, int offset) {
1623             if (rawData == null || rawData.length <= offset) {
1624                 throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal.");
1625             }
1626 
1627             ByteBuffer bqrBuf =
1628                     ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer();
1629             bqrBuf.order(ByteOrder.LITTLE_ENDIAN);
1630 
1631             mGlitchCount = bqrBuf.getShort() & 0xFFFF;
1632             mIntervalEsco = bqrBuf.get() & 0xFF;
1633             mWindowEsco = bqrBuf.get() & 0xFF;
1634             mAirFormat = bqrBuf.get() & 0xFF;
1635             mInstanceCount = bqrBuf.getShort() & 0xFFFF;
1636             mTxCxmDenials = bqrBuf.getShort() & 0xFFFF;
1637             mRxCxmDenials = bqrBuf.getShort() & 0xFFFF;
1638             mTxAbortCount = bqrBuf.getShort() & 0xFFFF;
1639             mLateDispatch = bqrBuf.getShort() & 0xFFFF;
1640             mMicIntrMiss = bqrBuf.getShort() & 0xFFFF;
1641             mLpaIntrMiss = bqrBuf.getShort() & 0xFFFF;
1642             mSprIntrMiss = bqrBuf.getShort() & 0xFFFF;
1643             mPlcFillCount = bqrBuf.getShort() & 0xFFFF;
1644             mPlcDiscardCount = bqrBuf.getShort() & 0xFFFF;
1645             mMissedInstanceCount = bqrBuf.getShort() & 0xFFFF;
1646             mTxRetransmitSlotCount = bqrBuf.getShort() & 0xFFFF;
1647             mRxRetransmitSlotCount = bqrBuf.getShort() & 0xFFFF;
1648             mGoodRxFrameCount = bqrBuf.getShort() & 0xFFFF;
1649         }
1650 
BqrVsScoChoppy(Parcel in)1651         private BqrVsScoChoppy(Parcel in) {
1652             mGlitchCount = in.readInt();
1653             mIntervalEsco = in.readInt();
1654             mWindowEsco = in.readInt();
1655             mAirFormat = in.readInt();
1656             mInstanceCount = in.readInt();
1657             mTxCxmDenials = in.readInt();
1658             mRxCxmDenials = in.readInt();
1659             mTxAbortCount = in.readInt();
1660             mLateDispatch = in.readInt();
1661             mMicIntrMiss = in.readInt();
1662             mLpaIntrMiss = in.readInt();
1663             mSprIntrMiss = in.readInt();
1664             mPlcFillCount = in.readInt();
1665             mPlcDiscardCount = in.readInt();
1666             mMissedInstanceCount = in.readInt();
1667             mTxRetransmitSlotCount = in.readInt();
1668             mRxRetransmitSlotCount = in.readInt();
1669             mGoodRxFrameCount = in.readInt();
1670         }
1671 
1672         /**
1673          * Get the sco glitch count since the last event.
1674          *
1675          * @return the sco glitch count
1676          * @hide
1677          */
1678         @SystemApi
getGlitchCount()1679         public int getGlitchCount() {
1680             return mGlitchCount;
1681         }
1682 
1683         /**
1684          * Get ESCO interval in slots. It is the value of Transmission_Interval parameter in
1685          * Synchronous Connection Complete event.
1686          *
1687          * @return ESCO interval in slots
1688          * @hide
1689          */
1690         @SystemApi
getIntervalEsco()1691         public int getIntervalEsco() {
1692             return mIntervalEsco;
1693         }
1694 
1695         /**
1696          * Get ESCO window in slots. It is the value of Retransmission Window parameter in
1697          * Synchronous Connection Complete event.
1698          *
1699          * @return ESCO window in slots
1700          * @hide
1701          */
1702         @SystemApi
getWindowEsco()1703         public int getWindowEsco() {
1704             return mWindowEsco;
1705         }
1706 
1707         /**
1708          * Get the air mode. It is the value of Air Mode parameter in Synchronous Connection
1709          * Complete event.
1710          *
1711          * @return the air mode
1712          * @hide
1713          */
1714         @SystemApi
getAirFormat()1715         public int getAirFormat() {
1716             return mAirFormat;
1717         }
1718 
1719         /**
1720          * Get the string of air mode.
1721          *
1722          * @param airFormat the value of Air Mode parameter in Synchronous Connection Complete event
1723          * @return the string of air mode
1724          * @hide
1725          */
1726         @SystemApi
airFormatToString(int airFormat)1727         public static @Nullable String airFormatToString(int airFormat) {
1728             AirMode m = AirMode.fromOrdinal(airFormat);
1729             return m.toString();
1730         }
1731 
1732         /**
1733          * Get the xSCO instance count.
1734          *
1735          * @return the xSCO instance count
1736          * @hide
1737          */
1738         @SystemApi
getInstanceCount()1739         public int getInstanceCount() {
1740             return mInstanceCount;
1741         }
1742 
1743         /**
1744          * Get the count of Coex TX denials.
1745          *
1746          * @return the count of Coex TX denials
1747          * @hide
1748          */
1749         @SystemApi
getTxCxmDenials()1750         public int getTxCxmDenials() {
1751             return mTxCxmDenials;
1752         }
1753 
1754         /**
1755          * Get the count of Coex RX denials.
1756          *
1757          * @return the count of Coex RX denials
1758          * @hide
1759          */
1760         @SystemApi
getRxCxmDenials()1761         public int getRxCxmDenials() {
1762             return mRxCxmDenials;
1763         }
1764 
1765         /**
1766          * Get the count of sco packets aborted.
1767          *
1768          * @return the count of sco packets aborted
1769          * @hide
1770          */
1771         @SystemApi
getTxAbortCount()1772         public int getTxAbortCount() {
1773             return mTxAbortCount;
1774         }
1775 
1776         /**
1777          * Get the count of sco packets dispatched late.
1778          *
1779          * @return the count of sco packets dispatched late
1780          * @hide
1781          */
1782         @SystemApi
getLateDispatch()1783         public int getLateDispatch() {
1784             return mLateDispatch;
1785         }
1786 
1787         /**
1788          * Get the count of missed Mic interrupts.
1789          *
1790          * @return the count of missed Mic interrupts
1791          * @hide
1792          */
1793         @SystemApi
getMicIntrMiss()1794         public int getMicIntrMiss() {
1795             return mMicIntrMiss;
1796         }
1797 
1798         /**
1799          * Get the count of missed LPA interrupts.
1800          *
1801          * @return the count of missed LPA interrupts
1802          * @hide
1803          */
1804         @SystemApi
getLpaIntrMiss()1805         public int getLpaIntrMiss() {
1806             return mLpaIntrMiss;
1807         }
1808 
1809         /**
1810          * Get the count of missed Speaker interrupts.
1811          *
1812          * @return the count of missed Speaker interrupts
1813          * @hide
1814          */
1815         @SystemApi
getSprIntrMiss()1816         public int getSprIntrMiss() {
1817             return mSprIntrMiss;
1818         }
1819 
1820         /**
1821          * Get the count of packet loss concealment filled.
1822          *
1823          * @return the count of packet loss concealment filled
1824          * @hide
1825          */
1826         @SystemApi
getPlcFillCount()1827         public int getPlcFillCount() {
1828             return mPlcFillCount;
1829         }
1830 
1831         /**
1832          * Get the count of packet loss concealment discarded.
1833          *
1834          * @return the count of packet loss concealment discarded
1835          * @hide
1836          */
1837         @SystemApi
getPlcDiscardCount()1838         public int getPlcDiscardCount() {
1839             return mPlcDiscardCount;
1840         }
1841 
1842         /**
1843          * Get the count of sco instances missed.
1844          *
1845          * @return the count of sco instances missed
1846          * @hide
1847          */
1848         @SystemApi
getMissedInstanceCount()1849         public int getMissedInstanceCount() {
1850             return mMissedInstanceCount;
1851         }
1852 
1853         /**
1854          * Get the count of slots for Tx retransmission.
1855          *
1856          * @return the count of slots for Tx retransmission
1857          * @hide
1858          */
1859         @SystemApi
getTxRetransmitSlotCount()1860         public int getTxRetransmitSlotCount() {
1861             return mTxRetransmitSlotCount;
1862         }
1863 
1864         /**
1865          * Get the count of slots for Rx retransmission.
1866          *
1867          * @return the count of slots for Rx retransmission
1868          * @hide
1869          */
1870         @SystemApi
getRxRetransmitSlotCount()1871         public int getRxRetransmitSlotCount() {
1872             return mRxRetransmitSlotCount;
1873         }
1874 
1875         /**
1876          * Get the count of Rx good packets
1877          *
1878          * @return the count of Rx good packets
1879          * @hide
1880          */
1881         @SystemApi
getGoodRxFrameCount()1882         public int getGoodRxFrameCount() {
1883             return mGoodRxFrameCount;
1884         }
1885 
1886         /**
1887          * Describe contents.
1888          *
1889          * @return 0
1890          * @hide
1891          */
describeContents()1892         public int describeContents() {
1893             return 0;
1894         }
1895 
1896         /**
1897          * Write BqrVsScoChoppy to parcel.
1898          *
1899          * @hide
1900          */
1901         @SystemApi
1902         @Override
writeToParcel(@onNull Parcel dest, int flags)1903         public void writeToParcel(@NonNull Parcel dest, int flags) {
1904             dest.writeInt(mGlitchCount);
1905             dest.writeInt(mIntervalEsco);
1906             dest.writeInt(mWindowEsco);
1907             dest.writeInt(mAirFormat);
1908             dest.writeInt(mInstanceCount);
1909             dest.writeInt(mTxCxmDenials);
1910             dest.writeInt(mRxCxmDenials);
1911             dest.writeInt(mTxAbortCount);
1912             dest.writeInt(mLateDispatch);
1913             dest.writeInt(mMicIntrMiss);
1914             dest.writeInt(mLpaIntrMiss);
1915             dest.writeInt(mSprIntrMiss);
1916             dest.writeInt(mPlcFillCount);
1917             dest.writeInt(mPlcDiscardCount);
1918             dest.writeInt(mMissedInstanceCount);
1919             dest.writeInt(mTxRetransmitSlotCount);
1920             dest.writeInt(mRxRetransmitSlotCount);
1921             dest.writeInt(mGoodRxFrameCount);
1922         }
1923 
1924         /** @hide */
1925         @SystemApi
1926         public static final @NonNull Parcelable.Creator<BqrVsScoChoppy> CREATOR =
1927                 new Parcelable.Creator<BqrVsScoChoppy>() {
1928                     public BqrVsScoChoppy createFromParcel(Parcel in) {
1929                         return new BqrVsScoChoppy(in);
1930                     }
1931 
1932                     public BqrVsScoChoppy[] newArray(int size) {
1933                         return new BqrVsScoChoppy[size];
1934                     }
1935                 };
1936 
1937         /** BqrVsScoChoppy to String. */
1938         @Override
1939         @NonNull
toString()1940         public String toString() {
1941             String str;
1942             str =
1943                     "  BqrVsScoChoppy: {\n"
1944                             + "    mGlitchCount: "
1945                             + mGlitchCount
1946                             + ", mIntervalEsco: "
1947                             + mIntervalEsco
1948                             + ", mWindowEsco: "
1949                             + mWindowEsco
1950                             + ", mAirFormat: "
1951                             + airFormatToString(mAirFormat)
1952                             + "("
1953                             + String.format("0x%02X", mAirFormat)
1954                             + ")"
1955                             + ", mInstanceCount: "
1956                             + mInstanceCount
1957                             + ", mTxCxmDenials: "
1958                             + mTxCxmDenials
1959                             + ", mRxCxmDenials: "
1960                             + mRxCxmDenials
1961                             + ", mTxAbortCount: "
1962                             + mTxAbortCount
1963                             + ",\n"
1964                             + "    mLateDispatch: "
1965                             + mLateDispatch
1966                             + ", mMicIntrMiss: "
1967                             + mMicIntrMiss
1968                             + ", mLpaIntrMiss: "
1969                             + mLpaIntrMiss
1970                             + ", mSprIntrMiss: "
1971                             + mSprIntrMiss
1972                             + ", mPlcFillCount: "
1973                             + mPlcFillCount
1974                             + ", mPlcDiscardCount: "
1975                             + mPlcDiscardCount
1976                             + ", mMissedInstanceCount: "
1977                             + mMissedInstanceCount
1978                             + ", mTxRetransmitSlotCount: "
1979                             + mTxRetransmitSlotCount
1980                             + ",\n"
1981                             + "    mRxRetransmitSlotCount: "
1982                             + mRxRetransmitSlotCount
1983                             + ", mGoodRxFrameCount: "
1984                             + mGoodRxFrameCount
1985                             + "\n  }";
1986 
1987             return str;
1988         }
1989     }
1990 
1991     /**
1992      * This class provides the System APIs to access the Connect fail event.
1993      *
1994      * @hide
1995      */
1996     @SystemApi
1997     public static final class BqrConnectFail implements Parcelable {
1998         private static final String TAG = BluetoothQualityReport.TAG + ".BqrConnectFail";
1999 
2000         /**
2001          * Connect Fail reason: No error.
2002          *
2003          * @hide
2004          */
2005         @SystemApi public static final int CONNECT_FAIL_ID_NO_ERROR = 0x00;
2006 
2007         /**
2008          * Connect Fail reason: Page timeout.
2009          *
2010          * @hide
2011          */
2012         @SystemApi public static final int CONNECT_FAIL_ID_PAGE_TIMEOUT = 0x04;
2013 
2014         /**
2015          * Connect Fail reason: Connection timeout.
2016          *
2017          * @hide
2018          */
2019         @SystemApi public static final int CONNECT_FAIL_ID_CONNECTION_TIMEOUT = 0x08;
2020 
2021         /**
2022          * Connect Fail reason: ACL already exists.
2023          *
2024          * @hide
2025          */
2026         @SystemApi public static final int CONNECT_FAIL_ID_ACL_ALREADY_EXIST = 0x0b;
2027 
2028         /**
2029          * Connect Fail reason: Controller busy.
2030          *
2031          * @hide
2032          */
2033         @SystemApi public static final int CONNECT_FAIL_ID_CONTROLLER_BUSY = 0x3a;
2034 
2035         /** @hide */
2036         @Retention(RetentionPolicy.SOURCE)
2037         @IntDef(
2038                 prefix = {"CONNECT_FAIL_ID"},
2039                 value = {
2040                     CONNECT_FAIL_ID_NO_ERROR,
2041                     CONNECT_FAIL_ID_PAGE_TIMEOUT,
2042                     CONNECT_FAIL_ID_CONNECTION_TIMEOUT,
2043                     CONNECT_FAIL_ID_ACL_ALREADY_EXIST,
2044                     CONNECT_FAIL_ID_CONTROLLER_BUSY,
2045                 })
2046         public @interface ConnectFailId {}
2047 
2048         private int mFailReason;
2049 
BqrConnectFail(byte[] rawData, int offset)2050         private BqrConnectFail(byte[] rawData, int offset) {
2051             if (rawData == null || rawData.length <= offset) {
2052                 throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal.");
2053             }
2054 
2055             ByteBuffer bqrBuf =
2056                     ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer();
2057             bqrBuf.order(ByteOrder.LITTLE_ENDIAN);
2058 
2059             mFailReason = bqrBuf.get() & 0xFF;
2060         }
2061 
BqrConnectFail(Parcel in)2062         private BqrConnectFail(Parcel in) {
2063             mFailReason = in.readInt();
2064         }
2065 
2066         /**
2067          * Get the fail reason.
2068          *
2069          * @return the fail reason
2070          * @hide
2071          */
2072         @SystemApi
2073         @ConnectFailId
getFailReason()2074         public int getFailReason() {
2075             return mFailReason;
2076         }
2077 
2078         /**
2079          * Describe contents.
2080          *
2081          * @return 0
2082          * @hide
2083          */
describeContents()2084         public int describeContents() {
2085             return 0;
2086         }
2087 
2088         /**
2089          * Write BqrConnectFail to parcel.
2090          *
2091          * @hide
2092          */
2093         @SystemApi
2094         @Override
writeToParcel(@onNull Parcel dest, int flags)2095         public void writeToParcel(@NonNull Parcel dest, int flags) {
2096             dest.writeInt(mFailReason);
2097         }
2098 
2099         /** @hide */
2100         @SystemApi
2101         public static final @NonNull Parcelable.Creator<BqrConnectFail> CREATOR =
2102                 new Parcelable.Creator<BqrConnectFail>() {
2103                     public BqrConnectFail createFromParcel(Parcel in) {
2104                         return new BqrConnectFail(in);
2105                     }
2106 
2107                     public BqrConnectFail[] newArray(int size) {
2108                         return new BqrConnectFail[size];
2109                     }
2110                 };
2111 
2112         /**
2113          * Get the string of the Connect Fail ID.
2114          *
2115          * @param id the connect fail reason
2116          * @return the string of the id
2117          * @hide
2118          */
2119         @SystemApi
connectFailIdToString(@onnectFailId int id)2120         public static @NonNull String connectFailIdToString(@ConnectFailId int id) {
2121             switch (id) {
2122                 case CONNECT_FAIL_ID_NO_ERROR:
2123                     return "No error";
2124                 case CONNECT_FAIL_ID_PAGE_TIMEOUT:
2125                     return "Page Timeout";
2126                 case CONNECT_FAIL_ID_CONNECTION_TIMEOUT:
2127                     return "Connection Timeout";
2128                 case CONNECT_FAIL_ID_ACL_ALREADY_EXIST:
2129                     return "ACL already exists";
2130                 case CONNECT_FAIL_ID_CONTROLLER_BUSY:
2131                     return "Controller busy";
2132                 default:
2133                     return "INVALID";
2134             }
2135         }
2136 
2137         /** BqrConnectFail to String. */
2138         @Override
2139         @NonNull
toString()2140         public String toString() {
2141             String str;
2142             str =
2143                     "  BqrConnectFail: {\n"
2144                             + "    mFailReason: "
2145                             + connectFailIdToString(mFailReason)
2146                             + " ("
2147                             + String.format("0x%02X", mFailReason)
2148                             + ")"
2149                             + "\n  }";
2150 
2151             return str;
2152         }
2153     }
2154 }
2155