/* * Copyright (C) 2019 The Linux Foundation * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Objects; /** * This class provides the System APIs to access the data of BQR event reported from firmware side. * Currently it supports five event types: Quality monitor event, Approaching LSTO event, A2DP * choppy event, SCO choppy event and Connect fail event. To know which kind of event is wrapped in * this {@link BluetoothQualityReport} object, you need to call {@link #getQualityReportId}. * * * * @hide */ @SystemApi public final class BluetoothQualityReport implements Parcelable { private static final String TAG = BluetoothQualityReport.class.getSimpleName(); /** * Quality report ID: Monitor. * * @hide */ @SystemApi public static final int QUALITY_REPORT_ID_MONITOR = 0x01; /** * Quality report ID: Approaching LSTO. * * @hide */ @SystemApi public static final int QUALITY_REPORT_ID_APPROACH_LSTO = 0x02; /** * Quality report ID: A2DP choppy. * * @hide */ @SystemApi public static final int QUALITY_REPORT_ID_A2DP_CHOPPY = 0x03; /** * Quality report ID: SCO choppy. * * @hide */ @SystemApi public static final int QUALITY_REPORT_ID_SCO_CHOPPY = 0x04; /** * Quality report ID: Connect Fail. * * @hide */ @SystemApi public static final int QUALITY_REPORT_ID_CONN_FAIL = 0x08; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = {"QUALITY_REPORT_ID"}, value = { QUALITY_REPORT_ID_MONITOR, QUALITY_REPORT_ID_APPROACH_LSTO, QUALITY_REPORT_ID_A2DP_CHOPPY, QUALITY_REPORT_ID_SCO_CHOPPY, QUALITY_REPORT_ID_CONN_FAIL, }) public @interface QualityReportId {} private String mAddr; private int mLmpVer; private int mLmpSubVer; private int mManufacturerId; private String mName; private BluetoothClass mBluetoothClass; private BqrCommon mBqrCommon; private BqrVsLsto mBqrVsLsto; private BqrVsA2dpChoppy mBqrVsA2dpChoppy; private BqrVsScoChoppy mBqrVsScoChoppy; private BqrConnectFail mBqrConnectFail; enum PacketType { INVALID, TYPE_ID, TYPE_NULL, TYPE_POLL, TYPE_FHS, TYPE_HV1, TYPE_HV2, TYPE_HV3, TYPE_DV, TYPE_EV3, TYPE_EV4, TYPE_EV5, TYPE_2EV3, TYPE_2EV5, TYPE_3EV3, TYPE_3EV5, TYPE_DM1, TYPE_DH1, TYPE_DM3, TYPE_DH3, TYPE_DM5, TYPE_DH5, TYPE_AUX1, TYPE_2DH1, TYPE_2DH3, TYPE_2DH5, TYPE_3DH1, TYPE_3DH3, TYPE_3DH5; private static PacketType[] sAllValues = values(); static PacketType fromOrdinal(int n) { if (n < sAllValues.length) { return sAllValues[n]; } return INVALID; } } enum ConnState { CONN_IDLE(0x00), CONN_ACTIVE(0x81), CONN_HOLD(0x02), CONN_SNIFF_IDLE(0x03), CONN_SNIFF_ACTIVE(0x84), CONN_SNIFF_MASTER_TRANSITION(0x85), CONN_PARK(0x06), CONN_PARK_PEND(0x47), CONN_UNPARK_PEND(0x08), CONN_UNPARK_ACTIVE(0x89), CONN_DISCONNECT_PENDING(0x4A), CONN_PAGING(0x0B), CONN_PAGE_SCAN(0x0C), CONN_LOCAL_LOOPBACK(0x0D), CONN_LE_ACTIVE(0x0E), CONN_ANT_ACTIVE(0x0F), CONN_TRIGGER_SCAN(0x10), CONN_RECONNECTING(0x11), CONN_SEMI_CONN(0x12); private final int mValue; private static ConnState[] sAllStates = values(); ConnState(int val) { mValue = val; } public static String toString(int val) { for (ConnState state : sAllStates) { if (state.mValue == val) { return state.toString(); } } return "INVALID"; } } enum LinkQuality { ULTRA_HIGH, HIGH, STANDARD, MEDIUM, LOW, INVALID; private static LinkQuality[] sAllValues = values(); static LinkQuality fromOrdinal(int n) { if (n < sAllValues.length - 1) { return sAllValues[n]; } return INVALID; } } enum AirMode { uLaw, aLaw, CVSD, transparent_msbc, INVALID; private static AirMode[] sAllValues = values(); static AirMode fromOrdinal(int n) { if (n < sAllValues.length - 1) { return sAllValues[n]; } return INVALID; } } private BluetoothQualityReport( String remoteAddr, int lmpVer, int lmpSubVer, int manufacturerId, String remoteName, BluetoothClass bluetoothClass, byte[] rawData) { mAddr = remoteAddr; mLmpVer = lmpVer; mLmpSubVer = lmpSubVer; mManufacturerId = manufacturerId; mName = remoteName; mBluetoothClass = bluetoothClass; mBqrCommon = new BqrCommon(rawData, 0); int id = mBqrCommon.getQualityReportId(); if (id == QUALITY_REPORT_ID_MONITOR) return; int vsPartOffset = BqrCommon.BQR_COMMON_LEN; if (id == QUALITY_REPORT_ID_APPROACH_LSTO) { mBqrVsLsto = new BqrVsLsto(rawData, vsPartOffset); } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) { mBqrVsA2dpChoppy = new BqrVsA2dpChoppy(rawData, vsPartOffset); } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) { mBqrVsScoChoppy = new BqrVsScoChoppy(rawData, vsPartOffset); } else if (id == QUALITY_REPORT_ID_CONN_FAIL) { mBqrConnectFail = new BqrConnectFail(rawData, vsPartOffset); } else { throw new IllegalArgumentException(TAG + ": unknown quality report id:" + id); } } private BluetoothQualityReport(Parcel in) { mAddr = in.readString(); mLmpVer = in.readInt(); mLmpSubVer = in.readInt(); mManufacturerId = in.readInt(); mName = in.readString(); mBluetoothClass = new BluetoothClass(in.readInt()); mBqrCommon = new BqrCommon(in); int id = mBqrCommon.getQualityReportId(); if (id == QUALITY_REPORT_ID_APPROACH_LSTO) { mBqrVsLsto = new BqrVsLsto(in); } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) { mBqrVsA2dpChoppy = new BqrVsA2dpChoppy(in); } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) { mBqrVsScoChoppy = new BqrVsScoChoppy(in); } else if (id == QUALITY_REPORT_ID_CONN_FAIL) { mBqrConnectFail = new BqrConnectFail(in); } } /** * Get the quality report id. * * @hide */ @SystemApi @QualityReportId public int getQualityReportId() { return mBqrCommon.getQualityReportId(); } /** * Get the string of the quality report id. * * @return the string of the id * @hide */ @SystemApi public static @NonNull String qualityReportIdToString(@QualityReportId int id) { return BqrCommon.qualityReportIdToString(id); } /** * Get bluetooth address of remote device in this report. * * @return bluetooth address of remote device * @hide */ @SystemApi public @Nullable String getRemoteAddress() { return mAddr; } /** * Get LMP version of remote device in this report. * * @return LMP version of remote device * @hide */ @SystemApi public int getLmpVersion() { return mLmpVer; } /** * Get LMP subVersion of remote device in this report. * * @return LMP subVersion of remote device * @hide */ @SystemApi public int getLmpSubVersion() { return mLmpSubVer; } /** * Get manufacturer id of remote device in this report. * * @return manufacturer id of remote device * @hide */ @SystemApi public int getManufacturerId() { return mManufacturerId; } /** * Get the name of remote device in this report. * * @return the name of remote device * @hide */ @SystemApi public @Nullable String getRemoteName() { return mName; } /** * Get the class of remote device in this report. * * @return the class of remote device * @hide */ @SystemApi public @Nullable BluetoothClass getBluetoothClass() { return mBluetoothClass; } /** * Get the {@link BluetoothQualityReport.BqrCommon} object. * * @return the {@link BluetoothQualityReport.BqrCommon} object. * @hide */ @SystemApi public @Nullable BqrCommon getBqrCommon() { return mBqrCommon; } /** * Get the event data object based on current Quality Report Id. If the report id is {@link * #QUALITY_REPORT_ID_MONITOR}, this returns a {@link BluetoothQualityReport.BqrCommon} object. * If the report id is {@link #QUALITY_REPORT_ID_APPROACH_LSTO}, this returns a {@link * BluetoothQualityReport.BqrVsLsto} object. If the report id is {@link * #QUALITY_REPORT_ID_A2DP_CHOPPY}, this returns a {@link * BluetoothQualityReport.BqrVsA2dpChoppy} object. If the report id is {@link * #QUALITY_REPORT_ID_SCO_CHOPPY}, this returns a {@link BluetoothQualityReport.BqrVsScoChoppy} * object. If the report id is {@link #QUALITY_REPORT_ID_CONN_FAIL}, this returns a {@link * BluetoothQualityReport.BqrConnectFail} object. If the report id is none of the above, this * returns {@code null}. * * @return the event data object based on the quality report id * @hide */ @SystemApi public @Nullable Parcelable getBqrEvent() { if (mBqrCommon == null) { return null; } switch (mBqrCommon.getQualityReportId()) { case QUALITY_REPORT_ID_MONITOR: return mBqrCommon; case QUALITY_REPORT_ID_APPROACH_LSTO: return mBqrVsLsto; case QUALITY_REPORT_ID_A2DP_CHOPPY: return mBqrVsA2dpChoppy; case QUALITY_REPORT_ID_SCO_CHOPPY: return mBqrVsScoChoppy; case QUALITY_REPORT_ID_CONN_FAIL: return mBqrConnectFail; default: return null; } } /** @hide */ @SystemApi public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothQualityReport createFromParcel(Parcel in) { return new BluetoothQualityReport(in); } public BluetoothQualityReport[] newArray(int size) { return new BluetoothQualityReport[size]; } }; /** * Describe contents. * * @return 0 * @hide */ public int describeContents() { return 0; } /** * Write BluetoothQualityReport to parcel. * * @hide */ @SystemApi @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeString(mAddr); out.writeInt(mLmpVer); out.writeInt(mLmpSubVer); out.writeInt(mManufacturerId); out.writeString(mName); out.writeInt(mBluetoothClass.getClassOfDevice()); mBqrCommon.writeToParcel(out, flags); int id = mBqrCommon.getQualityReportId(); if (id == QUALITY_REPORT_ID_APPROACH_LSTO) { mBqrVsLsto.writeToParcel(out, flags); } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) { mBqrVsA2dpChoppy.writeToParcel(out, flags); } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) { mBqrVsScoChoppy.writeToParcel(out, flags); } else if (id == QUALITY_REPORT_ID_CONN_FAIL) { mBqrConnectFail.writeToParcel(out, flags); } } /** BluetoothQualityReport to String. */ @Override @NonNull public String toString() { String str; str = "BQR: {\n" + " mAddr: " + mAddr + ", mLmpVer: " + String.format("0x%02X", mLmpVer) + ", mLmpSubVer: " + String.format("0x%04X", mLmpSubVer) + ", mManufacturerId: " + String.format("0x%04X", mManufacturerId) + ", mName: " + mName + ", mBluetoothClass: " + mBluetoothClass.toString() + ",\n" + mBqrCommon + "\n"; int id = mBqrCommon.getQualityReportId(); if (id == QUALITY_REPORT_ID_APPROACH_LSTO) { str += mBqrVsLsto + "\n}"; } else if (id == QUALITY_REPORT_ID_A2DP_CHOPPY) { str += mBqrVsA2dpChoppy + "\n}"; } else if (id == QUALITY_REPORT_ID_SCO_CHOPPY) { str += mBqrVsScoChoppy + "\n}"; } else if (id == QUALITY_REPORT_ID_CONN_FAIL) { str += mBqrConnectFail + "\n}"; } else if (id == QUALITY_REPORT_ID_MONITOR) { str += "}"; } return str; } /** * Builder for new instances of {@link BluetoothQualityReport}. * * @hide */ @SystemApi public static final class Builder { private String remoteAddr = "00:00:00:00:00:00"; private int lmpVer; private int lmpSubVer; private int manufacturerId; private String remoteName = ""; private BluetoothClass bluetoothClass = new BluetoothClass(0); private byte[] rawData; /** * Creates a new instance of {@link Builder}. * * @return The new instance * @throws NullPointerException if rawData is null * @hide */ @SystemApi public Builder(@NonNull byte[] rawData) { this.rawData = Objects.requireNonNull(rawData); } /** * Sets the Remote Device Address (big-endian) attribute for the new instance of {@link * BluetoothQualityReport}. * * @param remoteAddr the Remote Device Address (big-endian) attribute * @hide */ @NonNull @SystemApi public Builder setRemoteAddress(@Nullable String remoteAddr) { if (!BluetoothAdapter.checkBluetoothAddress(remoteAddr)) { Log.d(TAG, "remote address is not a valid bluetooth address: " + remoteAddr); } else { this.remoteAddr = remoteAddr; } return this; } /** * Sets the Link Manager Protocol Version attribute for the new instance of {@link * BluetoothQualityReport}. * * @param lmpVer the Link Manager Protocol Version attribute * @hide */ @NonNull @SystemApi public Builder setLmpVersion(int lmpVer) { this.lmpVer = lmpVer; return this; } /** * Sets the Link Manager Protocol SubVersion attribute for the new instance of {@link * BluetoothQualityReport}. * * @param lmpSubVer the Link Manager Protocol SubVersion attribute * @hide */ @NonNull @SystemApi public Builder setLmpSubVersion(int lmpSubVer) { this.lmpSubVer = lmpSubVer; return this; } /** * Sets the Manufacturer Id attribute for the new instance of {@link * BluetoothQualityReport}. * * @param manufacturerId the Manufacturer Id attribute * @hide */ @NonNull @SystemApi public Builder setManufacturerId(int manufacturerId) { this.manufacturerId = manufacturerId; return this; } /** * Sets the Remote Device Name attribute for the new instance of {@link * BluetoothQualityReport}. * * @param remoteName the Remote Device Name attribute * @hide */ @NonNull @SystemApi public Builder setRemoteName(@Nullable String remoteName) { if (remoteName == null) { Log.d(TAG, "remote name is null"); } else { this.remoteName = remoteName; } return this; } /** * Sets the Bluetooth Class of Remote Device attribute for the new instance of {@link * BluetoothQualityReport}. * * @param bluetoothClass the Remote Class of Device attribute * @hide */ @NonNull @SystemApi public Builder setBluetoothClass(@Nullable BluetoothClass bluetoothClass) { if (bluetoothClass == null) { Log.d(TAG, "remote bluetooth class is null"); } else { this.bluetoothClass = bluetoothClass; } return this; } /** * Creates a new instance of {@link BluetoothQualityReport}. * * @return The new instance * @throws IllegalArgumentException Unsupported Quality Report Id or invalid raw data * @hide */ @NonNull @SystemApi public BluetoothQualityReport build() { return new BluetoothQualityReport( remoteAddr, lmpVer, lmpSubVer, manufacturerId, remoteName, bluetoothClass, rawData); } } /** * This class provides the System APIs to access the common part of BQR event. * * @hide */ @SystemApi public static final class BqrCommon implements Parcelable { private static final String TAG = BluetoothQualityReport.TAG + ".BqrCommon"; static final int BQR_COMMON_LEN = 55; private int mQualityReportId; private int mPacketType; private int mConnectionHandle; private int mConnectionRole; private int mTxPowerLevel; private int mRssi; private int mSnr; private int mUnusedAfhChannelCount; private int mAfhSelectUnidealChannelCount; private int mLsto; private long mPiconetClock; private long mRetransmissionCount; private long mNoRxCount; private long mNakCount; private long mLastTxAckTimestamp; private long mFlowOffCount; private long mLastFlowOnTimestamp; private long mOverflowCount; private long mUnderflowCount; private String mAddr; private int mCalFailedItemCount; private BqrCommon(byte[] rawData, int offset) { if (rawData == null || rawData.length < offset + BQR_COMMON_LEN) { throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal."); } ByteBuffer bqrBuf = ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer(); bqrBuf.order(ByteOrder.LITTLE_ENDIAN); mQualityReportId = bqrBuf.get() & 0xFF; mPacketType = bqrBuf.get() & 0xFF; mConnectionHandle = bqrBuf.getShort() & 0xFFFF; mConnectionRole = bqrBuf.get() & 0xFF; mTxPowerLevel = bqrBuf.get() & 0xFF; mRssi = bqrBuf.get(); mSnr = bqrBuf.get(); mUnusedAfhChannelCount = bqrBuf.get() & 0xFF; mAfhSelectUnidealChannelCount = bqrBuf.get() & 0xFF; mLsto = bqrBuf.getShort() & 0xFFFF; mPiconetClock = bqrBuf.getInt() & 0xFFFFFFFFL; mRetransmissionCount = bqrBuf.getInt() & 0xFFFFFFFFL; mNoRxCount = bqrBuf.getInt() & 0xFFFFFFFFL; mNakCount = bqrBuf.getInt() & 0xFFFFFFFFL; mLastTxAckTimestamp = bqrBuf.getInt() & 0xFFFFFFFFL; mFlowOffCount = bqrBuf.getInt() & 0xFFFFFFFFL; mLastFlowOnTimestamp = bqrBuf.getInt() & 0xFFFFFFFFL; mOverflowCount = bqrBuf.getInt() & 0xFFFFFFFFL; mUnderflowCount = bqrBuf.getInt() & 0xFFFFFFFFL; int currentOffset = bqrBuf.position(); mAddr = String.format( "%02X:%02X:%02X:%02X:%02X:%02X", bqrBuf.get(currentOffset + 5), bqrBuf.get(currentOffset + 4), bqrBuf.get(currentOffset + 3), bqrBuf.get(currentOffset + 2), bqrBuf.get(currentOffset + 1), bqrBuf.get(currentOffset + 0)); bqrBuf.position(currentOffset + 6); mCalFailedItemCount = bqrBuf.get() & 0xFF; } private BqrCommon(Parcel in) { mQualityReportId = in.readInt(); mPacketType = in.readInt(); mConnectionHandle = in.readInt(); mConnectionRole = in.readInt(); mTxPowerLevel = in.readInt(); mRssi = in.readInt(); mSnr = in.readInt(); mUnusedAfhChannelCount = in.readInt(); mAfhSelectUnidealChannelCount = in.readInt(); mLsto = in.readInt(); mPiconetClock = in.readLong(); mRetransmissionCount = in.readLong(); mNoRxCount = in.readLong(); mNakCount = in.readLong(); mLastTxAckTimestamp = in.readLong(); mFlowOffCount = in.readLong(); mLastFlowOnTimestamp = in.readLong(); mOverflowCount = in.readLong(); mUnderflowCount = in.readLong(); mAddr = in.readString(); mCalFailedItemCount = in.readInt(); } int getQualityReportId() { return mQualityReportId; } static String qualityReportIdToString(@QualityReportId int id) { switch (id) { case QUALITY_REPORT_ID_MONITOR: return "Quality monitor"; case QUALITY_REPORT_ID_APPROACH_LSTO: return "Approaching LSTO"; case QUALITY_REPORT_ID_A2DP_CHOPPY: return "A2DP choppy"; case QUALITY_REPORT_ID_SCO_CHOPPY: return "SCO choppy"; case QUALITY_REPORT_ID_CONN_FAIL: return "Connect fail"; default: return "INVALID"; } } /** * Get the packet type of the connection. * * @return the packet type * @hide */ @SystemApi public int getPacketType() { return mPacketType; } /** * Get the string of packet type. * * @param packetType packet type of the connection * @return the string of packet type * @hide */ @SystemApi public static @Nullable String packetTypeToString(int packetType) { PacketType type = PacketType.fromOrdinal(packetType); return type.toString(); } /** * Get the connection handle of the connection. * * @return the connection handle * @hide */ @SystemApi public int getConnectionHandle() { return mConnectionHandle; } /** * Connection role: central. * * @hide */ @SystemApi public static final int CONNECTION_ROLE_CENTRAL = 0; /** * Connection role: peripheral. * * @hide */ @SystemApi public static final int CONNECTION_ROLE_PERIPHERAL = 1; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = {"CONNECTION_ROLE"}, value = { CONNECTION_ROLE_CENTRAL, CONNECTION_ROLE_PERIPHERAL, }) public @interface ConnectionRole {} /** * Get the connection Role of the connection. * * @return the connection Role * @hide */ @SystemApi @ConnectionRole public int getConnectionRole() { return mConnectionRole; } /** * Get the connection Role of the connection, "Central" or "Peripheral". * * @param connectionRole connection Role of the connection * @return the connection Role String * @hide */ @SystemApi public static @NonNull String connectionRoleToString(int connectionRole) { if (connectionRole == CONNECTION_ROLE_CENTRAL) { return "Central"; } else if (connectionRole == CONNECTION_ROLE_PERIPHERAL) { return "Peripheral"; } else { return "INVALID:" + connectionRole; } } /** * Get the current transmit power level for the connection. * * @return the TX power level * @hide */ @SystemApi public int getTxPowerLevel() { return mTxPowerLevel; } /** * Get the Received Signal Strength Indication (RSSI) value for the connection. * * @return the RSSI * @hide */ @SystemApi public int getRssi() { return mRssi; } /** * Get the Signal-to-Noise Ratio (SNR) value for the connection. * * @return the SNR * @hide */ @SystemApi public int getSnr() { return mSnr; } /** * Get the number of unused channels in AFH_channel_map. * * @return the number of unused channels * @hide */ @SystemApi public int getUnusedAfhChannelCount() { return mUnusedAfhChannelCount; } /** * Get the number of the channels which are interfered and quality is bad but are still * selected for AFH. * * @return the number of the selected unideal channels * @hide */ @SystemApi public int getAfhSelectUnidealChannelCount() { return mAfhSelectUnidealChannelCount; } /** * Get the current link supervision timeout setting. time_ms: N * 0.625 ms (1 slot). * * @return link supervision timeout value * @hide */ @SystemApi public int getLsto() { return mLsto; } /** * Get the piconet clock for the specified Connection_Handle. time_ms: N * 0.3125 ms (1 * Bluetooth Clock). * * @return the piconet clock * @hide */ @SystemApi public long getPiconetClock() { return mPiconetClock; } /** * Get the count of retransmission. * * @return the count of retransmission * @hide */ @SystemApi public long getRetransmissionCount() { return mRetransmissionCount; } /** * Get the count of no RX. * * @return the count of no RX * @hide */ @SystemApi public long getNoRxCount() { return mNoRxCount; } /** * Get the count of NAK(Negative Acknowledge). * * @return the count of NAK * @hide */ @SystemApi public long getNakCount() { return mNakCount; } /** * Get the timestamp of last TX ACK. time_ms: N * 0.3125 ms (1 Bluetooth Clock). * * @return the timestamp of last TX ACK * @hide */ @SystemApi public long getLastTxAckTimestamp() { return mLastTxAckTimestamp; } /** * Get the count of flow-off. * * @return the count of flow-off * @hide */ @SystemApi public long getFlowOffCount() { return mFlowOffCount; } /** * Get the timestamp of last flow-on. * * @return the timestamp of last flow-on * @hide */ @SystemApi public long getLastFlowOnTimestamp() { return mLastFlowOnTimestamp; } /** * Get the buffer overflow count (how many bytes of TX data are dropped) since the last * event. * * @return the buffer overflow count * @hide */ @SystemApi public long getOverflowCount() { return mOverflowCount; } /** * Get the buffer underflow count (in byte). * * @return the buffer underflow count * @hide */ @SystemApi public long getUnderflowCount() { return mUnderflowCount; } /** * Get the count of calibration failed items. * * @return the count of calibration failure * @hide */ @SystemApi public int getCalFailedItemCount() { return mCalFailedItemCount; } /** * Describe contents. * * @return 0 * @hide */ public int describeContents() { return 0; } /** * Write BqrCommon to parcel. * * @hide */ @SystemApi @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mQualityReportId); dest.writeInt(mPacketType); dest.writeInt(mConnectionHandle); dest.writeInt(mConnectionRole); dest.writeInt(mTxPowerLevel); dest.writeInt(mRssi); dest.writeInt(mSnr); dest.writeInt(mUnusedAfhChannelCount); dest.writeInt(mAfhSelectUnidealChannelCount); dest.writeInt(mLsto); dest.writeLong(mPiconetClock); dest.writeLong(mRetransmissionCount); dest.writeLong(mNoRxCount); dest.writeLong(mNakCount); dest.writeLong(mLastTxAckTimestamp); dest.writeLong(mFlowOffCount); dest.writeLong(mLastFlowOnTimestamp); dest.writeLong(mOverflowCount); dest.writeLong(mUnderflowCount); dest.writeString(mAddr); dest.writeInt(mCalFailedItemCount); } /** @hide */ @SystemApi public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BqrCommon createFromParcel(Parcel in) { return new BqrCommon(in); } public BqrCommon[] newArray(int size) { return new BqrCommon[size]; } }; /** BqrCommon to String. */ @Override @NonNull public String toString() { String str; str = " BqrCommon: {\n" + " mQualityReportId: " + qualityReportIdToString(getQualityReportId()) + "(" + String.format("0x%02X", mQualityReportId) + ")" + ", mPacketType: " + packetTypeToString(mPacketType) + "(" + String.format("0x%02X", mPacketType) + ")" + ", mConnectionHandle: " + String.format("0x%04X", mConnectionHandle) + ", mConnectionRole: " + getConnectionRole() + "(" + mConnectionRole + ")" + ", mTxPowerLevel: " + mTxPowerLevel + ", mRssi: " + mRssi + ", mSnr: " + mSnr + ", mUnusedAfhChannelCount: " + mUnusedAfhChannelCount + ",\n" + " mAfhSelectUnidealChannelCount: " + mAfhSelectUnidealChannelCount + ", mLsto: " + mLsto + ", mPiconetClock: " + String.format("0x%08X", mPiconetClock) + ", mRetransmissionCount: " + mRetransmissionCount + ", mNoRxCount: " + mNoRxCount + ", mNakCount: " + mNakCount + ", mLastTxAckTimestamp: " + String.format("0x%08X", mLastTxAckTimestamp) + ", mFlowOffCount: " + mFlowOffCount + ",\n" + " mLastFlowOnTimestamp: " + String.format("0x%08X", mLastFlowOnTimestamp) + ", mOverflowCount: " + mOverflowCount + ", mUnderflowCount: " + mUnderflowCount + ", mAddr: " + mAddr + ", mCalFailedItemCount: " + mCalFailedItemCount + "\n }"; return str; } } /** * This class provides the System APIs to access the vendor specific part of Approaching LSTO * event. * * @hide */ @SystemApi public static final class BqrVsLsto implements Parcelable { private static final String TAG = BluetoothQualityReport.TAG + ".BqrVsLsto"; private int mConnState; private long mBasebandStats; private long mSlotsUsed; private int mCxmDenials; private int mTxSkipped; private int mRfLoss; private long mNativeClock; private long mLastTxAckTimestamp; private BqrVsLsto(byte[] rawData, int offset) { if (rawData == null || rawData.length <= offset) { throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal."); } ByteBuffer bqrBuf = ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer(); bqrBuf.order(ByteOrder.LITTLE_ENDIAN); mConnState = bqrBuf.get() & 0xFF; mBasebandStats = bqrBuf.getInt() & 0xFFFFFFFFL; mSlotsUsed = bqrBuf.getInt() & 0xFFFFFFFFL; mCxmDenials = bqrBuf.getShort() & 0xFFFF; mTxSkipped = bqrBuf.getShort() & 0xFFFF; mRfLoss = bqrBuf.getShort() & 0xFFFF; mNativeClock = bqrBuf.getInt() & 0xFFFFFFFFL; mLastTxAckTimestamp = bqrBuf.getInt() & 0xFFFFFFFFL; } private BqrVsLsto(Parcel in) { mConnState = in.readInt(); mBasebandStats = in.readLong(); mSlotsUsed = in.readLong(); mCxmDenials = in.readInt(); mTxSkipped = in.readInt(); mRfLoss = in.readInt(); mNativeClock = in.readLong(); mLastTxAckTimestamp = in.readLong(); } /** * Get the conn state of sco. * * @return the conn state * @hide */ @SystemApi public int getConnState() { return mConnState; } /** * Get the string of conn state of sco. * * @param connectionState connection state of sco * @return the string of conn state * @hide */ @SystemApi public static @Nullable String connStateToString(int connectionState) { return ConnState.toString(connectionState); } /** * Get the baseband statistics. * * @return the baseband statistics * @hide */ @SystemApi public long getBasebandStats() { return mBasebandStats; } /** * Get the count of slots allocated for current connection. * * @return the count of slots allocated for current connection * @hide */ @SystemApi public long getSlotsUsed() { return mSlotsUsed; } /** * Get the count of Coex denials. * * @return the count of CXM denials * @hide */ @SystemApi public int getCxmDenials() { return mCxmDenials; } /** * Get the count of TX skipped when no poll from remote device. * * @return the count of TX skipped * @hide */ @SystemApi public int getTxSkipped() { return mTxSkipped; } /** * Get the count of RF loss. * * @return the count of RF loss * @hide */ @SystemApi public int getRfLoss() { return mRfLoss; } /** * Get the timestamp when issue happened. time_ms: N * 0.3125 ms (1 Bluetooth Clock). * * @return the timestamp when issue happened * @hide */ @SystemApi public long getNativeClock() { return mNativeClock; } /** * Get the timestamp of last TX ACK. time_ms: N * 0.3125 ms (1 Bluetooth Clock). * * @return the timestamp of last TX ACK * @hide */ @SystemApi public long getLastTxAckTimestamp() { return mLastTxAckTimestamp; } /** * Describe contents. * * @return 0 * @hide */ public int describeContents() { return 0; } /** * Write BqrVsLsto to parcel. * * @hide */ @SystemApi @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mConnState); dest.writeLong(mBasebandStats); dest.writeLong(mSlotsUsed); dest.writeInt(mCxmDenials); dest.writeInt(mTxSkipped); dest.writeInt(mRfLoss); dest.writeLong(mNativeClock); dest.writeLong(mLastTxAckTimestamp); } /** @hide */ @SystemApi public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BqrVsLsto createFromParcel(Parcel in) { return new BqrVsLsto(in); } public BqrVsLsto[] newArray(int size) { return new BqrVsLsto[size]; } }; /** BqrVsLsto to String. */ @Override @NonNull public String toString() { String str; str = " BqrVsLsto: {\n" + " mConnState: " + connStateToString(getConnState()) + "(" + String.format("0x%02X", mConnState) + ")" + ", mBasebandStats: " + String.format("0x%08X", mBasebandStats) + ", mSlotsUsed: " + mSlotsUsed + ", mCxmDenials: " + mCxmDenials + ", mTxSkipped: " + mTxSkipped + ", mRfLoss: " + mRfLoss + ", mNativeClock: " + String.format("0x%08X", mNativeClock) + ", mLastTxAckTimestamp: " + String.format("0x%08X", mLastTxAckTimestamp) + "\n }"; return str; } } /** * This class provides the System APIs to access the vendor specific part of A2dp choppy event. * * @hide */ @SystemApi public static final class BqrVsA2dpChoppy implements Parcelable { private static final String TAG = BluetoothQualityReport.TAG + ".BqrVsA2dpChoppy"; private long mArrivalTime; private long mScheduleTime; private int mGlitchCount; private int mTxCxmDenials; private int mRxCxmDenials; private int mAclTxQueueLength; private int mLinkQuality; private BqrVsA2dpChoppy(byte[] rawData, int offset) { if (rawData == null || rawData.length <= offset) { throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal."); } ByteBuffer bqrBuf = ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer(); bqrBuf.order(ByteOrder.LITTLE_ENDIAN); mArrivalTime = bqrBuf.getInt() & 0xFFFFFFFFL; mScheduleTime = bqrBuf.getInt() & 0xFFFFFFFFL; mGlitchCount = bqrBuf.getShort() & 0xFFFF; mTxCxmDenials = bqrBuf.getShort() & 0xFFFF; mRxCxmDenials = bqrBuf.getShort() & 0xFFFF; mAclTxQueueLength = bqrBuf.get() & 0xFF; mLinkQuality = bqrBuf.get() & 0xFF; } private BqrVsA2dpChoppy(Parcel in) { mArrivalTime = in.readLong(); mScheduleTime = in.readLong(); mGlitchCount = in.readInt(); mTxCxmDenials = in.readInt(); mRxCxmDenials = in.readInt(); mAclTxQueueLength = in.readInt(); mLinkQuality = in.readInt(); } /** * Get the timestamp of a2dp packet arrived. time_ms: N * 0.3125 ms (1 Bluetooth Clock). * * @return the timestamp of a2dp packet arrived * @hide */ @SystemApi public long getArrivalTime() { return mArrivalTime; } /** * Get the timestamp of a2dp packet scheduled. time_ms: N * 0.3125 ms (1 Bluetooth Clock). * * @return the timestamp of a2dp packet scheduled * @hide */ @SystemApi public long getScheduleTime() { return mScheduleTime; } /** * Get the a2dp glitch count since the last event. * * @return the a2dp glitch count * @hide */ @SystemApi public int getGlitchCount() { return mGlitchCount; } /** * Get the count of Coex TX denials. * * @return the count of Coex TX denials * @hide */ @SystemApi public int getTxCxmDenials() { return mTxCxmDenials; } /** * Get the count of Coex RX denials. * * @return the count of Coex RX denials * @hide */ @SystemApi public int getRxCxmDenials() { return mRxCxmDenials; } /** * Get the ACL queue length which are pending TX in FW. * * @return the ACL queue length * @hide */ @SystemApi public int getAclTxQueueLength() { return mAclTxQueueLength; } /** * Get the link quality for the current connection. * * @return the link quality * @hide */ @SystemApi public int getLinkQuality() { return mLinkQuality; } /** * Get the string of link quality for the current connection. * * @param linkQuality link quality for the current connection * @return the string of link quality * @hide */ @SystemApi public static @Nullable String linkQualityToString(int linkQuality) { LinkQuality q = LinkQuality.fromOrdinal(linkQuality); return q.toString(); } /** * Describe contents. * * @return 0 * @hide */ public int describeContents() { return 0; } /** * Write BqrVsA2dpChoppy to parcel. * * @hide */ @SystemApi @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mArrivalTime); dest.writeLong(mScheduleTime); dest.writeInt(mGlitchCount); dest.writeInt(mTxCxmDenials); dest.writeInt(mRxCxmDenials); dest.writeInt(mAclTxQueueLength); dest.writeInt(mLinkQuality); } /** @hide */ @SystemApi public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BqrVsA2dpChoppy createFromParcel(Parcel in) { return new BqrVsA2dpChoppy(in); } public BqrVsA2dpChoppy[] newArray(int size) { return new BqrVsA2dpChoppy[size]; } }; /** BqrVsA2dpChoppy to String. */ @Override @NonNull public String toString() { String str; str = " BqrVsA2dpChoppy: {\n" + " mArrivalTime: " + String.format("0x%08X", mArrivalTime) + ", mScheduleTime: " + String.format("0x%08X", mScheduleTime) + ", mGlitchCount: " + mGlitchCount + ", mTxCxmDenials: " + mTxCxmDenials + ", mRxCxmDenials: " + mRxCxmDenials + ", mAclTxQueueLength: " + mAclTxQueueLength + ", mLinkQuality: " + linkQualityToString(mLinkQuality) + "(" + String.format("0x%02X", mLinkQuality) + ")" + "\n }"; return str; } } /** * This class provides the System APIs to access the vendor specific part of SCO choppy event. * * @hide */ @SystemApi public static final class BqrVsScoChoppy implements Parcelable { private static final String TAG = BluetoothQualityReport.TAG + ".BqrVsScoChoppy"; private int mGlitchCount; private int mIntervalEsco; private int mWindowEsco; private int mAirFormat; private int mInstanceCount; private int mTxCxmDenials; private int mRxCxmDenials; private int mTxAbortCount; private int mLateDispatch; private int mMicIntrMiss; private int mLpaIntrMiss; private int mSprIntrMiss; private int mPlcFillCount; private int mPlcDiscardCount; private int mMissedInstanceCount; private int mTxRetransmitSlotCount; private int mRxRetransmitSlotCount; private int mGoodRxFrameCount; private BqrVsScoChoppy(byte[] rawData, int offset) { if (rawData == null || rawData.length <= offset) { throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal."); } ByteBuffer bqrBuf = ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer(); bqrBuf.order(ByteOrder.LITTLE_ENDIAN); mGlitchCount = bqrBuf.getShort() & 0xFFFF; mIntervalEsco = bqrBuf.get() & 0xFF; mWindowEsco = bqrBuf.get() & 0xFF; mAirFormat = bqrBuf.get() & 0xFF; mInstanceCount = bqrBuf.getShort() & 0xFFFF; mTxCxmDenials = bqrBuf.getShort() & 0xFFFF; mRxCxmDenials = bqrBuf.getShort() & 0xFFFF; mTxAbortCount = bqrBuf.getShort() & 0xFFFF; mLateDispatch = bqrBuf.getShort() & 0xFFFF; mMicIntrMiss = bqrBuf.getShort() & 0xFFFF; mLpaIntrMiss = bqrBuf.getShort() & 0xFFFF; mSprIntrMiss = bqrBuf.getShort() & 0xFFFF; mPlcFillCount = bqrBuf.getShort() & 0xFFFF; mPlcDiscardCount = bqrBuf.getShort() & 0xFFFF; mMissedInstanceCount = bqrBuf.getShort() & 0xFFFF; mTxRetransmitSlotCount = bqrBuf.getShort() & 0xFFFF; mRxRetransmitSlotCount = bqrBuf.getShort() & 0xFFFF; mGoodRxFrameCount = bqrBuf.getShort() & 0xFFFF; } private BqrVsScoChoppy(Parcel in) { mGlitchCount = in.readInt(); mIntervalEsco = in.readInt(); mWindowEsco = in.readInt(); mAirFormat = in.readInt(); mInstanceCount = in.readInt(); mTxCxmDenials = in.readInt(); mRxCxmDenials = in.readInt(); mTxAbortCount = in.readInt(); mLateDispatch = in.readInt(); mMicIntrMiss = in.readInt(); mLpaIntrMiss = in.readInt(); mSprIntrMiss = in.readInt(); mPlcFillCount = in.readInt(); mPlcDiscardCount = in.readInt(); mMissedInstanceCount = in.readInt(); mTxRetransmitSlotCount = in.readInt(); mRxRetransmitSlotCount = in.readInt(); mGoodRxFrameCount = in.readInt(); } /** * Get the sco glitch count since the last event. * * @return the sco glitch count * @hide */ @SystemApi public int getGlitchCount() { return mGlitchCount; } /** * Get ESCO interval in slots. It is the value of Transmission_Interval parameter in * Synchronous Connection Complete event. * * @return ESCO interval in slots * @hide */ @SystemApi public int getIntervalEsco() { return mIntervalEsco; } /** * Get ESCO window in slots. It is the value of Retransmission Window parameter in * Synchronous Connection Complete event. * * @return ESCO window in slots * @hide */ @SystemApi public int getWindowEsco() { return mWindowEsco; } /** * Get the air mode. It is the value of Air Mode parameter in Synchronous Connection * Complete event. * * @return the air mode * @hide */ @SystemApi public int getAirFormat() { return mAirFormat; } /** * Get the string of air mode. * * @param airFormat the value of Air Mode parameter in Synchronous Connection Complete event * @return the string of air mode * @hide */ @SystemApi public static @Nullable String airFormatToString(int airFormat) { AirMode m = AirMode.fromOrdinal(airFormat); return m.toString(); } /** * Get the xSCO instance count. * * @return the xSCO instance count * @hide */ @SystemApi public int getInstanceCount() { return mInstanceCount; } /** * Get the count of Coex TX denials. * * @return the count of Coex TX denials * @hide */ @SystemApi public int getTxCxmDenials() { return mTxCxmDenials; } /** * Get the count of Coex RX denials. * * @return the count of Coex RX denials * @hide */ @SystemApi public int getRxCxmDenials() { return mRxCxmDenials; } /** * Get the count of sco packets aborted. * * @return the count of sco packets aborted * @hide */ @SystemApi public int getTxAbortCount() { return mTxAbortCount; } /** * Get the count of sco packets dispatched late. * * @return the count of sco packets dispatched late * @hide */ @SystemApi public int getLateDispatch() { return mLateDispatch; } /** * Get the count of missed Mic interrupts. * * @return the count of missed Mic interrupts * @hide */ @SystemApi public int getMicIntrMiss() { return mMicIntrMiss; } /** * Get the count of missed LPA interrupts. * * @return the count of missed LPA interrupts * @hide */ @SystemApi public int getLpaIntrMiss() { return mLpaIntrMiss; } /** * Get the count of missed Speaker interrupts. * * @return the count of missed Speaker interrupts * @hide */ @SystemApi public int getSprIntrMiss() { return mSprIntrMiss; } /** * Get the count of packet loss concealment filled. * * @return the count of packet loss concealment filled * @hide */ @SystemApi public int getPlcFillCount() { return mPlcFillCount; } /** * Get the count of packet loss concealment discarded. * * @return the count of packet loss concealment discarded * @hide */ @SystemApi public int getPlcDiscardCount() { return mPlcDiscardCount; } /** * Get the count of sco instances missed. * * @return the count of sco instances missed * @hide */ @SystemApi public int getMissedInstanceCount() { return mMissedInstanceCount; } /** * Get the count of slots for Tx retransmission. * * @return the count of slots for Tx retransmission * @hide */ @SystemApi public int getTxRetransmitSlotCount() { return mTxRetransmitSlotCount; } /** * Get the count of slots for Rx retransmission. * * @return the count of slots for Rx retransmission * @hide */ @SystemApi public int getRxRetransmitSlotCount() { return mRxRetransmitSlotCount; } /** * Get the count of Rx good packets * * @return the count of Rx good packets * @hide */ @SystemApi public int getGoodRxFrameCount() { return mGoodRxFrameCount; } /** * Describe contents. * * @return 0 * @hide */ public int describeContents() { return 0; } /** * Write BqrVsScoChoppy to parcel. * * @hide */ @SystemApi @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mGlitchCount); dest.writeInt(mIntervalEsco); dest.writeInt(mWindowEsco); dest.writeInt(mAirFormat); dest.writeInt(mInstanceCount); dest.writeInt(mTxCxmDenials); dest.writeInt(mRxCxmDenials); dest.writeInt(mTxAbortCount); dest.writeInt(mLateDispatch); dest.writeInt(mMicIntrMiss); dest.writeInt(mLpaIntrMiss); dest.writeInt(mSprIntrMiss); dest.writeInt(mPlcFillCount); dest.writeInt(mPlcDiscardCount); dest.writeInt(mMissedInstanceCount); dest.writeInt(mTxRetransmitSlotCount); dest.writeInt(mRxRetransmitSlotCount); dest.writeInt(mGoodRxFrameCount); } /** @hide */ @SystemApi public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BqrVsScoChoppy createFromParcel(Parcel in) { return new BqrVsScoChoppy(in); } public BqrVsScoChoppy[] newArray(int size) { return new BqrVsScoChoppy[size]; } }; /** BqrVsScoChoppy to String. */ @Override @NonNull public String toString() { String str; str = " BqrVsScoChoppy: {\n" + " mGlitchCount: " + mGlitchCount + ", mIntervalEsco: " + mIntervalEsco + ", mWindowEsco: " + mWindowEsco + ", mAirFormat: " + airFormatToString(mAirFormat) + "(" + String.format("0x%02X", mAirFormat) + ")" + ", mInstanceCount: " + mInstanceCount + ", mTxCxmDenials: " + mTxCxmDenials + ", mRxCxmDenials: " + mRxCxmDenials + ", mTxAbortCount: " + mTxAbortCount + ",\n" + " mLateDispatch: " + mLateDispatch + ", mMicIntrMiss: " + mMicIntrMiss + ", mLpaIntrMiss: " + mLpaIntrMiss + ", mSprIntrMiss: " + mSprIntrMiss + ", mPlcFillCount: " + mPlcFillCount + ", mPlcDiscardCount: " + mPlcDiscardCount + ", mMissedInstanceCount: " + mMissedInstanceCount + ", mTxRetransmitSlotCount: " + mTxRetransmitSlotCount + ",\n" + " mRxRetransmitSlotCount: " + mRxRetransmitSlotCount + ", mGoodRxFrameCount: " + mGoodRxFrameCount + "\n }"; return str; } } /** * This class provides the System APIs to access the Connect fail event. * * @hide */ @SystemApi public static final class BqrConnectFail implements Parcelable { private static final String TAG = BluetoothQualityReport.TAG + ".BqrConnectFail"; /** * Connect Fail reason: No error. * * @hide */ @SystemApi public static final int CONNECT_FAIL_ID_NO_ERROR = 0x00; /** * Connect Fail reason: Page timeout. * * @hide */ @SystemApi public static final int CONNECT_FAIL_ID_PAGE_TIMEOUT = 0x04; /** * Connect Fail reason: Connection timeout. * * @hide */ @SystemApi public static final int CONNECT_FAIL_ID_CONNECTION_TIMEOUT = 0x08; /** * Connect Fail reason: ACL already exists. * * @hide */ @SystemApi public static final int CONNECT_FAIL_ID_ACL_ALREADY_EXIST = 0x0b; /** * Connect Fail reason: Controller busy. * * @hide */ @SystemApi public static final int CONNECT_FAIL_ID_CONTROLLER_BUSY = 0x3a; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = {"CONNECT_FAIL_ID"}, value = { CONNECT_FAIL_ID_NO_ERROR, CONNECT_FAIL_ID_PAGE_TIMEOUT, CONNECT_FAIL_ID_CONNECTION_TIMEOUT, CONNECT_FAIL_ID_ACL_ALREADY_EXIST, CONNECT_FAIL_ID_CONTROLLER_BUSY, }) public @interface ConnectFailId {} private int mFailReason; private BqrConnectFail(byte[] rawData, int offset) { if (rawData == null || rawData.length <= offset) { throw new IllegalArgumentException(TAG + ": BQR raw data length is abnormal."); } ByteBuffer bqrBuf = ByteBuffer.wrap(rawData, offset, rawData.length - offset).asReadOnlyBuffer(); bqrBuf.order(ByteOrder.LITTLE_ENDIAN); mFailReason = bqrBuf.get() & 0xFF; } private BqrConnectFail(Parcel in) { mFailReason = in.readInt(); } /** * Get the fail reason. * * @return the fail reason * @hide */ @SystemApi @ConnectFailId public int getFailReason() { return mFailReason; } /** * Describe contents. * * @return 0 * @hide */ public int describeContents() { return 0; } /** * Write BqrConnectFail to parcel. * * @hide */ @SystemApi @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mFailReason); } /** @hide */ @SystemApi public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BqrConnectFail createFromParcel(Parcel in) { return new BqrConnectFail(in); } public BqrConnectFail[] newArray(int size) { return new BqrConnectFail[size]; } }; /** * Get the string of the Connect Fail ID. * * @param id the connect fail reason * @return the string of the id * @hide */ @SystemApi public static @NonNull String connectFailIdToString(@ConnectFailId int id) { switch (id) { case CONNECT_FAIL_ID_NO_ERROR: return "No error"; case CONNECT_FAIL_ID_PAGE_TIMEOUT: return "Page Timeout"; case CONNECT_FAIL_ID_CONNECTION_TIMEOUT: return "Connection Timeout"; case CONNECT_FAIL_ID_ACL_ALREADY_EXIST: return "ACL already exists"; case CONNECT_FAIL_ID_CONTROLLER_BUSY: return "Controller busy"; default: return "INVALID"; } } /** BqrConnectFail to String. */ @Override @NonNull public String toString() { String str; str = " BqrConnectFail: {\n" + " mFailReason: " + connectFailIdToString(mFailReason) + " (" + String.format("0x%02X", mFailReason) + ")" + "\n }"; return str; } } }