diff --git a/nearby/framework/Android.bp b/nearby/framework/Android.bp index 278f8231257e48fed191e8734e0ce2e98e5060da..f32929596d0bc84ce10e3c3ef57264c4ff37ff0a 100644 --- a/nearby/framework/Android.bp +++ b/nearby/framework/Android.bp @@ -52,5 +52,7 @@ java_library { static_libs: [ "modules-utils-preconditions", ], - visibility: ["//packages/modules/Connectivity/nearby/tests:__subpackages__"], + visibility: [ + "//packages/modules/Connectivity/nearby/tests:__subpackages__", + ], } diff --git a/nearby/framework/java/android/nearby/FastPairAccountKeyDeviceMetadata.java b/nearby/framework/java/android/nearby/FastPairAccountKeyDeviceMetadata.java deleted file mode 100644 index d42fbf4054104ed10e0efbbeea9479fb7b0ef53c..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairAccountKeyDeviceMetadata.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel; - -/** - * Class for metadata of a Fast Pair device associated with an account. - * - * @hide - */ -public class FastPairAccountKeyDeviceMetadata { - - FastPairAccountKeyDeviceMetadataParcel mMetadataParcel; - - FastPairAccountKeyDeviceMetadata(FastPairAccountKeyDeviceMetadataParcel metadataParcel) { - this.mMetadataParcel = metadataParcel; - } - - /** - * Get Device Account Key, which uniquely identifies a Fast Pair device associated with an - * account. AccountKey is 16 bytes: first byte is 0x04. Other 15 bytes are randomly generated. - * - * @return 16-byte Account Key. - * @hide - */ - @Nullable - public byte[] getDeviceAccountKey() { - return mMetadataParcel.deviceAccountKey; - } - - /** - * Get a hash value of device's account key and public bluetooth address without revealing the - * public bluetooth address. Sha256 hash value is 32 bytes. - * - * @return 32-byte Sha256 hash value. - * @hide - */ - @Nullable - public byte[] getSha256DeviceAccountKeyPublicAddress() { - return mMetadataParcel.sha256DeviceAccountKeyPublicAddress; - } - - /** - * Get metadata of a Fast Pair device type. - * - * @hide - */ - @Nullable - public FastPairDeviceMetadata getFastPairDeviceMetadata() { - if (mMetadataParcel.metadata == null) { - return null; - } - return new FastPairDeviceMetadata(mMetadataParcel.metadata); - } - - /** - * Get Fast Pair discovery item, which is tied to both the device type and the account. - * - * @hide - */ - @Nullable - public FastPairDiscoveryItem getFastPairDiscoveryItem() { - if (mMetadataParcel.discoveryItem == null) { - return null; - } - return new FastPairDiscoveryItem(mMetadataParcel.discoveryItem); - } - - /** - * Builder used to create FastPairAccountKeyDeviceMetadata. - * - * @hide - */ - public static final class Builder { - - private final FastPairAccountKeyDeviceMetadataParcel mBuilderParcel; - - /** - * Default constructor of Builder. - * - * @hide - */ - public Builder() { - mBuilderParcel = new FastPairAccountKeyDeviceMetadataParcel(); - mBuilderParcel.deviceAccountKey = null; - mBuilderParcel.sha256DeviceAccountKeyPublicAddress = null; - mBuilderParcel.metadata = null; - mBuilderParcel.discoveryItem = null; - } - - /** - * Set Account Key. - * - * @param deviceAccountKey Fast Pair device account key, which is 16 bytes: first byte is - * 0x04. Next 15 bytes are randomly generated. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setDeviceAccountKey(@Nullable byte[] deviceAccountKey) { - mBuilderParcel.deviceAccountKey = deviceAccountKey; - return this; - } - - /** - * Set sha256 hash value of account key and public bluetooth address. - * - * @param sha256DeviceAccountKeyPublicAddress 32-byte sha256 hash value of account key and - * public bluetooth address. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setSha256DeviceAccountKeyPublicAddress( - @Nullable byte[] sha256DeviceAccountKeyPublicAddress) { - mBuilderParcel.sha256DeviceAccountKeyPublicAddress = - sha256DeviceAccountKeyPublicAddress; - return this; - } - - - /** - * Set Fast Pair metadata. - * - * @param metadata Fast Pair metadata. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setFastPairDeviceMetadata(@Nullable FastPairDeviceMetadata metadata) { - if (metadata == null) { - mBuilderParcel.metadata = null; - } else { - mBuilderParcel.metadata = metadata.mMetadataParcel; - } - return this; - } - - /** - * Set Fast Pair discovery item. - * - * @param discoveryItem Fast Pair discovery item. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setFastPairDiscoveryItem(@Nullable FastPairDiscoveryItem discoveryItem) { - if (discoveryItem == null) { - mBuilderParcel.discoveryItem = null; - } else { - mBuilderParcel.discoveryItem = discoveryItem.mMetadataParcel; - } - return this; - } - - /** - * Build {@link FastPairAccountKeyDeviceMetadata} with the currently set configuration. - * - * @hide - */ - @NonNull - public FastPairAccountKeyDeviceMetadata build() { - return new FastPairAccountKeyDeviceMetadata(mBuilderParcel); - } - } -} diff --git a/nearby/framework/java/android/nearby/FastPairAntispoofKeyDeviceMetadata.java b/nearby/framework/java/android/nearby/FastPairAntispoofKeyDeviceMetadata.java deleted file mode 100644 index 74831d5183e5c8c945eede014cdf382dbd230055..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairAntispoofKeyDeviceMetadata.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel; - -/** - * Class for a type of registered Fast Pair device keyed by modelID, or antispoofKey. - * - * @hide - */ -public class FastPairAntispoofKeyDeviceMetadata { - - FastPairAntispoofKeyDeviceMetadataParcel mMetadataParcel; - FastPairAntispoofKeyDeviceMetadata( - FastPairAntispoofKeyDeviceMetadataParcel metadataParcel) { - this.mMetadataParcel = metadataParcel; - } - - /** - * Get Antispoof public key. - * - * @hide - */ - @Nullable - public byte[] getAntispoofPublicKey() { - return this.mMetadataParcel.antispoofPublicKey; - } - - /** - * Get metadata of a Fast Pair device type. - * - * @hide - */ - @Nullable - public FastPairDeviceMetadata getFastPairDeviceMetadata() { - if (this.mMetadataParcel.deviceMetadata == null) { - return null; - } - return new FastPairDeviceMetadata(this.mMetadataParcel.deviceMetadata); - } - - /** - * Builder used to create FastPairAntispoofkeyDeviceMetadata. - * - * @hide - */ - public static final class Builder { - - private final FastPairAntispoofKeyDeviceMetadataParcel mBuilderParcel; - - /** - * Default constructor of Builder. - * - * @hide - */ - public Builder() { - mBuilderParcel = new FastPairAntispoofKeyDeviceMetadataParcel(); - mBuilderParcel.antispoofPublicKey = null; - mBuilderParcel.deviceMetadata = null; - } - - /** - * Set AntiSpoof public key, which uniquely identify a Fast Pair device type. - * - * @param antispoofPublicKey is 64 bytes, see Data Format. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setAntispoofPublicKey(@Nullable byte[] antispoofPublicKey) { - mBuilderParcel.antispoofPublicKey = antispoofPublicKey; - return this; - } - - /** - * Set Fast Pair metadata, which is the property of a Fast Pair device type, including - * device images and strings. - * - * @param metadata Fast Pair device meta data. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setFastPairDeviceMetadata(@Nullable FastPairDeviceMetadata metadata) { - if (metadata != null) { - mBuilderParcel.deviceMetadata = metadata.mMetadataParcel; - } else { - mBuilderParcel.deviceMetadata = null; - } - return this; - } - - /** - * Build {@link FastPairAntispoofKeyDeviceMetadata} with the currently set configuration. - * - * @hide - */ - @NonNull - public FastPairAntispoofKeyDeviceMetadata build() { - return new FastPairAntispoofKeyDeviceMetadata(mBuilderParcel); - } - } -} diff --git a/nearby/framework/java/android/nearby/FastPairDataProviderService.java b/nearby/framework/java/android/nearby/FastPairDataProviderService.java deleted file mode 100644 index f1d507471fbab0a37699ff8f90f5e70cdc93f416..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairDataProviderService.java +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby; - -import android.accounts.Account; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.Service; -import android.content.Intent; -import android.nearby.aidl.ByteArrayParcel; -import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel; -import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel; -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel; -import android.nearby.aidl.FastPairEligibleAccountParcel; -import android.nearby.aidl.FastPairEligibleAccountsRequestParcel; -import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel; -import android.nearby.aidl.FastPairManageAccountRequestParcel; -import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback; -import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback; -import android.nearby.aidl.IFastPairDataProvider; -import android.nearby.aidl.IFastPairEligibleAccountsCallback; -import android.nearby.aidl.IFastPairManageAccountCallback; -import android.nearby.aidl.IFastPairManageAccountDeviceCallback; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * A service class for fast pair data providers outside the system server. - * - * Fast pair providers should be wrapped in a non-exported service which returns the result of - * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The - * service should not be exported so that components other than the system server cannot bind to it. - * Alternatively, the service may be guarded by a permission that only system server can obtain. - * - *

Fast Pair providers are identified by their UID / package name. - * - * @hide - */ -public abstract class FastPairDataProviderService extends Service { - /** - * The action the wrapping service should have in its intent filter to implement the - * {@link android.nearby.FastPairDataProviderBase}. - * - * @hide - */ - public static final String ACTION_FAST_PAIR_DATA_PROVIDER = - "android.nearby.action.FAST_PAIR_DATA_PROVIDER"; - - /** - * Manage request type to add, or opt-in. - * - * @hide - */ - public static final int MANAGE_REQUEST_ADD = 0; - - /** - * Manage request type to remove, or opt-out. - * - * @hide - */ - public static final int MANAGE_REQUEST_REMOVE = 1; - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - MANAGE_REQUEST_ADD, - MANAGE_REQUEST_REMOVE}) - @interface ManageRequestType {} - - /** - * Error code for bad request. - * - * @hide - */ - public static final int ERROR_CODE_BAD_REQUEST = 0; - - /** - * Error code for internal error. - * - * @hide - */ - public static final int ERROR_CODE_INTERNAL_ERROR = 1; - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - ERROR_CODE_BAD_REQUEST, - ERROR_CODE_INTERNAL_ERROR}) - @interface ErrorCode {} - - private final IBinder mBinder; - private final String mTag; - - /** - * Constructor of FastPairDataProviderService. - * - * @param tag TAG for on device logging. - * @hide - */ - public FastPairDataProviderService(@NonNull String tag) { - mBinder = new Service(); - mTag = tag; - } - - @Override - @NonNull - public final IBinder onBind(@NonNull Intent intent) { - return mBinder; - } - - /** - * Callback to be invoked when an AntispoofKeyed device metadata is loaded. - * - * @hide - */ - public interface FastPairAntispoofKeyDeviceMetadataCallback { - - /** - * Invoked once the meta data is loaded. - * - * @hide - */ - void onFastPairAntispoofKeyDeviceMetadataReceived( - @NonNull FastPairAntispoofKeyDeviceMetadata metadata); - - /** Invoked in case of error. - * - * @hide - */ - void onError(@ErrorCode int code, @Nullable String message); - } - - /** - * Callback to be invoked when Fast Pair devices of a given account is loaded. - * - * @hide - */ - public interface FastPairAccountDevicesMetadataCallback { - - /** - * Should be invoked once the metadatas are loaded. - * - * @hide - */ - void onFastPairAccountDevicesMetadataReceived( - @NonNull Collection metadatas); - /** - * Invoked in case of error. - * - * @hide - */ - void onError(@ErrorCode int code, @Nullable String message); - } - - /** - * Callback to be invoked when FastPair eligible accounts are loaded. - * - * @hide - */ - public interface FastPairEligibleAccountsCallback { - - /** - * Should be invoked once the eligible accounts are loaded. - * - * @hide - */ - void onFastPairEligibleAccountsReceived( - @NonNull Collection accounts); - /** - * Invoked in case of error. - * - * @hide - */ - void onError(@ErrorCode int code, @Nullable String message); - } - - /** - * Callback to be invoked when a management action is finished. - * - * @hide - */ - public interface FastPairManageActionCallback { - - /** - * Should be invoked once the manage action is successful. - * - * @hide - */ - void onSuccess(); - /** - * Invoked in case of error. - * - * @hide - */ - void onError(@ErrorCode int code, @Nullable String message); - } - - /** - * Fulfills the Fast Pair device metadata request by using callback to send back the - * device meta data of a given modelId. - * - * @hide - */ - public abstract void onLoadFastPairAntispoofKeyDeviceMetadata( - @NonNull FastPairAntispoofKeyDeviceMetadataRequest request, - @NonNull FastPairAntispoofKeyDeviceMetadataCallback callback); - - /** - * Fulfills the account tied Fast Pair devices metadata request by using callback to send back - * all Fast Pair device's metadata of a given account. - * - * @hide - */ - public abstract void onLoadFastPairAccountDevicesMetadata( - @NonNull FastPairAccountDevicesMetadataRequest request, - @NonNull FastPairAccountDevicesMetadataCallback callback); - - /** - * Fulfills the Fast Pair eligible accounts request by using callback to send back Fast Pair - * eligible accounts. - * - * @hide - */ - public abstract void onLoadFastPairEligibleAccounts( - @NonNull FastPairEligibleAccountsRequest request, - @NonNull FastPairEligibleAccountsCallback callback); - - /** - * Fulfills the Fast Pair account management request by using callback to send back result. - * - * @hide - */ - public abstract void onManageFastPairAccount( - @NonNull FastPairManageAccountRequest request, - @NonNull FastPairManageActionCallback callback); - - /** - * Fulfills the request to manage device-account mapping by using callback to send back result. - * - * @hide - */ - public abstract void onManageFastPairAccountDevice( - @NonNull FastPairManageAccountDeviceRequest request, - @NonNull FastPairManageActionCallback callback); - - /** - * Class for reading FastPairAntispoofKeyDeviceMetadataRequest, which specifies the model ID of - * a Fast Pair device. To fulfill this request, corresponding - * {@link FastPairAntispoofKeyDeviceMetadata} should be fetched and returned. - * - * @hide - */ - public static class FastPairAntispoofKeyDeviceMetadataRequest { - - private final FastPairAntispoofKeyDeviceMetadataRequestParcel mMetadataRequestParcel; - - private FastPairAntispoofKeyDeviceMetadataRequest( - final FastPairAntispoofKeyDeviceMetadataRequestParcel metaDataRequestParcel) { - this.mMetadataRequestParcel = metaDataRequestParcel; - } - - /** - * Get modelId (24 bit), the key for FastPairAntispoofKeyDeviceMetadata in the same format - * returned by Google at device registration time. - * - * ModelId format is defined at device registration time, see - * Model ID. - * @return raw bytes of modelId in the same format returned by Google at device registration - * time. - * @hide - */ - public @NonNull byte[] getModelId() { - return this.mMetadataRequestParcel.modelId; - } - } - - /** - * Class for reading FastPairAccountDevicesMetadataRequest, which specifies the Fast Pair - * account and the allow list of the FastPair device keys saved to the account (i.e., FastPair - * accountKeys). - * - * A Fast Pair accountKey is created when a Fast Pair device is saved to an account. It is per - * Fast Pair device per account. - * - * To retrieve all Fast Pair accountKeys saved to an account, the caller needs to set - * account with an empty allow list. - * - * To retrieve metadata of a selected list of Fast Pair devices saved to an account, the caller - * needs to set account with a non-empty allow list. - * @hide - */ - public static class FastPairAccountDevicesMetadataRequest { - - private final FastPairAccountDevicesMetadataRequestParcel mMetadataRequestParcel; - - private FastPairAccountDevicesMetadataRequest( - final FastPairAccountDevicesMetadataRequestParcel metaDataRequestParcel) { - this.mMetadataRequestParcel = metaDataRequestParcel; - } - - /** - * Get FastPair account, whose Fast Pair devices' metadata is requested. - * - * @return a FastPair account. - * @hide - */ - public @NonNull Account getAccount() { - return this.mMetadataRequestParcel.account; - } - - /** - * Get allowlist of Fast Pair devices using a collection of deviceAccountKeys. - * Note that as a special case, empty list actually means all FastPair devices under the - * account instead of none. - * - * DeviceAccountKey is 16 bytes: first byte is 0x04. Other 15 bytes are randomly generated. - * - * @return allowlist of Fast Pair devices using a collection of deviceAccountKeys. - * @hide - */ - public @NonNull Collection getDeviceAccountKeys() { - if (this.mMetadataRequestParcel.deviceAccountKeys == null) { - return new ArrayList(0); - } - List deviceAccountKeys = - new ArrayList<>(this.mMetadataRequestParcel.deviceAccountKeys.length); - for (ByteArrayParcel deviceAccountKey : this.mMetadataRequestParcel.deviceAccountKeys) { - deviceAccountKeys.add(deviceAccountKey.byteArray); - } - return deviceAccountKeys; - } - } - - /** - * Class for reading FastPairEligibleAccountsRequest. Upon receiving this request, Fast Pair - * eligible accounts should be returned to bind Fast Pair devices. - * - * @hide - */ - public static class FastPairEligibleAccountsRequest { - @SuppressWarnings("UnusedVariable") - private final FastPairEligibleAccountsRequestParcel mAccountsRequestParcel; - - private FastPairEligibleAccountsRequest( - final FastPairEligibleAccountsRequestParcel accountsRequestParcel) { - this.mAccountsRequestParcel = accountsRequestParcel; - } - } - - /** - * Class for reading FastPairManageAccountRequest. If the request type is MANAGE_REQUEST_ADD, - * the account is enabled to bind Fast Pair devices; If the request type is - * MANAGE_REQUEST_REMOVE, the account is disabled to bind more Fast Pair devices. Furthermore, - * all existing bounded Fast Pair devices are unbounded. - * - * @hide - */ - public static class FastPairManageAccountRequest { - - private final FastPairManageAccountRequestParcel mAccountRequestParcel; - - private FastPairManageAccountRequest( - final FastPairManageAccountRequestParcel accountRequestParcel) { - this.mAccountRequestParcel = accountRequestParcel; - } - - /** - * Get request type: MANAGE_REQUEST_ADD, or MANAGE_REQUEST_REMOVE. - * - * @hide - */ - public @ManageRequestType int getRequestType() { - return this.mAccountRequestParcel.requestType; - } - /** - * Get account. - * - * @hide - */ - public @NonNull Account getAccount() { - return this.mAccountRequestParcel.account; - } - } - - /** - * Class for reading FastPairManageAccountDeviceRequest. If the request type is - * MANAGE_REQUEST_ADD, then a Fast Pair device is bounded to a Fast Pair account. If the - * request type is MANAGE_REQUEST_REMOVE, then a Fast Pair device is removed from a Fast Pair - * account. - * - * @hide - */ - public static class FastPairManageAccountDeviceRequest { - - private final FastPairManageAccountDeviceRequestParcel mRequestParcel; - - private FastPairManageAccountDeviceRequest( - final FastPairManageAccountDeviceRequestParcel requestParcel) { - this.mRequestParcel = requestParcel; - } - - /** - * Get request type: MANAGE_REQUEST_ADD, or MANAGE_REQUEST_REMOVE. - * - * @hide - */ - public @ManageRequestType int getRequestType() { - return this.mRequestParcel.requestType; - } - /** - * Get account. - * - * @hide - */ - public @NonNull Account getAccount() { - return this.mRequestParcel.account; - } - /** - * Get account key device metadata. - * - * @hide - */ - public @NonNull FastPairAccountKeyDeviceMetadata getAccountKeyDeviceMetadata() { - return new FastPairAccountKeyDeviceMetadata( - this.mRequestParcel.accountKeyDeviceMetadata); - } - } - - /** - * Callback class that sends back FastPairAntispoofKeyDeviceMetadata. - */ - private final class WrapperFastPairAntispoofKeyDeviceMetadataCallback implements - FastPairAntispoofKeyDeviceMetadataCallback { - - private IFastPairAntispoofKeyDeviceMetadataCallback mCallback; - - private WrapperFastPairAntispoofKeyDeviceMetadataCallback( - IFastPairAntispoofKeyDeviceMetadataCallback callback) { - mCallback = callback; - } - - /** - * Sends back FastPairAntispoofKeyDeviceMetadata. - */ - @Override - public void onFastPairAntispoofKeyDeviceMetadataReceived( - @NonNull FastPairAntispoofKeyDeviceMetadata metadata) { - try { - mCallback.onFastPairAntispoofKeyDeviceMetadataReceived(metadata.mMetadataParcel); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - - @Override - public void onError(@ErrorCode int code, @Nullable String message) { - try { - mCallback.onError(code, message); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - } - - /** - * Callback class that sends back collection of FastPairAccountKeyDeviceMetadata. - */ - private final class WrapperFastPairAccountDevicesMetadataCallback implements - FastPairAccountDevicesMetadataCallback { - - private IFastPairAccountDevicesMetadataCallback mCallback; - - private WrapperFastPairAccountDevicesMetadataCallback( - IFastPairAccountDevicesMetadataCallback callback) { - mCallback = callback; - } - - /** - * Sends back collection of FastPairAccountKeyDeviceMetadata. - */ - @Override - public void onFastPairAccountDevicesMetadataReceived( - @NonNull Collection metadatas) { - FastPairAccountKeyDeviceMetadataParcel[] metadataParcels = - new FastPairAccountKeyDeviceMetadataParcel[metadatas.size()]; - int i = 0; - for (FastPairAccountKeyDeviceMetadata metadata : metadatas) { - metadataParcels[i] = metadata.mMetadataParcel; - i = i + 1; - } - try { - mCallback.onFastPairAccountDevicesMetadataReceived(metadataParcels); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - - @Override - public void onError(@ErrorCode int code, @Nullable String message) { - try { - mCallback.onError(code, message); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - } - - /** - * Callback class that sends back eligible Fast Pair accounts. - */ - private final class WrapperFastPairEligibleAccountsCallback implements - FastPairEligibleAccountsCallback { - - private IFastPairEligibleAccountsCallback mCallback; - - private WrapperFastPairEligibleAccountsCallback( - IFastPairEligibleAccountsCallback callback) { - mCallback = callback; - } - - /** - * Sends back the eligible Fast Pair accounts. - */ - @Override - public void onFastPairEligibleAccountsReceived( - @NonNull Collection accounts) { - int i = 0; - FastPairEligibleAccountParcel[] accountParcels = - new FastPairEligibleAccountParcel[accounts.size()]; - for (FastPairEligibleAccount account: accounts) { - accountParcels[i] = account.mAccountParcel; - i = i + 1; - } - try { - mCallback.onFastPairEligibleAccountsReceived(accountParcels); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - - @Override - public void onError(@ErrorCode int code, @Nullable String message) { - try { - mCallback.onError(code, message); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - } - - /** - * Callback class that sends back Fast Pair account management result. - */ - private final class WrapperFastPairManageAccountCallback implements - FastPairManageActionCallback { - - private IFastPairManageAccountCallback mCallback; - - private WrapperFastPairManageAccountCallback( - IFastPairManageAccountCallback callback) { - mCallback = callback; - } - - /** - * Sends back Fast Pair account opt in result. - */ - @Override - public void onSuccess() { - try { - mCallback.onSuccess(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - - @Override - public void onError(@ErrorCode int code, @Nullable String message) { - try { - mCallback.onError(code, message); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - } - - /** - * Call back class that sends back account-device mapping management result. - */ - private final class WrapperFastPairManageAccountDeviceCallback implements - FastPairManageActionCallback { - - private IFastPairManageAccountDeviceCallback mCallback; - - private WrapperFastPairManageAccountDeviceCallback( - IFastPairManageAccountDeviceCallback callback) { - mCallback = callback; - } - - /** - * Sends back the account-device mapping management result. - */ - @Override - public void onSuccess() { - try { - mCallback.onSuccess(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - - @Override - public void onError(@ErrorCode int code, @Nullable String message) { - try { - mCallback.onError(code, message); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { - Log.w(mTag, e); - } - } - } - - private final class Service extends IFastPairDataProvider.Stub { - - Service() { - } - - @Override - public void loadFastPairAntispoofKeyDeviceMetadata( - @NonNull FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel, - IFastPairAntispoofKeyDeviceMetadataCallback callback) { - onLoadFastPairAntispoofKeyDeviceMetadata( - new FastPairAntispoofKeyDeviceMetadataRequest(requestParcel), - new WrapperFastPairAntispoofKeyDeviceMetadataCallback(callback)); - } - - @Override - public void loadFastPairAccountDevicesMetadata( - @NonNull FastPairAccountDevicesMetadataRequestParcel requestParcel, - IFastPairAccountDevicesMetadataCallback callback) { - onLoadFastPairAccountDevicesMetadata( - new FastPairAccountDevicesMetadataRequest(requestParcel), - new WrapperFastPairAccountDevicesMetadataCallback(callback)); - } - - @Override - public void loadFastPairEligibleAccounts( - @NonNull FastPairEligibleAccountsRequestParcel requestParcel, - IFastPairEligibleAccountsCallback callback) { - onLoadFastPairEligibleAccounts(new FastPairEligibleAccountsRequest(requestParcel), - new WrapperFastPairEligibleAccountsCallback(callback)); - } - - @Override - public void manageFastPairAccount( - @NonNull FastPairManageAccountRequestParcel requestParcel, - IFastPairManageAccountCallback callback) { - onManageFastPairAccount(new FastPairManageAccountRequest(requestParcel), - new WrapperFastPairManageAccountCallback(callback)); - } - - @Override - public void manageFastPairAccountDevice( - @NonNull FastPairManageAccountDeviceRequestParcel requestParcel, - IFastPairManageAccountDeviceCallback callback) { - onManageFastPairAccountDevice(new FastPairManageAccountDeviceRequest(requestParcel), - new WrapperFastPairManageAccountDeviceCallback(callback)); - } - } -} diff --git a/nearby/framework/java/android/nearby/FastPairDeviceMetadata.java b/nearby/framework/java/android/nearby/FastPairDeviceMetadata.java deleted file mode 100644 index 0e2e79d8a67ea7d3d3f0394e143c862d7f1429c6..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairDeviceMetadata.java +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.nearby.aidl.FastPairDeviceMetadataParcel; - -/** - * Class for the properties of a given type of Fast Pair device, including images and text. - * - * @hide - */ -public class FastPairDeviceMetadata { - - FastPairDeviceMetadataParcel mMetadataParcel; - - FastPairDeviceMetadata( - FastPairDeviceMetadataParcel metadataParcel) { - this.mMetadataParcel = metadataParcel; - } - - /** - * Get ImageUrl, which will be displayed in notification. - * - * @hide - */ - @Nullable - public String getImageUrl() { - return mMetadataParcel.imageUrl; - } - - /** - * Get IntentUri, which will be launched to install companion app. - * - * @hide - */ - @Nullable - public String getIntentUri() { - return mMetadataParcel.intentUri; - } - - /** - * Get BLE transmit power, as described in Fast Pair spec, see - * Transmit Power - * - * @hide - */ - public int getBleTxPower() { - return mMetadataParcel.bleTxPower; - } - - /** - * Get Fast Pair Half Sheet trigger distance in meters. - * - * @hide - */ - public float getTriggerDistance() { - return mMetadataParcel.triggerDistance; - } - - /** - * Get Fast Pair device image, which is submitted at device registration time to display on - * notification. It is a 32-bit PNG with dimensions of 512px by 512px. - * - * @return Fast Pair device image in 32-bit PNG with dimensions of 512px by 512px. - * @hide - */ - @Nullable - public byte[] getImage() { - return mMetadataParcel.image; - } - - /** - * Get Fast Pair device type. - * DEVICE_TYPE_UNSPECIFIED = 0; - * HEADPHONES = 1; - * TRUE_WIRELESS_HEADPHONES = 7; - * @hide - */ - public int getDeviceType() { - return mMetadataParcel.deviceType; - } - - /** - * Get Fast Pair device name. e.g., "Pixel Buds A-Series". - * - * @hide - */ - @Nullable - public String getName() { - return mMetadataParcel.name; - } - - /** - * Get true wireless image url for left bud. - * - * @hide - */ - @Nullable - public String getTrueWirelessImageUrlLeftBud() { - return mMetadataParcel.trueWirelessImageUrlLeftBud; - } - - /** - * Get true wireless image url for right bud. - * - * @hide - */ - @Nullable - public String getTrueWirelessImageUrlRightBud() { - return mMetadataParcel.trueWirelessImageUrlRightBud; - } - - /** - * Get true wireless image url for case. - * - * @hide - */ - @Nullable - public String getTrueWirelessImageUrlCase() { - return mMetadataParcel.trueWirelessImageUrlCase; - } - - /** - * Get InitialNotificationDescription, which is a translated string of - * "Tap to pair. Earbuds will be tied to %s" based on locale. - * - * @hide - */ - @Nullable - public String getInitialNotificationDescription() { - return mMetadataParcel.initialNotificationDescription; - } - - /** - * Get InitialNotificationDescriptionNoAccount, which is a translated string of - * "Tap to pair with this device" based on locale. - * - * @hide - */ - @Nullable - public String getInitialNotificationDescriptionNoAccount() { - return mMetadataParcel.initialNotificationDescriptionNoAccount; - } - - /** - * Get OpenCompanionAppDescription, which is a translated string of - * "Tap to finish setup" based on locale. - * - * @hide - */ - @Nullable - public String getOpenCompanionAppDescription() { - return mMetadataParcel.openCompanionAppDescription; - } - - /** - * Get UpdateCompanionAppDescription, which is a translated string of - * "Tap to update device settings and finish setup" based on locale. - * - * @hide - */ - @Nullable - public String getUpdateCompanionAppDescription() { - return mMetadataParcel.updateCompanionAppDescription; - } - - /** - * Get DownloadCompanionAppDescription, which is a translated string of - * "Tap to download device app on Google Play and see all features" based on locale. - * - * @hide - */ - @Nullable - public String getDownloadCompanionAppDescription() { - return mMetadataParcel.downloadCompanionAppDescription; - } - - /** - * Get UnableToConnectTitle, which is a translated string of - * "Unable to connect" based on locale. - */ - @Nullable - public String getUnableToConnectTitle() { - return mMetadataParcel.unableToConnectTitle; - } - - /** - * Get UnableToConnectDescription, which is a translated string of - * "Try manually pairing to the device" based on locale. - * - * @hide - */ - @Nullable - public String getUnableToConnectDescription() { - return mMetadataParcel.unableToConnectDescription; - } - - /** - * Get InitialPairingDescription, which is a translated string of - * "%s will appear on devices linked with %s" based on locale. - * - * @hide - */ - @Nullable - public String getInitialPairingDescription() { - return mMetadataParcel.initialPairingDescription; - } - - /** - * Get ConnectSuccessCompanionAppInstalled, which is a translated string of - * "Your device is ready to be set up" based on locale. - * - * @hide - */ - @Nullable - public String getConnectSuccessCompanionAppInstalled() { - return mMetadataParcel.connectSuccessCompanionAppInstalled; - } - - /** - * Get ConnectSuccessCompanionAppNotInstalled, which is a translated string of - * "Download the device app on Google Play to see all available features" based on locale. - * - * @hide - */ - @Nullable - public String getConnectSuccessCompanionAppNotInstalled() { - return mMetadataParcel.connectSuccessCompanionAppNotInstalled; - } - - /** - * Get SubsequentPairingDescription, which is a translated string of - * "Connect %s to this phone" based on locale. - * - * @hide - */ - @Nullable - public String getSubsequentPairingDescription() { - return mMetadataParcel.subsequentPairingDescription; - } - - /** - * Get RetroactivePairingDescription, which is a translated string of - * "Save device to %s for faster pairing to your other devices" based on locale. - * - * @hide - */ - @Nullable - public String getRetroactivePairingDescription() { - return mMetadataParcel.retroactivePairingDescription; - } - - /** - * Get WaitLaunchCompanionAppDescription, which is a translated string of - * "This will take a few moments" based on locale. - * - * @hide - */ - @Nullable - public String getWaitLaunchCompanionAppDescription() { - return mMetadataParcel.waitLaunchCompanionAppDescription; - } - - /** - * Get FailConnectGoToSettingsDescription, which is a translated string of - * "Try manually pairing to the device by going to Settings" based on locale. - * - * @hide - */ - @Nullable - public String getFailConnectGoToSettingsDescription() { - return mMetadataParcel.failConnectGoToSettingsDescription; - } - - /** - * Builder used to create FastPairDeviceMetadata. - * - * @hide - */ - public static final class Builder { - - private final FastPairDeviceMetadataParcel mBuilderParcel; - - /** - * Default constructor of Builder. - * - * @hide - */ - public Builder() { - mBuilderParcel = new FastPairDeviceMetadataParcel(); - mBuilderParcel.imageUrl = null; - mBuilderParcel.intentUri = null; - mBuilderParcel.name = null; - mBuilderParcel.bleTxPower = 0; - mBuilderParcel.triggerDistance = 0; - mBuilderParcel.image = null; - mBuilderParcel.deviceType = 0; // DEVICE_TYPE_UNSPECIFIED - mBuilderParcel.trueWirelessImageUrlLeftBud = null; - mBuilderParcel.trueWirelessImageUrlRightBud = null; - mBuilderParcel.trueWirelessImageUrlCase = null; - mBuilderParcel.initialNotificationDescription = null; - mBuilderParcel.initialNotificationDescriptionNoAccount = null; - mBuilderParcel.openCompanionAppDescription = null; - mBuilderParcel.updateCompanionAppDescription = null; - mBuilderParcel.downloadCompanionAppDescription = null; - mBuilderParcel.unableToConnectTitle = null; - mBuilderParcel.unableToConnectDescription = null; - mBuilderParcel.initialPairingDescription = null; - mBuilderParcel.connectSuccessCompanionAppInstalled = null; - mBuilderParcel.connectSuccessCompanionAppNotInstalled = null; - mBuilderParcel.subsequentPairingDescription = null; - mBuilderParcel.retroactivePairingDescription = null; - mBuilderParcel.waitLaunchCompanionAppDescription = null; - mBuilderParcel.failConnectGoToSettingsDescription = null; - } - - /** - * Set ImageUlr. - * - * @param imageUrl Image Ulr. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setImageUrl(@Nullable String imageUrl) { - mBuilderParcel.imageUrl = imageUrl; - return this; - } - - /** - * Set IntentUri. - * - * @param intentUri Intent uri. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setIntentUri(@Nullable String intentUri) { - mBuilderParcel.intentUri = intentUri; - return this; - } - - /** - * Set device name. - * - * @param name Device name. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setName(@Nullable String name) { - mBuilderParcel.name = name; - return this; - } - - /** - * Set ble transmission power. - * - * @param bleTxPower Ble transmission power. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setBleTxPower(int bleTxPower) { - mBuilderParcel.bleTxPower = bleTxPower; - return this; - } - - /** - * Set trigger distance. - * - * @param triggerDistance Fast Pair trigger distance. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTriggerDistance(float triggerDistance) { - mBuilderParcel.triggerDistance = triggerDistance; - return this; - } - - /** - * Set image. - * - * @param image Fast Pair device image, which is submitted at device registration time to - * display on notification. It is a 32-bit PNG with dimensions of - * 512px by 512px. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setImage(@Nullable byte[] image) { - mBuilderParcel.image = image; - return this; - } - - /** - * Set device type. - * - * @param deviceType Fast Pair device type. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setDeviceType(int deviceType) { - mBuilderParcel.deviceType = deviceType; - return this; - } - - /** - * Set true wireless image url for left bud. - * - * @param trueWirelessImageUrlLeftBud True wireless image url for left bud. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTrueWirelessImageUrlLeftBud( - @Nullable String trueWirelessImageUrlLeftBud) { - mBuilderParcel.trueWirelessImageUrlLeftBud = trueWirelessImageUrlLeftBud; - return this; - } - - /** - * Set true wireless image url for right bud. - * - * @param trueWirelessImageUrlRightBud True wireless image url for right bud. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTrueWirelessImageUrlRightBud( - @Nullable String trueWirelessImageUrlRightBud) { - mBuilderParcel.trueWirelessImageUrlRightBud = trueWirelessImageUrlRightBud; - return this; - } - - /** - * Set true wireless image url for case. - * - * @param trueWirelessImageUrlCase True wireless image url for case. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTrueWirelessImageUrlCase(@Nullable String trueWirelessImageUrlCase) { - mBuilderParcel.trueWirelessImageUrlCase = trueWirelessImageUrlCase; - return this; - } - - /** - * Set InitialNotificationDescription. - * - * @param initialNotificationDescription Initial notification description. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setInitialNotificationDescription( - @Nullable String initialNotificationDescription) { - mBuilderParcel.initialNotificationDescription = initialNotificationDescription; - return this; - } - - /** - * Set InitialNotificationDescriptionNoAccount. - * - * @param initialNotificationDescriptionNoAccount Initial notification description when - * account is not present. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setInitialNotificationDescriptionNoAccount( - @Nullable String initialNotificationDescriptionNoAccount) { - mBuilderParcel.initialNotificationDescriptionNoAccount = - initialNotificationDescriptionNoAccount; - return this; - } - - /** - * Set OpenCompanionAppDescription. - * - * @param openCompanionAppDescription Description for opening companion app. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setOpenCompanionAppDescription( - @Nullable String openCompanionAppDescription) { - mBuilderParcel.openCompanionAppDescription = openCompanionAppDescription; - return this; - } - - /** - * Set UpdateCompanionAppDescription. - * - * @param updateCompanionAppDescription Description for updating companion app. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setUpdateCompanionAppDescription( - @Nullable String updateCompanionAppDescription) { - mBuilderParcel.updateCompanionAppDescription = updateCompanionAppDescription; - return this; - } - - /** - * Set DownloadCompanionAppDescription. - * - * @param downloadCompanionAppDescription Description for downloading companion app. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setDownloadCompanionAppDescription( - @Nullable String downloadCompanionAppDescription) { - mBuilderParcel.downloadCompanionAppDescription = downloadCompanionAppDescription; - return this; - } - - /** - * Set UnableToConnectTitle. - * - * @param unableToConnectTitle Title when Fast Pair device is unable to be connected to. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setUnableToConnectTitle(@Nullable String unableToConnectTitle) { - mBuilderParcel.unableToConnectTitle = unableToConnectTitle; - return this; - } - - /** - * Set UnableToConnectDescription. - * - * @param unableToConnectDescription Description when Fast Pair device is unable to be - * connected to. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setUnableToConnectDescription( - @Nullable String unableToConnectDescription) { - mBuilderParcel.unableToConnectDescription = unableToConnectDescription; - return this; - } - - /** - * Set InitialPairingDescription. - * - * @param initialPairingDescription Description for Fast Pair initial pairing. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setInitialPairingDescription(@Nullable String initialPairingDescription) { - mBuilderParcel.initialPairingDescription = initialPairingDescription; - return this; - } - - /** - * Set ConnectSuccessCompanionAppInstalled. - * - * @param connectSuccessCompanionAppInstalled Description that let user open the companion - * app. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setConnectSuccessCompanionAppInstalled( - @Nullable String connectSuccessCompanionAppInstalled) { - mBuilderParcel.connectSuccessCompanionAppInstalled = - connectSuccessCompanionAppInstalled; - return this; - } - - /** - * Set ConnectSuccessCompanionAppNotInstalled. - * - * @param connectSuccessCompanionAppNotInstalled Description that let user download the - * companion app. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setConnectSuccessCompanionAppNotInstalled( - @Nullable String connectSuccessCompanionAppNotInstalled) { - mBuilderParcel.connectSuccessCompanionAppNotInstalled = - connectSuccessCompanionAppNotInstalled; - return this; - } - - /** - * Set SubsequentPairingDescription. - * - * @param subsequentPairingDescription Description that reminds user there is a paired - * device nearby. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setSubsequentPairingDescription( - @Nullable String subsequentPairingDescription) { - mBuilderParcel.subsequentPairingDescription = subsequentPairingDescription; - return this; - } - - /** - * Set RetroactivePairingDescription. - * - * @param retroactivePairingDescription Description that reminds users opt in their device. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setRetroactivePairingDescription( - @Nullable String retroactivePairingDescription) { - mBuilderParcel.retroactivePairingDescription = retroactivePairingDescription; - return this; - } - - /** - * Set WaitLaunchCompanionAppDescription. - * - * @param waitLaunchCompanionAppDescription Description that indicates companion app is - * about to launch. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setWaitLaunchCompanionAppDescription( - @Nullable String waitLaunchCompanionAppDescription) { - mBuilderParcel.waitLaunchCompanionAppDescription = - waitLaunchCompanionAppDescription; - return this; - } - - /** - * Set FailConnectGoToSettingsDescription. - * - * @param failConnectGoToSettingsDescription Description that indicates go to bluetooth - * settings when connection fail. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setFailConnectGoToSettingsDescription( - @Nullable String failConnectGoToSettingsDescription) { - mBuilderParcel.failConnectGoToSettingsDescription = - failConnectGoToSettingsDescription; - return this; - } - - /** - * Build {@link FastPairDeviceMetadata} with the currently set configuration. - * - * @hide - */ - @NonNull - public FastPairDeviceMetadata build() { - return new FastPairDeviceMetadata(mBuilderParcel); - } - } -} diff --git a/nearby/framework/java/android/nearby/FastPairDiscoveryItem.java b/nearby/framework/java/android/nearby/FastPairDiscoveryItem.java deleted file mode 100644 index d8dfe299688a48d62a17bc0fb3750555b0317a99..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairDiscoveryItem.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.nearby.aidl.FastPairDiscoveryItemParcel; - -/** - * Class for FastPairDiscoveryItem and its builder. - * - * @hide - */ -public class FastPairDiscoveryItem { - - FastPairDiscoveryItemParcel mMetadataParcel; - - FastPairDiscoveryItem( - FastPairDiscoveryItemParcel metadataParcel) { - this.mMetadataParcel = metadataParcel; - } - - /** - * Get Id. - * - * @hide - */ - @Nullable - public String getId() { - return mMetadataParcel.id; - } - - /** - * Get MacAddress. - * - * @hide - */ - @Nullable - public String getMacAddress() { - return mMetadataParcel.macAddress; - } - - /** - * Get ActionUrl. - * - * @hide - */ - @Nullable - public String getActionUrl() { - return mMetadataParcel.actionUrl; - } - - /** - * Get DeviceName. - * - * @hide - */ - @Nullable - public String getDeviceName() { - return mMetadataParcel.deviceName; - } - - /** - * Get Title. - * - * @hide - */ - @Nullable - public String getTitle() { - return mMetadataParcel.title; - } - - /** - * Get Description. - * - * @hide - */ - @Nullable - public String getDescription() { - return mMetadataParcel.description; - } - - /** - * Get DisplayUrl. - * - * @hide - */ - @Nullable - public String getDisplayUrl() { - return mMetadataParcel.displayUrl; - } - - /** - * Get LastObservationTimestampMillis. - * - * @hide - */ - public long getLastObservationTimestampMillis() { - return mMetadataParcel.lastObservationTimestampMillis; - } - - /** - * Get FirstObservationTimestampMillis. - * - * @hide - */ - public long getFirstObservationTimestampMillis() { - return mMetadataParcel.firstObservationTimestampMillis; - } - - /** - * Get State. - * - * @hide - */ - public int getState() { - return mMetadataParcel.state; - } - - /** - * Get ActionUrlType. - * - * @hide - */ - public int getActionUrlType() { - return mMetadataParcel.actionUrlType; - } - - /** - * Get Rssi. - * - * @hide - */ - public int getRssi() { - return mMetadataParcel.rssi; - } - - /** - * Get PendingAppInstallTimestampMillis. - * - * @hide - */ - public long getPendingAppInstallTimestampMillis() { - return mMetadataParcel.pendingAppInstallTimestampMillis; - } - - /** - * Get TxPower. - * - * @hide - */ - public int getTxPower() { - return mMetadataParcel.txPower; - } - - /** - * Get AppName. - * - * @hide - */ - @Nullable - public String getAppName() { - return mMetadataParcel.appName; - } - - /** - * Get PackageName. - * - * @hide - */ - @Nullable - public String getPackageName() { - return mMetadataParcel.packageName; - } - - /** - * Get TriggerId. - * - * @hide - */ - @Nullable - public String getTriggerId() { - return mMetadataParcel.triggerId; - } - - /** - * Get IconPng, which is submitted at device registration time to display on notification. It is - * a 32-bit PNG with dimensions of 512px by 512px. - * - * @return IconPng in 32-bit PNG with dimensions of 512px by 512px. - * @hide - */ - @Nullable - public byte[] getIconPng() { - return mMetadataParcel.iconPng; - } - - /** - * Get IconFifeUrl. - * - * @hide - */ - @Nullable - public String getIconFfeUrl() { - return mMetadataParcel.iconFifeUrl; - } - - /** - * Get authenticationPublicKeySecp256r1, which is same as AntiSpoof public key, see - * Data Format. - * - * @return 64-byte authenticationPublicKeySecp256r1. - * @hide - */ - @Nullable - public byte[] getAuthenticationPublicKeySecp256r1() { - return mMetadataParcel.authenticationPublicKeySecp256r1; - } - - /** - * Builder used to create FastPairDiscoveryItem. - * - * @hide - */ - public static final class Builder { - - private final FastPairDiscoveryItemParcel mBuilderParcel; - - /** - * Default constructor of Builder. - * - * @hide - */ - public Builder() { - mBuilderParcel = new FastPairDiscoveryItemParcel(); - } - - /** - * Set Id. - * - * @param id Unique id. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * - * @hide - */ - @NonNull - public Builder setId(@Nullable String id) { - mBuilderParcel.id = id; - return this; - } - - /** - * Set MacAddress. - * - * @param macAddress Fast Pair device rotating mac address. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setMacAddress(@Nullable String macAddress) { - mBuilderParcel.macAddress = macAddress; - return this; - } - - /** - * Set ActionUrl. - * - * @param actionUrl Action Url of Fast Pair device. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setActionUrl(@Nullable String actionUrl) { - mBuilderParcel.actionUrl = actionUrl; - return this; - } - - /** - * Set DeviceName. - * @param deviceName Fast Pair device name. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setDeviceName(@Nullable String deviceName) { - mBuilderParcel.deviceName = deviceName; - return this; - } - - /** - * Set Title. - * - * @param title Title of Fast Pair device. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTitle(@Nullable String title) { - mBuilderParcel.title = title; - return this; - } - - /** - * Set Description. - * - * @param description Description of Fast Pair device. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setDescription(@Nullable String description) { - mBuilderParcel.description = description; - return this; - } - - /** - * Set DisplayUrl. - * - * @param displayUrl Display Url of Fast Pair device. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setDisplayUrl(@Nullable String displayUrl) { - mBuilderParcel.displayUrl = displayUrl; - return this; - } - - /** - * Set LastObservationTimestampMillis. - * - * @param lastObservationTimestampMillis Last observed timestamp of Fast Pair device, keyed - * by a rotating id. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setLastObservationTimestampMillis( - long lastObservationTimestampMillis) { - mBuilderParcel.lastObservationTimestampMillis = lastObservationTimestampMillis; - return this; - } - - /** - * Set FirstObservationTimestampMillis. - * - * @param firstObservationTimestampMillis First observed timestamp of Fast Pair device, - * keyed by a rotating id. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setFirstObservationTimestampMillis( - long firstObservationTimestampMillis) { - mBuilderParcel.firstObservationTimestampMillis = firstObservationTimestampMillis; - return this; - } - - /** - * Set State. - * - * @param state Item's current state. e.g. if the item is blocked. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setState(int state) { - mBuilderParcel.state = state; - return this; - } - - /** - * Set ActionUrlType. - * - * @param actionUrlType The resolved url type for the action_url. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setActionUrlType(int actionUrlType) { - mBuilderParcel.actionUrlType = actionUrlType; - return this; - } - - /** - * Set Rssi. - * - * @param rssi Beacon's RSSI value. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setRssi(int rssi) { - mBuilderParcel.rssi = rssi; - return this; - } - - /** - * Set PendingAppInstallTimestampMillis. - * - * @param pendingAppInstallTimestampMillis The timestamp when the user is redirected to App - * Store after clicking on the item. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setPendingAppInstallTimestampMillis(long pendingAppInstallTimestampMillis) { - mBuilderParcel.pendingAppInstallTimestampMillis = pendingAppInstallTimestampMillis; - return this; - } - - /** - * Set TxPower. - * - * @param txPower Beacon's tx power. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTxPower(int txPower) { - mBuilderParcel.txPower = txPower; - return this; - } - - /** - * Set AppName. - * - * @param appName Human readable name of the app designated to open the uri. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setAppName(@Nullable String appName) { - mBuilderParcel.appName = appName; - return this; - } - - /** - * Set PackageName. - * - * @param packageName Package name of the App that owns this item. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setPackageName(@Nullable String packageName) { - mBuilderParcel.packageName = packageName; - return this; - } - - /** - * Set TriggerId. - * - * @param triggerId TriggerId identifies the trigger/beacon that is attached with a message. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setTriggerId(@Nullable String triggerId) { - mBuilderParcel.triggerId = triggerId; - return this; - } - - /** - * Set IconPng. - * - * @param iconPng Bytes of item icon in PNG format displayed in Discovery item list. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setIconPng(@Nullable byte[] iconPng) { - mBuilderParcel.iconPng = iconPng; - return this; - } - - /** - * Set IconFifeUrl. - * - * @param iconFifeUrl A FIFE URL of the item icon displayed in Discovery item list. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setIconFfeUrl(@Nullable String iconFifeUrl) { - mBuilderParcel.iconFifeUrl = iconFifeUrl; - return this; - } - - /** - * Set authenticationPublicKeySecp256r1, which is same as AntiSpoof public key, see - * Data Format - * - * @param authenticationPublicKeySecp256r1 64-byte Fast Pair device public key. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setAuthenticationPublicKeySecp256r1( - @Nullable byte[] authenticationPublicKeySecp256r1) { - mBuilderParcel.authenticationPublicKeySecp256r1 = authenticationPublicKeySecp256r1; - return this; - } - - /** - * Build {@link FastPairDiscoveryItem} with the currently set configuration. - * - * @hide - */ - @NonNull - public FastPairDiscoveryItem build() { - return new FastPairDiscoveryItem(mBuilderParcel); - } - } -} diff --git a/nearby/framework/java/android/nearby/FastPairEligibleAccount.java b/nearby/framework/java/android/nearby/FastPairEligibleAccount.java deleted file mode 100644 index 8be4cca8e8993e40ca5464a0af9df4c6214dd88d..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairEligibleAccount.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby; - -import android.accounts.Account; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.nearby.aidl.FastPairEligibleAccountParcel; - -/** - * Class for FastPairEligibleAccount and its builder. - * - * @hide - */ -public class FastPairEligibleAccount { - - FastPairEligibleAccountParcel mAccountParcel; - - FastPairEligibleAccount(FastPairEligibleAccountParcel accountParcel) { - this.mAccountParcel = accountParcel; - } - - /** - * Get Account. - * - * @hide - */ - @Nullable - public Account getAccount() { - return this.mAccountParcel.account; - } - - /** - * Get OptIn Status. - * - * @hide - */ - public boolean isOptIn() { - return this.mAccountParcel.optIn; - } - - /** - * Builder used to create FastPairEligibleAccount. - * - * @hide - */ - public static final class Builder { - - private final FastPairEligibleAccountParcel mBuilderParcel; - - /** - * Default constructor of Builder. - * - * @hide - */ - public Builder() { - mBuilderParcel = new FastPairEligibleAccountParcel(); - mBuilderParcel.account = null; - mBuilderParcel.optIn = false; - } - - /** - * Set Account. - * - * @param account Fast Pair eligible account. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setAccount(@Nullable Account account) { - mBuilderParcel.account = account; - return this; - } - - /** - * Set whether the account is opt into Fast Pair. - * - * @param optIn Whether the Fast Pair eligible account opts into Fast Pair. - * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. - * @hide - */ - @NonNull - public Builder setOptIn(boolean optIn) { - mBuilderParcel.optIn = optIn; - return this; - } - - /** - * Build {@link FastPairEligibleAccount} with the currently set configuration. - * - * @hide - */ - @NonNull - public FastPairEligibleAccount build() { - return new FastPairEligibleAccount(mBuilderParcel); - } - } -} diff --git a/nearby/framework/java/android/nearby/FastPairStatusCallback.java b/nearby/framework/java/android/nearby/FastPairStatusCallback.java deleted file mode 100644 index 1567828695a8e94217d4155d5617de3394f1f64e..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/FastPairStatusCallback.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby; - -import android.annotation.NonNull; - -/** - * Reports the pair status for an ongoing pair with a {@link FastPairDevice}. - * @hide - */ -public interface FastPairStatusCallback { - - /** Reports a pair status related metadata associated with a {@link FastPairDevice} */ - void onPairUpdate(@NonNull FastPairDevice fastPairDevice, - PairStatusMetadata pairStatusMetadata); -} diff --git a/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl b/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl deleted file mode 100644 index 2e6fc879261778a5b5fbe57008f3a85fbc0d3ec5..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2021 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.nearby; - -import android.content.Intent; -/** - * Provides callback interface for halfsheet to send FastPair call back. - * - * {@hide} - */ -interface IFastPairHalfSheetCallback { - void onHalfSheetConnectionConfirm(in Intent intent); - } \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/PairStatusMetadata.aidl b/nearby/framework/java/android/nearby/PairStatusMetadata.aidl deleted file mode 100644 index 911a30089f25188dcfb0fe58a3a5a9a989181544..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/PairStatusMetadata.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2022, 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.nearby; - -/** - * Metadata about an ongoing paring. Wraps transient data like status and progress. - * - * @hide - */ -parcelable PairStatusMetadata; diff --git a/nearby/framework/java/android/nearby/PairStatusMetadata.java b/nearby/framework/java/android/nearby/PairStatusMetadata.java deleted file mode 100644 index 438cd6b46fc0096fe0d22ce30252a4c5f07f7bc0..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/PairStatusMetadata.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * Metadata about an ongoing paring. Wraps transient data like status and progress. - * - * @hide - */ -public final class PairStatusMetadata implements Parcelable { - - @Status - private final int mStatus; - - /** The status of the pairing. */ - @IntDef({ - Status.UNKNOWN, - Status.SUCCESS, - Status.FAIL, - Status.DISMISS - }) - public @interface Status { - int UNKNOWN = 1000; - int SUCCESS = 1001; - int FAIL = 1002; - int DISMISS = 1003; - } - - /** Converts the status to readable string. */ - public static String statusToString(@Status int status) { - switch (status) { - case Status.SUCCESS: - return "SUCCESS"; - case Status.FAIL: - return "FAIL"; - case Status.DISMISS: - return "DISMISS"; - case Status.UNKNOWN: - default: - return "UNKNOWN"; - } - } - - public int getStatus() { - return mStatus; - } - - @Override - public String toString() { - return "PairStatusMetadata[ status=" + statusToString(mStatus) + "]"; - } - - @Override - public boolean equals(Object other) { - if (other instanceof PairStatusMetadata) { - return mStatus == ((PairStatusMetadata) other).mStatus; - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(mStatus); - } - - public PairStatusMetadata(@Status int status) { - mStatus = status; - } - - public static final Creator CREATOR = new Creator() { - @Override - public PairStatusMetadata createFromParcel(Parcel in) { - return new PairStatusMetadata(in.readInt()); - } - - @Override - public PairStatusMetadata[] newArray(int size) { - return new PairStatusMetadata[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public int getStability() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mStatus); - } -} diff --git a/nearby/framework/java/android/nearby/aidl/ByteArrayParcel.aidl b/nearby/framework/java/android/nearby/aidl/ByteArrayParcel.aidl deleted file mode 100644 index 53c73bdf6ee3947704f5cbf16ab4d79afa569d5b..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/ByteArrayParcel.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -/** - * This is to support 2D byte arrays. - * {@hide} - */ -parcelable ByteArrayParcel { - byte[] byteArray; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl deleted file mode 100644 index fc3ba22e0873fe17f6f3949165b794e8f482720d..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairAccountDevicesMetadataRequestParcel.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -import android.accounts.Account; -import android.nearby.aidl.ByteArrayParcel; - -/** - * Request details for Metadata of Fast Pair devices associated with an account. - * {@hide} - */ -parcelable FastPairAccountDevicesMetadataRequestParcel { - Account account; - ByteArrayParcel[] deviceAccountKeys; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAccountKeyDeviceMetadataParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAccountKeyDeviceMetadataParcel.aidl deleted file mode 100644 index 80143232af215eceedcf06439d91b36463515a9d..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairAccountKeyDeviceMetadataParcel.aidl +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -import android.nearby.aidl.FastPairDeviceMetadataParcel; -import android.nearby.aidl.FastPairDiscoveryItemParcel; - -/** - * Metadata of a Fast Pair device associated with an account. - * {@hide} - */ - // TODO(b/204780849): remove unnecessary fields and polish comments. -parcelable FastPairAccountKeyDeviceMetadataParcel { - // Key of the Fast Pair device associated with the account. - byte[] deviceAccountKey; - // Hash function of device account key and public bluetooth address. - byte[] sha256DeviceAccountKeyPublicAddress; - // Fast Pair device metadata for the Fast Pair device. - FastPairDeviceMetadataParcel metadata; - // Fast Pair discovery item tied to both the Fast Pair device and the - // account. - FastPairDiscoveryItemParcel discoveryItem; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataParcel.aidl deleted file mode 100644 index 4fd4d4b83ffd58dabea40617bec559b06ec0d629..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataParcel.aidl +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -import android.nearby.aidl.FastPairDeviceMetadataParcel; - -/** - * Metadata of a Fast Pair device keyed by AntispoofKey, - * Used by initial pairing without account association. - * - * {@hide} - */ -parcelable FastPairAntispoofKeyDeviceMetadataParcel { - // Anti-spoof public key. - byte[] antispoofPublicKey; - - // Fast Pair device metadata for the Fast Pair device. - FastPairDeviceMetadataParcel deviceMetadata; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataRequestParcel.aidl deleted file mode 100644 index afdcf154aa604be30aacb6c7a91b7b24df5faef6..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairAntispoofKeyDeviceMetadataRequestParcel.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -/** - * Request details for metadata of a Fast Pair device keyed by either - * antispoofKey or modelId. - * {@hide} - */ -parcelable FastPairAntispoofKeyDeviceMetadataRequestParcel { - byte[] modelId; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairDeviceMetadataParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairDeviceMetadataParcel.aidl deleted file mode 100644 index d90f6a1d8b5e219e580f8308731bc6e4c65aa96b..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairDeviceMetadataParcel.aidl +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -/** - * Fast Pair Device Metadata for a given device model ID. - * @hide - */ -// TODO(b/204780849): remove unnecessary fields and polish comments. -parcelable FastPairDeviceMetadataParcel { - // The image to show on the notification. - String imageUrl; - - // The intent that will be launched via the notification. - String intentUri; - - // The transmit power of the device's BLE chip. - int bleTxPower; - - // The distance that the device must be within to show a notification. - // If no distance is set, we default to 0.6 meters. Only Nearby admins can - // change this. - float triggerDistance; - - // The image icon that shows in the notification. - byte[] image; - - // The name of the device. - String name; - - int deviceType; - - // The image urls for device with device type "true wireless". - String trueWirelessImageUrlLeftBud; - String trueWirelessImageUrlRightBud; - String trueWirelessImageUrlCase; - - // The notification description for when the device is initially discovered. - String initialNotificationDescription; - - // The notification description for when the device is initially discovered - // and no account is logged in. - String initialNotificationDescriptionNoAccount; - - // The notification description for once we have finished pairing and the - // companion app has been opened. For Bisto devices, this String will point - // users to setting up the assistant. - String openCompanionAppDescription; - - // The notification description for once we have finished pairing and the - // companion app needs to be updated before use. - String updateCompanionAppDescription; - - // The notification description for once we have finished pairing and the - // companion app needs to be installed. - String downloadCompanionAppDescription; - - // The notification title when a pairing fails. - String unableToConnectTitle; - - // The notification summary when a pairing fails. - String unableToConnectDescription; - - // The description that helps user initially paired with device. - String initialPairingDescription; - - // The description that let user open the companion app. - String connectSuccessCompanionAppInstalled; - - // The description that let user download the companion app. - String connectSuccessCompanionAppNotInstalled; - - // The description that reminds user there is a paired device nearby. - String subsequentPairingDescription; - - // The description that reminds users opt in their device. - String retroactivePairingDescription; - - // The description that indicates companion app is about to launch. - String waitLaunchCompanionAppDescription; - - // The description that indicates go to bluetooth settings when connection - // fail. - String failConnectGoToSettingsDescription; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairDiscoveryItemParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairDiscoveryItemParcel.aidl deleted file mode 100644 index 2cc2daad00a68a86d5190c619b55aaaa21f9a528..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairDiscoveryItemParcel.aidl +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -/** - * Fast Pair Discovery Item. - * @hide - */ -// TODO(b/204780849): remove unnecessary fields and polish comments. -parcelable FastPairDiscoveryItemParcel { - // Offline item: unique ID generated on client. - // Online item: unique ID generated on server. - String id; - - // The most recent all upper case mac associated with this item. - // (Mac-to-DiscoveryItem is a many-to-many relationship) - String macAddress; - - String actionUrl; - - // The bluetooth device name from advertisement - String deviceName; - - // Item's title - String title; - - // Item's description. - String description; - - // The URL for display - String displayUrl; - - // Client timestamp when the beacon was last observed in BLE scan. - long lastObservationTimestampMillis; - - // Client timestamp when the beacon was first observed in BLE scan. - long firstObservationTimestampMillis; - - // Item's current state. e.g. if the item is blocked. - int state; - - // The resolved url type for the action_url. - int actionUrlType; - - // The timestamp when the user is redirected to Play Store after clicking on - // the item. - long pendingAppInstallTimestampMillis; - - // Beacon's RSSI value - int rssi; - - // Beacon's tx power - int txPower; - - // Human readable name of the app designated to open the uri - // Used in the second line of the notification, "Open in {} app" - String appName; - - // Package name of the App that owns this item. - String packageName; - - // TriggerId identifies the trigger/beacon that is attached with a message. - // It's generated from server for online messages to synchronize formatting - // across client versions. - // Example: - // * BLE_UID: 3||deadbeef - // * BLE_URL: http://trigger.id - // See go/discovery-store-message-and-trigger-id for more details. - String triggerId; - - // Bytes of item icon in PNG format displayed in Discovery item list. - byte[] iconPng; - - // A FIFE URL of the item icon displayed in Discovery item list. - String iconFifeUrl; - - // Fast Pair antispoof key. - byte[] authenticationPublicKeySecp256r1; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairEligibleAccountParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairEligibleAccountParcel.aidl deleted file mode 100644 index 747758d03277960a8756557d22c3328057a2b21f..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairEligibleAccountParcel.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -import android.accounts.Account; - -/** - * Fast Pair Eligible Account. - * {@hide} - */ -parcelable FastPairEligibleAccountParcel { - Account account; - // Whether the account opts in Fast Pair. - boolean optIn; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairEligibleAccountsRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairEligibleAccountsRequestParcel.aidl deleted file mode 100644 index 8db335602474bbf4b8abe6d84ac4c8f90bfb92ed..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairEligibleAccountsRequestParcel.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -/** - * Request details for Fast Pair eligible accounts. - * Empty place holder for future expansion. - * {@hide} - */ -parcelable FastPairEligibleAccountsRequestParcel { -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairManageAccountDeviceRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairManageAccountDeviceRequestParcel.aidl deleted file mode 100644 index 59834b2839fa1b167d4bf040241e7b4371fb2eb9..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairManageAccountDeviceRequestParcel.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -import android.accounts.Account; -import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel; - -/** - * Request details for managing Fast Pair device-account mapping. - * {@hide} - */ - // TODO(b/204780849): remove unnecessary fields and polish comments. -parcelable FastPairManageAccountDeviceRequestParcel { - Account account; - // MANAGE_ACCOUNT_DEVICE_ADD: add Fast Pair device to the account. - // MANAGE_ACCOUNT_DEVICE_REMOVE: remove Fast Pair device from the account. - int requestType; - // Fast Pair account key-ed device metadata. - FastPairAccountKeyDeviceMetadataParcel accountKeyDeviceMetadata; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/FastPairManageAccountRequestParcel.aidl b/nearby/framework/java/android/nearby/aidl/FastPairManageAccountRequestParcel.aidl deleted file mode 100644 index 3d9206464dc7247c4e534faa45284e2c3bcafb44..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/FastPairManageAccountRequestParcel.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2021 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.nearby.aidl; - -import android.accounts.Account; - -/** - * Request details for managing a Fast Pair account. - * - * {@hide} - */ -parcelable FastPairManageAccountRequestParcel { - Account account; - // MANAGE_ACCOUNT_OPT_IN: opt account into Fast Pair. - // MANAGE_ACCOUNT_OPT_OUT: opt account out of Fast Pair. - int requestType; -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairAccountDevicesMetadataCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairAccountDevicesMetadataCallback.aidl deleted file mode 100644 index 7db18d00106503c2fd656e80f4434c89cad2b676..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairAccountDevicesMetadataCallback.aidl +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel; - -/** - * Provides callback interface for OEMs to send back metadata of FastPair - * devices associated with an account. - * - * {@hide} - */ -interface IFastPairAccountDevicesMetadataCallback { - void onFastPairAccountDevicesMetadataReceived(in FastPairAccountKeyDeviceMetadataParcel[] accountDevicesMetadata); - void onError(int code, String message); -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofKeyDeviceMetadataCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofKeyDeviceMetadataCallback.aidl deleted file mode 100644 index 38abba4fb1baccfc66a899e01d42b0f19792984b..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairAntispoofKeyDeviceMetadataCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel; - -/** - * Provides callback interface for OEMs to send FastPair AntispoofKey Device metadata back. - * - * {@hide} - */ -interface IFastPairAntispoofKeyDeviceMetadataCallback { - void onFastPairAntispoofKeyDeviceMetadataReceived(in FastPairAntispoofKeyDeviceMetadataParcel metadata); - void onError(int code, String message); -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl deleted file mode 100644 index 295621188c15337d7d871e5847c45df91c6b9592..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairDataProvider.aidl +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel; -import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback; -import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel; -import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback; -import android.nearby.aidl.FastPairEligibleAccountsRequestParcel; -import android.nearby.aidl.IFastPairEligibleAccountsCallback; -import android.nearby.aidl.FastPairManageAccountRequestParcel; -import android.nearby.aidl.IFastPairManageAccountCallback; -import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel; -import android.nearby.aidl.IFastPairManageAccountDeviceCallback; - -/** - * Interface for communicating with the fast pair providers. - * - * {@hide} - */ -oneway interface IFastPairDataProvider { - void loadFastPairAntispoofKeyDeviceMetadata(in FastPairAntispoofKeyDeviceMetadataRequestParcel request, - in IFastPairAntispoofKeyDeviceMetadataCallback callback); - void loadFastPairAccountDevicesMetadata(in FastPairAccountDevicesMetadataRequestParcel request, - in IFastPairAccountDevicesMetadataCallback callback); - void loadFastPairEligibleAccounts(in FastPairEligibleAccountsRequestParcel request, - in IFastPairEligibleAccountsCallback callback); - void manageFastPairAccount(in FastPairManageAccountRequestParcel request, - in IFastPairManageAccountCallback callback); - void manageFastPairAccountDevice(in FastPairManageAccountDeviceRequestParcel request, - in IFastPairManageAccountDeviceCallback callback); -} diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairEligibleAccountsCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairEligibleAccountsCallback.aidl deleted file mode 100644 index 9990014d405af74e72e2c144fc184bda986d3df3..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairEligibleAccountsCallback.aidl +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -import android.accounts.Account; -import android.nearby.aidl.FastPairEligibleAccountParcel; - -/** - * Provides callback interface for OEMs to return FastPair Eligible accounts. - * - * {@hide} - */ -interface IFastPairEligibleAccountsCallback { - void onFastPairEligibleAccountsReceived(in FastPairEligibleAccountParcel[] accounts); - void onError(int code, String message); - } \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairManageAccountCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairManageAccountCallback.aidl deleted file mode 100644 index 6b4aaee0b64b07209d6062700543c35fb84b44c3..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairManageAccountCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -/** - * Provides callback interface to send response for account management request. - * - * {@hide} - */ -interface IFastPairManageAccountCallback { - void onSuccess(); - void onError(int code, String message); -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairManageAccountDeviceCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairManageAccountDeviceCallback.aidl deleted file mode 100644 index bffc533e53db2f436576b2363318711ccf1d0504..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairManageAccountDeviceCallback.aidl +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2021 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.nearby.aidl; - -/** - * Provides callback interface to send response for account-device mapping - * management request. - * - * {@hide} - */ -interface IFastPairManageAccountDeviceCallback { - void onSuccess(); - void onError(int code, String message); -} \ No newline at end of file diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairStatusCallback.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairStatusCallback.aidl deleted file mode 100644 index d844c06f4041282fa590d475f203cac33708c06a..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairStatusCallback.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2022, 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.nearby.aidl; - -import android.nearby.FastPairDevice; -import android.nearby.PairStatusMetadata; - -/** - * - * Provides callbacks for Fast Pair foreground activity to learn about paring status from backend. - * - * {@hide} - */ -interface IFastPairStatusCallback { - - /** Reports a pair status related metadata associated with a {@link FastPairDevice} */ - void onPairUpdate(in FastPairDevice fastPairDevice, in PairStatusMetadata pairStatusMetadata); -} diff --git a/nearby/framework/java/android/nearby/aidl/IFastPairUiService.aidl b/nearby/framework/java/android/nearby/aidl/IFastPairUiService.aidl deleted file mode 100644 index 9200a9d25440e1e2e90e0bb78f5f5e54c5a6259c..0000000000000000000000000000000000000000 --- a/nearby/framework/java/android/nearby/aidl/IFastPairUiService.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2022, 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.nearby.aidl; - -import android.nearby.aidl.IFastPairStatusCallback; -import android.nearby.FastPairDevice; - -/** - * 0p API for controlling Fast Pair. Used to talk between foreground activities - * and background services. - * - * {@hide} - */ -interface IFastPairUiService { - - void registerCallback(in IFastPairStatusCallback fastPairStatusCallback); - - void unregisterCallback(in IFastPairStatusCallback fastPairStatusCallback); - - void connect(in FastPairDevice fastPairDevice); - - void cancel(in FastPairDevice fastPairDevice); -} \ No newline at end of file diff --git a/nearby/halfsheet/Android.bp b/nearby/halfsheet/Android.bp deleted file mode 100644 index 8011dc64e3d0a37a0991a7b713a7b1abe01afc8f..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/Android.bp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2021 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_app { - name: "HalfSheetUX", - defaults: ["platform_app_defaults"], - srcs: ["src/**/*.java"], - sdk_version: "module_current", - // This is included in tethering apex, which uses min SDK 30 - min_sdk_version: "30", - updatable: true, - certificate: ":com.android.nearby.halfsheetcertificate", - libs: [ - "framework-bluetooth", - "framework-connectivity-t.impl", - "nearby-service-string", - ], - static_libs: [ - "androidx.annotation_annotation", - "androidx.fragment_fragment", - "androidx-constraintlayout_constraintlayout", - "androidx.localbroadcastmanager_localbroadcastmanager", - "androidx.core_core", - "androidx.appcompat_appcompat", - "androidx.recyclerview_recyclerview", - "androidx.lifecycle_lifecycle-runtime", - "androidx.lifecycle_lifecycle-extensions", - "com.google.android.material_material", - "fast-pair-lite-protos", - ], - manifest: "AndroidManifest.xml", - jarjar_rules: ":nearby-jarjar-rules", - apex_available: ["com.android.tethering",], - lint: { strict_updatability_linting: true } -} - -android_app_certificate { - name: "com.android.nearby.halfsheetcertificate", - certificate: "apk-certs/com.android.nearby.halfsheet" -} diff --git a/nearby/halfsheet/AndroidManifest.xml b/nearby/halfsheet/AndroidManifest.xml deleted file mode 100644 index 22987fb2a0e0533d1fbe11fe629b34d3896abb8d..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/AndroidManifest.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - diff --git a/nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.pk8 b/nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.pk8 deleted file mode 100644 index 187d51e6754399d45cb90c805151ef495a92f30d..0000000000000000000000000000000000000000 Binary files a/nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.pk8 and /dev/null differ diff --git a/nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.x509.pem b/nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.x509.pem deleted file mode 100644 index 440c524a7f90a9dc8e317badfb0cc34fa2861870..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF6zCCA9OgAwIBAgIUU5ATKevcNA5ZSurwgwGenwrr4c4wDQYJKoZIhvcNAQEL -BQAwgYMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQwwCgYDVQQH -DANNVFYxDzANBgNVBAoMBkdvb2dsZTEPMA0GA1UECwwGbmVhcmJ5MQswCQYDVQQD -DAJ3czEiMCAGCSqGSIb3DQEJARYTd2VpY2VzdW5AZ29vZ2xlLmNvbTAgFw0yMTEy -MDgwMTMxMzFaGA80NzU5MTEwNDAxMzEzMVowgYMxCzAJBgNVBAYTAlVTMRMwEQYD -VQQIDApDYWxpZm9ybmlhMQwwCgYDVQQHDANNVFYxDzANBgNVBAoMBkdvb2dsZTEP -MA0GA1UECwwGbmVhcmJ5MQswCQYDVQQDDAJ3czEiMCAGCSqGSIb3DQEJARYTd2Vp -Y2VzdW5AZ29vZ2xlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -AO0JW1YZ5bKHZG5B9eputz3kGREmXcWZ97dg/ODDs3+op4ulBmgaYeo5yeCy29GI -Sjgxo4G+9fNZ7Fejrk5/LLWovAoRvVxnkRxCkTfp15jZpKNnZjT2iTRLXzNz2O04 -cC0jB81mu5vJ9a8pt+EQkuSwjDMiUi6q4Sf6IRxtTCd5a1yn9eHf1y2BbCmU+Eys -bs97HJl9PgMCp7hP+dYDxEtNTAESg5IpJ1i7uINgPNl8d0tvJ9rOEdy0IcdeGwt/ -t0L9fIoRCePttH+idKIyDjcNyp9WtX2/wZKlsGap83rGzLdL2PI4DYJ2Ytmy8W3a -9qFJNrhl3Q3BYgPlcCg9qQOIKq6ZJgFFH3snVDKvtSFd8b9ofK7UzD5g2SllTqDA -4YvrdK4GETQunSjG7AC/2PpvN/FdhHm7pBi0fkgwykMh35gv0h8mmb6pBISYgr85 -+GMBilNiNJ4G6j3cdOa72pvfDW5qn5dn5ks8cIgW2X1uF/GT8rR6Mb2rwhjY9eXk -TaP0RykyzheMY/7dWeA/PdN3uMCEJEt72ZakDIswgQVPCIw8KQPIf6pl0d5hcLSV -QzhqBaXudseVg0QlZ86iaobpZvCrW0KqQmMU5GVhEtDc2sPe5e+TCmUC/H+vo8F8 -1UYu3MJaBcpePFlgIsLhW0niUTfCq2FiNrPykOJT7U9NAgMBAAGjUzBRMB0GA1Ud -DgQWBBQKSepRcKTv9hr8mmKjYCL7NeG2izAfBgNVHSMEGDAWgBQKSepRcKTv9hr8 -mmKjYCL7NeG2izAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQC/ -BoItafzvjYPzENY16BIkgRqJVU7IosWxGLczzg19NFu6HPa54alqkawp7RI1ZNVH -bJjQma5ap0L+Y06/peIU9rvEtfbCkkYJwvIaSRlTlzrNwNEcj3yJMmGTr/wfIzq8 -PN1t0hihnqI8ZguOPC+sV6ARoC+ygkwaLU1oPbVvOGz9WplvSokE1mvtqKAyuDoL -LZfWwbhxRAgwgCIEz6cPfEcgg3Xzc+L4OzmNhTTc7GNOAtvvW7Zqc2Lohb8nQMNw -uY65yiHPNmjmc+xLHZk3jQg82tKv792JJRkVXPsIfQV087IzxFFjjvKy82rVfeaN -F9g2EpUvdjtm8zx7K5tiDv9Es/Up7oOnoB5baLgnMAEVMTZY+4k/6BfVM5CVUu+H -AO1yh2yeNWbzY8B+zxRef3C2Ax68lJHFyz8J1pfrGpWxML3rDmWiVDMtEk73t3g+ -lcyLYo7OW+iBn6BODRcINO4R640oyMjFz2wPSPAsU0Zj/MbgC6iaS+goS3QnyPQS -O3hKWfwqQuA7BZ0la1n+plKH5PKxQESAbd37arzCsgQuktl33ONiwYOt6eUyHl/S -E3ZdldkmGm9z0mcBYG9NczDBSYmtuZOGjEzIRqI5GFD2WixE+dqTzVP/kyBd4BLc -OTmBynN/8D/qdUZNrT+tgs+mH/I2SsKYW9Zymwf7Qw== ------END CERTIFICATE----- diff --git a/nearby/halfsheet/apk-certs/key.pem b/nearby/halfsheet/apk-certs/key.pem deleted file mode 100644 index e9f4288aa08836c944505ea3486a7b198854a674..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/apk-certs/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDtCVtWGeWyh2Ru -QfXqbrc95BkRJl3Fmfe3YPzgw7N/qKeLpQZoGmHqOcngstvRiEo4MaOBvvXzWexX -o65Ofyy1qLwKEb1cZ5EcQpE36deY2aSjZ2Y09ok0S18zc9jtOHAtIwfNZrubyfWv -KbfhEJLksIwzIlIuquEn+iEcbUwneWtcp/Xh39ctgWwplPhMrG7PexyZfT4DAqe4 -T/nWA8RLTUwBEoOSKSdYu7iDYDzZfHdLbyfazhHctCHHXhsLf7dC/XyKEQnj7bR/ -onSiMg43DcqfVrV9v8GSpbBmqfN6xsy3S9jyOA2CdmLZsvFt2vahSTa4Zd0NwWID -5XAoPakDiCqumSYBRR97J1Qyr7UhXfG/aHyu1Mw+YNkpZU6gwOGL63SuBhE0Lp0o -xuwAv9j6bzfxXYR5u6QYtH5IMMpDId+YL9IfJpm+qQSEmIK/OfhjAYpTYjSeBuo9 -3HTmu9qb3w1uap+XZ+ZLPHCIFtl9bhfxk/K0ejG9q8IY2PXl5E2j9EcpMs4XjGP+ -3VngPz3Td7jAhCRLe9mWpAyLMIEFTwiMPCkDyH+qZdHeYXC0lUM4agWl7nbHlYNE -JWfOomqG6Wbwq1tCqkJjFORlYRLQ3NrD3uXvkwplAvx/r6PBfNVGLtzCWgXKXjxZ -YCLC4VtJ4lE3wqthYjaz8pDiU+1PTQIDAQABAoICAQCt4R5CM+8enlka1IIbvann -2cpVnUpOaNqhh6EZFBY5gDOfqafgd/H5yvh/P1UnCI5BWJBz3ew33nAT/fsglAPt -ImEGFetNvJ9jFqXGWWCRPJ6cS35bPbp6RQwKB2JK6grH4ZmYoFLhPi5elwDPNcQ7 -xBKkc/nLSAiwtbjSTI7/qf8K0h752aTUOctpWWEnhZon00ywf4Ic3TbBatF/n/W/ -s20coEMp1cyKN/JrVQ5uD/LGwDyBModB2lWpFSxLrB14I9DWyxbxP28X7ckXLhbl -ZdWMOyQZoa/S7n5PYT49g1Wq5BW54UpvuH5c6fpWtrgSqk1cyUR2EbTf3NAAhPLU -PgPK8wbFMcMB3TpQDXl7USA7QX5wSv22OfhivPsHQ9szGM0f84mK0PhXYPWBiNUY -Y8rrIjOijB4eFGDFnTIMTofAb07NxRThci710BYUqgBVTBG5N+avIesjwkikMjOI -PwYukKSQSw/Tqxy5Z9l22xksGynBZFjEFs/WT5pDczPAktA4xW3CGxjkMsIYaOBs -OCEujqc5+mHSywYvy8aN+nA+yPucJP5e5pLZ1qaU0tqyakCx8XeeOyP6Wfm3UAAV -AYelBRcWcJxM51w4o5UnUnpBD+Uxiz1sRVlqa9bLJjP4M+wJNL+WaIn9D6WhPOvl -+naDC+p29ou2JzyKFDsOQQKCAQEA+Jalm+xAAPc+t/gCdAqEDo0NMA2/NG8m9BRc -CVZRRaWVyGPeg5ziT/7caGwy2jpOZEjK0OOTCAqF+sJRDj6DDIw7nDrlxNyaXnCF -gguQHFIYaHcjKGTs5l0vgL3H7pMFHN2qVynf4xrTuBXyT1GJ4vdWKAJbooa02c8W -XI2fjwZ7Y8wSWrm1tn3oTTBR3N6o1GyPY6/TrL0mhpWwgx5eJeLl3GuUxOhXY5R9 -y48ziS97Dqdq75MxUOHickofCNcm7p+jA8Hg+SxLMR/kUFsXOxawmvsBqdL1XzU5 -LTS7xAEY9iMuBcO6yIxcxqBx96idjsPXx1lgARo1CpaZYCzgPQKCAQEA9BqKMN/Y -o+T+ac99St8x3TYkk5lkvLVqlPw+EQhEqrm9EEBPntxWM5FEIpPVmFm7taGTgPfN -KKaaNxX5XyK9B2v1QqN7XrX0nF4+6x7ao64fdpRUParIuBVctqzQWWthme66eHrf -L86T/tkt3o/7p+Hd4Z9UT3FaAew1ggWr00xz5PJ/4b3f3mRmtNmgeTYskWMxOpSj -bEenom4Row7sfLNeXNSWDGlzJ/lf6svvbVM2X5h2uFsxlt/Frq9ooTA3wwhnbd1i -cFifDQ6cxF5mBpz/V/hnlHVfuXlknEZa9EQXHNo/aC9y+bR+ai05FJyK/WgqleW8 -5PBmoTReWA2MUQKCAQAnnnLkh+GnhcBEN83ESszDOO3KI9a+d5yguAH3Jv+q9voJ -Rwl2tnFHSJo+NkhgiXxm9UcFxc9wL6Us0v1yJLpkLJFvk9984Z/kv1A36rncGaV0 -ONCspnEvQdjJTvXnax0cfaOhYrYhDuyBYVYOGDO+rabYl4+dNpTqRdwNgjDU7baK -sEKYnRJ99FEqxDG33vDPckHkJGi7FiZmusK4EwX0SdZSq/6450LORyNJZxhSm/Oj -4UDkz/PDLU0W5ANQOGInE+A6QBMoA0w0lx2fRPVN4I7jFHAubcXXl7b2InpugbJF -wFOcbZZ+UgiTS4z+aKw7zbC9P9xSMKgVeO0W6/ANAoIBABe0LA8q7YKczgfAWk5W -9iShCVQ75QheJYdqJyzIPMLHXpChbhnjE4vWY2NoL6mnrQ6qLgSsC4QTCY6n15th -aDG8Tgi2j1hXGvXEQR/b0ydp1SxSowuJ9gvKJ0Kl7WWBg+zKvdjNNbcSvFRXCpk+ -KhXXXRB3xFwiibb+FQQXQOQ33FkzIy/snDygS0jsiSS8Gf/UPgeOP4BYRPME9Tl8 -TYKeeF9TVW7HHqOXF7VZMFrRZcpKp9ynHl2kRTH9Xo+oewG5YzHL+a8nK+q8rIR1 -Fjs2K6WDPauw6ia8nwR94H8vzX7Dwrx/Pw74c/4jfhN+UBDjeJ8tu/YPUif9SdwL -FMECggEALdCGKfQ4vPmqI6UdfVB5hdCPoM6tUsI2yrXFvlHjSGVanC/IG9x2mpRb -4odamLYx4G4NjP1IJSY08LFT9VhLZtRM1W3fGeboW12LTEVNrI3lRBU84rAQ1ced -l6/DvTKJjhfwTxb/W7sqmZY5hF3QuNxs67Z8x0pe4b58musa0qFCs4Sa8qTNZKRW -fIbxIKuvu1HSNOKkZLu6Gq8km+XIlVAaSVA03Tt+EK74MFL6+pcd7/VkS00MAYUC -gS4ic+QFzCl5P8zl/GoX8iUFsRZQCSJkZ75VwO13pEupVwCAW8WWJO83U4jBsnJs -ayrX7pbsnW6jsNYBUlck+RYVYkVkxA== ------END PRIVATE KEY----- diff --git a/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_enter.xml b/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_enter.xml deleted file mode 100644 index 098dccbf503d2ecd4092be40adabff3bad328e0f..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_enter.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_exit.xml b/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_exit.xml deleted file mode 100644 index 1cf7401f76a33f027b8c2c5cbb82ed50bd95cde6..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_exit.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml b/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml deleted file mode 100644 index 9a51ddbe40130f417348607e8099875f89c33796..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml b/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml deleted file mode 100644 index c589482c33aae451b3e5a99cf7ac58da40dd5f5d..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - diff --git a/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml b/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml deleted file mode 100644 index 7d61d1c970c0dbeddd49081ae8be03f2844e37fc..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/drawable/fast_pair_ic_info.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - \ No newline at end of file diff --git a/nearby/halfsheet/res/drawable/fastpair_outline.xml b/nearby/halfsheet/res/drawable/fastpair_outline.xml deleted file mode 100644 index 6765e1194c97fae256e48b4ae8e5eeb67eee19db..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/drawable/fastpair_outline.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/nearby/halfsheet/res/drawable/half_sheet_bg.xml b/nearby/halfsheet/res/drawable/half_sheet_bg.xml deleted file mode 100644 index 7e7d8ddaeb33d026f7f02984f3d47151a0b02179..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/drawable/half_sheet_bg.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml b/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml deleted file mode 100644 index 7fbe229cfde85c9a74a938f04a020b47d39ad690..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nearby/halfsheet/res/layout/fast_pair_half_sheet.xml b/nearby/halfsheet/res/layout/fast_pair_half_sheet.xml deleted file mode 100644 index 705aa1b15804a16b2145f9a0480e29958e8e9df5..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/layout/fast_pair_half_sheet.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification.xml b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification.xml deleted file mode 100644 index 11b83434013b4f5e562a61d685eeaa8ed3671343..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_large_image.xml b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_large_image.xml deleted file mode 100644 index dd289477eab92ce82271cf78ab57573cc3270c16..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_large_image.xml +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_small_image.xml b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_small_image.xml deleted file mode 100644 index ee1d89f59bdf374cdd45de222be6d734e7432263..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_small_image.xml +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/nearby/halfsheet/res/values-af/strings.xml b/nearby/halfsheet/res/values-af/strings.xml deleted file mode 100644 index 7333e63a2f55fd606f93442f98ff8c4ebc3d9013..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-af/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Begin tans opstelling …" - "Stel toestel op" - "Toestel is gekoppel" - "Kon nie koppel nie" - "Klaar" - "Stoor" - "Koppel" - "Stel op" - "Instellings" - diff --git a/nearby/halfsheet/res/values-am/strings.xml b/nearby/halfsheet/res/values-am/strings.xml deleted file mode 100644 index da3b14494b8b578f73c254d09cb7c8e0f25f1944..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-am/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ማዋቀርን በመጀመር ላይ…" - "መሣሪያ አዋቅር" - "መሣሪያ ተገናኝቷል" - "መገናኘት አልተቻለም" - "ተጠናቅቋል" - "አስቀምጥ" - "አገናኝ" - "አዋቅር" - "ቅንብሮች" - diff --git a/nearby/halfsheet/res/values-ar/strings.xml b/nearby/halfsheet/res/values-ar/strings.xml deleted file mode 100644 index d0bfce4c3ba5379ad0895639774b9ed6beb05cd7..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ar/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "جارٍ الإعداد…" - "إعداد جهاز" - "تمّ إقران الجهاز" - "تعذّر الربط" - "تم" - "حفظ" - "ربط" - "إعداد" - "الإعدادات" - diff --git a/nearby/halfsheet/res/values-as/strings.xml b/nearby/halfsheet/res/values-as/strings.xml deleted file mode 100644 index 8ff49462b4a9085f8af51e17fcfed2569b66ff11..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-as/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ছেটআপ আৰম্ভ কৰি থকা হৈছে…" - "ডিভাইচ ছেট আপ কৰক" - "ডিভাইচ সংযোগ কৰা হ’ল" - "সংযোগ কৰিব পৰা নগ’ল" - "হ’ল" - "ছেভ কৰক" - "সংযোগ কৰক" - "ছেট আপ কৰক" - "ছেটিং" - diff --git a/nearby/halfsheet/res/values-az/strings.xml b/nearby/halfsheet/res/values-az/strings.xml deleted file mode 100644 index af499ef6f9be6f7a017f3e55f175b85ff47130c2..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-az/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Ayarlama başladılır…" - "Cihazı quraşdırın" - "Cihaz qoşulub" - "Qoşulmaq mümkün olmadı" - "Oldu" - "Saxlayın" - "Qoşun" - "Ayarlayın" - "Ayarlar" - diff --git a/nearby/halfsheet/res/values-b+sr+Latn/strings.xml b/nearby/halfsheet/res/values-b+sr+Latn/strings.xml deleted file mode 100644 index eea6b647551c28d50a817515b82e3730484ee3a1..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-b+sr+Latn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Podešavanje se pokreće…" - "Podesite uređaj" - "Uređaj je povezan" - "Povezivanje nije uspelo" - "Gotovo" - "Sačuvaj" - "Poveži" - "Podesi" - "Podešavanja" - diff --git a/nearby/halfsheet/res/values-be/strings.xml b/nearby/halfsheet/res/values-be/strings.xml deleted file mode 100644 index a5c1ef6b7d1e7023b1bc91e4a0661f9b49cb47f5..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-be/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Пачынаецца наладжванне…" - "Наладзьце прыладу" - "Прылада падключана" - "Не ўдалося падключыцца" - "Гатова" - "Захаваць" - "Падключыць" - "Наладзіць" - "Налады" - diff --git a/nearby/halfsheet/res/values-bg/strings.xml b/nearby/halfsheet/res/values-bg/strings.xml deleted file mode 100644 index 0ee7aef11f30fad0b78dbffc229126c266b31ade..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-bg/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Настройването се стартира…" - "Настройване на устройството" - "Устройството е свързано" - "Свързването не бе успешно" - "Готово" - "Запазване" - "Свързване" - "Настройване" - "Настройки" - diff --git a/nearby/halfsheet/res/values-bn/strings.xml b/nearby/halfsheet/res/values-bn/strings.xml deleted file mode 100644 index 484e35b5ab73f45d3e4bd8ddc3e05343a6e8358b..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-bn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "সেট-আপ করা শুরু হচ্ছে…" - "ডিভাইস সেট-আপ করুন" - "ডিভাইস কানেক্ট হয়েছে" - "কানেক্ট করা যায়নি" - "হয়ে গেছে" - "সেভ করুন" - "কানেক্ট করুন" - "সেট-আপ করুন" - "সেটিংস" - diff --git a/nearby/halfsheet/res/values-bs/strings.xml b/nearby/halfsheet/res/values-bs/strings.xml deleted file mode 100644 index 2fc8644b96efd1a7e8158f310e771c808ee4c6fd..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-bs/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Pokretanje postavljanja…" - "Postavi uređaj" - "Uređaj je povezan" - "Povezivanje nije uspjelo" - "Gotovo" - "Sačuvaj" - "Poveži" - "Postavi" - "Postavke" - diff --git a/nearby/halfsheet/res/values-ca/strings.xml b/nearby/halfsheet/res/values-ca/strings.xml deleted file mode 100644 index 8912792499f52a9be4f08e31e8cce8719083dc93..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ca/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iniciant la configuració…" - "Configura el dispositiu" - "El dispositiu s\'ha connectat" - "No s\'ha pogut connectar" - "Fet" - "Desa" - "Connecta" - "Configura" - "Configuració" - diff --git a/nearby/halfsheet/res/values-cs/strings.xml b/nearby/halfsheet/res/values-cs/strings.xml deleted file mode 100644 index 7e7ea3cc4faea9a1c6766b1afc22234764a3fc16..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-cs/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Zahajování nastavení…" - "Nastavení zařízení" - "Zařízení je připojeno" - "Nelze se připojit" - "Hotovo" - "Uložit" - "Připojit" - "Nastavit" - "Nastavení" - diff --git a/nearby/halfsheet/res/values-da/strings.xml b/nearby/halfsheet/res/values-da/strings.xml deleted file mode 100644 index 1d937e2963325e309a97da236855e1a7e04839a6..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-da/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Begynder konfiguration…" - "Konfigurer enhed" - "Enheden er forbundet" - "Forbindelsen kan ikke oprettes" - "Luk" - "Gem" - "Opret forbindelse" - "Konfigurer" - "Indstillinger" - diff --git a/nearby/halfsheet/res/values-de/strings.xml b/nearby/halfsheet/res/values-de/strings.xml deleted file mode 100644 index 9186a4473d772fd6769dabb26d9dcf1197673699..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-de/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Einrichtung wird gestartet..." - "Gerät einrichten" - "Gerät verbunden" - "Verbindung nicht möglich" - "Fertig" - "Speichern" - "Verbinden" - "Einrichten" - "Einstellungen" - diff --git a/nearby/halfsheet/res/values-el/strings.xml b/nearby/halfsheet/res/values-el/strings.xml deleted file mode 100644 index 3e18a93a60abf6ddcff4bad49db98277eb841668..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-el/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Έναρξη ρύθμισης…" - "Ρύθμιση συσκευής" - "Η συσκευή συνδέθηκε" - "Αδυναμία σύνδεσης" - "Τέλος" - "Αποθήκευση" - "Σύνδεση" - "Ρύθμιση" - "Ρυθμίσεις" - diff --git a/nearby/halfsheet/res/values-en-rAU/strings.xml b/nearby/halfsheet/res/values-en-rAU/strings.xml deleted file mode 100644 index d4ed675b30f83938d31a5fceb5c889742e4a0703..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-en-rAU/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Starting setup…" - "Set up device" - "Device connected" - "Couldn\'t connect" - "Done" - "Save" - "Connect" - "Set up" - "Settings" - diff --git a/nearby/halfsheet/res/values-en-rCA/strings.xml b/nearby/halfsheet/res/values-en-rCA/strings.xml deleted file mode 100644 index 6094199209761d2ac6e220f059a6656580258f1f..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-en-rCA/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Starting Setup…" - "Set up device" - "Device connected" - "Couldn\'t connect" - "Done" - "Save" - "Connect" - "Set up" - "Settings" - diff --git a/nearby/halfsheet/res/values-en-rGB/strings.xml b/nearby/halfsheet/res/values-en-rGB/strings.xml deleted file mode 100644 index d4ed675b30f83938d31a5fceb5c889742e4a0703..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Starting setup…" - "Set up device" - "Device connected" - "Couldn\'t connect" - "Done" - "Save" - "Connect" - "Set up" - "Settings" - diff --git a/nearby/halfsheet/res/values-en-rIN/strings.xml b/nearby/halfsheet/res/values-en-rIN/strings.xml deleted file mode 100644 index d4ed675b30f83938d31a5fceb5c889742e4a0703..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-en-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Starting setup…" - "Set up device" - "Device connected" - "Couldn\'t connect" - "Done" - "Save" - "Connect" - "Set up" - "Settings" - diff --git a/nearby/halfsheet/res/values-en-rXC/strings.xml b/nearby/halfsheet/res/values-en-rXC/strings.xml deleted file mode 100644 index 460cc1bbdb78b4f9fb85962d2e33056d6de80409..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-en-rXC/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‎Starting Setup…‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎Set up device‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎Device connected‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎Couldn\'t connect‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎Done‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎Save‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎Connect‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎Set up‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎Settings‎‏‎‎‏‎" - diff --git a/nearby/halfsheet/res/values-es-rUS/strings.xml b/nearby/halfsheet/res/values-es-rUS/strings.xml deleted file mode 100644 index d8fb2836d51ea77ef44a04346bbcd7f227f08896..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iniciando la configuración…" - "Configuración del dispositivo" - "Se conectó el dispositivo" - "No se pudo establecer conexión" - "Listo" - "Guardar" - "Conectar" - "Configurar" - "Configuración" - diff --git a/nearby/halfsheet/res/values-es/strings.xml b/nearby/halfsheet/res/values-es/strings.xml deleted file mode 100644 index 4b8340a2f74935f15113d10aa44968eedb3ea838..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-es/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iniciando configuración…" - "Configurar el dispositivo" - "Dispositivo conectado" - "No se ha podido conectar" - "Hecho" - "Guardar" - "Conectar" - "Configurar" - "Ajustes" - diff --git a/nearby/halfsheet/res/values-et/strings.xml b/nearby/halfsheet/res/values-et/strings.xml deleted file mode 100644 index e6abc6453fd2a7c12aefc7031adb5647a6f603fc..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-et/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Seadistuse käivitamine …" - "Seadistage seade" - "Seade on ühendatud" - "Ühendamine ebaõnnestus" - "Valmis" - "Salvesta" - "Ühenda" - "Seadistamine" - "Seaded" - diff --git a/nearby/halfsheet/res/values-eu/strings.xml b/nearby/halfsheet/res/values-eu/strings.xml deleted file mode 100644 index 4243fd55ac355e4ced392b5a62997d82edcc9968..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-eu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Konfigurazio-prozesua abiarazten…" - "Konfiguratu gailua" - "Konektatu da gailua" - "Ezin izan da konektatu" - "Eginda" - "Gorde" - "Konektatu" - "Konfiguratu" - "Ezarpenak" - diff --git a/nearby/halfsheet/res/values-fa/strings.xml b/nearby/halfsheet/res/values-fa/strings.xml deleted file mode 100644 index 3585f9518d0c66f3561d1b32142d012caf7839ad..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-fa/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "درحال شروع راه‌اندازی…" - "راه‌اندازی دستگاه" - "دستگاه متصل شد" - "متصل نشد" - "تمام" - "ذخیره" - "متصل کردن" - "راه‌اندازی" - "تنظیمات" - diff --git a/nearby/halfsheet/res/values-fi/strings.xml b/nearby/halfsheet/res/values-fi/strings.xml deleted file mode 100644 index e8d47de8782882cd67b8a79d1294c18609ba9083..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-fi/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Aloitetaan käyttöönottoa…" - "Määritä laite" - "Laite on yhdistetty" - "Ei yhteyttä" - "Valmis" - "Tallenna" - "Yhdistä" - "Ota käyttöön" - "Asetukset" - diff --git a/nearby/halfsheet/res/values-fr-rCA/strings.xml b/nearby/halfsheet/res/values-fr-rCA/strings.xml deleted file mode 100644 index 64dd107b8fc08fa62440a5550d8a9c9c8c1369e6..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-fr-rCA/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Démarrage de la configuration…" - "Configurer l\'appareil" - "Appareil associé" - "Impossible d\'associer" - "OK" - "Enregistrer" - "Associer" - "Configurer" - "Paramètres" - diff --git a/nearby/halfsheet/res/values-fr/strings.xml b/nearby/halfsheet/res/values-fr/strings.xml deleted file mode 100644 index 484c57bdea2a14580f5f446e96afe7d1dd17e860..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-fr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Début de la configuration…" - "Configurer un appareil" - "Appareil associé" - "Impossible de se connecter" - "OK" - "Enregistrer" - "Connecter" - "Configurer" - "Paramètres" - diff --git a/nearby/halfsheet/res/values-gl/strings.xml b/nearby/halfsheet/res/values-gl/strings.xml deleted file mode 100644 index 30393ff8fcefd39403e3d1cb2027b5d265d42162..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-gl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iniciando configuración…" - "Configura o dispositivo" - "Conectouse o dispositivo" - "Non se puido conectar" - "Feito" - "Gardar" - "Conectar" - "Configurar" - "Configuración" - diff --git a/nearby/halfsheet/res/values-gu/strings.xml b/nearby/halfsheet/res/values-gu/strings.xml deleted file mode 100644 index 03b057d37ad375afa1d91162c98f957305ca1485..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-gu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "સેટઅપ શરૂ કરી રહ્યાં છીએ…" - "ડિવાઇસનું સેટઅપ કરો" - "ડિવાઇસ કનેક્ટ કર્યું" - "કનેક્ટ કરી શક્યા નથી" - "થઈ ગયું" - "સાચવો" - "કનેક્ટ કરો" - "સેટઅપ કરો" - "સેટિંગ" - diff --git a/nearby/halfsheet/res/values-hi/strings.xml b/nearby/halfsheet/res/values-hi/strings.xml deleted file mode 100644 index ecd420e3bf3e390d2d11f9eb8202223d7269c5f2..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-hi/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "सेट अप शुरू किया जा रहा है…" - "डिवाइस सेट अप करें" - "डिवाइस कनेक्ट हो गया" - "कनेक्ट नहीं किया जा सका" - "हो गया" - "सेव करें" - "कनेक्ट करें" - "सेट अप करें" - "सेटिंग" - diff --git a/nearby/halfsheet/res/values-hr/strings.xml b/nearby/halfsheet/res/values-hr/strings.xml deleted file mode 100644 index 5a3de8f11c0a3d445acb8ee78f8e2af3f593a8f1..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-hr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Pokretanje postavljanja…" - "Postavi uređaj" - "Uređaj je povezan" - "Povezivanje nije uspjelo" - "Gotovo" - "Spremi" - "Poveži" - "Postavi" - "Postavke" - diff --git a/nearby/halfsheet/res/values-hu/strings.xml b/nearby/halfsheet/res/values-hu/strings.xml deleted file mode 100644 index ba3d2e0b86e674c7770442e57c4aaa0d60341b30..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-hu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Beállítás megkezdése…" - "Eszköz beállítása" - "Eszköz csatlakoztatva" - "Nem sikerült csatlakozni" - "Kész" - "Mentés" - "Csatlakozás" - "Beállítás" - "Beállítások" - diff --git a/nearby/halfsheet/res/values-hy/strings.xml b/nearby/halfsheet/res/values-hy/strings.xml deleted file mode 100644 index ecabd16c29ece6cc5ffb2f94d3640482ac15d017..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-hy/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Կարգավորում…" - "Կարգավորեք սարքը" - "Սարքը զուգակցվեց" - "Չհաջողվեց միանալ" - "Պատրաստ է" - "Պահել" - "Միանալ" - "Կարգավորել" - "Կարգավորումներ" - diff --git a/nearby/halfsheet/res/values-in/strings.xml b/nearby/halfsheet/res/values-in/strings.xml deleted file mode 100644 index dc777b2193c9d214ce1833c10cba684bb1b4c7f7..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-in/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Memulai Penyiapan …" - "Siapkan perangkat" - "Perangkat terhubung" - "Tidak dapat terhubung" - "Selesai" - "Simpan" - "Hubungkan" - "Siapkan" - "Setelan" - diff --git a/nearby/halfsheet/res/values-is/strings.xml b/nearby/halfsheet/res/values-is/strings.xml deleted file mode 100644 index ee094d9fe5aa2d7b4247cc9e4cb654a31fb6ba54..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-is/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Ræsir uppsetningu…" - "Uppsetning tækis" - "Tækið er tengt" - "Tenging mistókst" - "Lokið" - "Vista" - "Tengja" - "Setja upp" - "Stillingar" - diff --git a/nearby/halfsheet/res/values-it/strings.xml b/nearby/halfsheet/res/values-it/strings.xml deleted file mode 100644 index 700dd77b552605db380044c3dcb391432822dbb6..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-it/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Avvio della configurazione…" - "Configura dispositivo" - "Dispositivo connesso" - "Impossibile connettere" - "Fine" - "Salva" - "Connetti" - "Configura" - "Impostazioni" - diff --git a/nearby/halfsheet/res/values-iw/strings.xml b/nearby/halfsheet/res/values-iw/strings.xml deleted file mode 100644 index e6ff9b949c079436322a860ea77c69969bafc82a..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-iw/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ההגדרה מתבצעת…" - "הגדרת המכשיר" - "המכשיר מחובר" - "לא ניתן להתחבר" - "סיום" - "שמירה" - "התחברות" - "הגדרה" - "הגדרות" - diff --git a/nearby/halfsheet/res/values-ja/strings.xml b/nearby/halfsheet/res/values-ja/strings.xml deleted file mode 100644 index a429b7e5a704a1c970b67187639de6c6cf0e6b09..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ja/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "セットアップを開始中…" - "デバイスのセットアップ" - "デバイス接続完了" - "接続エラー" - "完了" - "保存" - "接続" - "セットアップ" - "設定" - diff --git a/nearby/halfsheet/res/values-ka/strings.xml b/nearby/halfsheet/res/values-ka/strings.xml deleted file mode 100644 index 4353ae9758fc44df32187fa9095c83b8ef79c963..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ka/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "დაყენება იწყება…" - "მოწყობილობის დაყენება" - "მოწყობილობა დაკავშირებულია" - "დაკავშირება ვერ მოხერხდა" - "მზადაა" - "შენახვა" - "დაკავშირება" - "დაყენება" - "პარამეტრები" - diff --git a/nearby/halfsheet/res/values-kk/strings.xml b/nearby/halfsheet/res/values-kk/strings.xml deleted file mode 100644 index 98d8073cdac8ecad6f01151c297a5689ca406649..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-kk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Реттеу басталуда…" - "Құрылғыны реттеу" - "Құрылғы байланыстырылды" - "Қосылмады" - "Дайын" - "Сақтау" - "Қосу" - "Реттеу" - "Параметрлер" - diff --git a/nearby/halfsheet/res/values-km/strings.xml b/nearby/halfsheet/res/values-km/strings.xml deleted file mode 100644 index 85e39dbd8f0b8b6b1b6d2d5b71b42b799b6efebe..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-km/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "កំពុងចាប់ផ្ដើម​រៀបចំ…" - "រៀបចំ​ឧបករណ៍" - "បានភ្ជាប់ឧបករណ៍" - "មិន​អាចភ្ជាប់​បានទេ" - "រួចរាល់" - "រក្សាទុក" - "ភ្ជាប់" - "រៀបចំ" - "ការកំណត់" - diff --git a/nearby/halfsheet/res/values-kn/strings.xml b/nearby/halfsheet/res/values-kn/strings.xml deleted file mode 100644 index fb62bb1d2dbbc4ce14f3b2790409fbb36ad1dda7..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-kn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ಸೆಟಪ್ ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…" - "ಸಾಧನವನ್ನು ಸೆಟಪ್ ಮಾಡಿ" - "ಸಾಧನವನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ" - "ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ" - "ಮುಗಿದಿದೆ" - "ಉಳಿಸಿ" - "ಕನೆಕ್ಟ್ ಮಾಡಿ" - "ಸೆಟಪ್ ಮಾಡಿ" - "ಸೆಟ್ಟಿಂಗ್‌ಗಳು" - diff --git a/nearby/halfsheet/res/values-ko/strings.xml b/nearby/halfsheet/res/values-ko/strings.xml deleted file mode 100644 index c94ff7650cc7939f0e5a5c47eeb1ceecd7a46f93..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ko/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "설정을 시작하는 중…" - "기기 설정" - "기기 연결됨" - "연결할 수 없음" - "완료" - "저장" - "연결" - "설정" - "설정" - diff --git a/nearby/halfsheet/res/values-ky/strings.xml b/nearby/halfsheet/res/values-ky/strings.xml deleted file mode 100644 index b0dfe2097cc2203470b7e2206b57f5395c6e027d..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ky/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Жөндөлүп баштады…" - "Түзмөктү жөндөө" - "Түзмөк туташты" - "Туташпай койду" - "Бүттү" - "Сактоо" - "Туташуу" - "Жөндөө" - "Параметрлер" - diff --git a/nearby/halfsheet/res/values-lo/strings.xml b/nearby/halfsheet/res/values-lo/strings.xml deleted file mode 100644 index 9c945b247eff19667691b80044255957fb66c4b7..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-lo/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ກຳລັງເລີ່ມການຕັ້ງຄ່າ…" - "ຕັ້ງຄ່າອຸປະກອນ" - "ເຊື່ອມຕໍ່ອຸປະກອນແລ້ວ" - "ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້" - "ແລ້ວໆ" - "ບັນທຶກ" - "ເຊື່ອມຕໍ່" - "ຕັ້ງຄ່າ" - "ການຕັ້ງຄ່າ" - diff --git a/nearby/halfsheet/res/values-lt/strings.xml b/nearby/halfsheet/res/values-lt/strings.xml deleted file mode 100644 index 5dbad0ad33cb0257ecc314e6f92b926dd6ace664..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-lt/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Pradedama sąranka…" - "Įrenginio nustatymas" - "Įrenginys prijungtas" - "Prisijungti nepavyko" - "Atlikta" - "Išsaugoti" - "Prisijungti" - "Nustatyti" - "Nustatymai" - diff --git a/nearby/halfsheet/res/values-lv/strings.xml b/nearby/halfsheet/res/values-lv/strings.xml deleted file mode 100644 index a9e1bf9c6f3d0a14ea1eeaa9dac9d3040fb84670..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-lv/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Tiek sākta iestatīšana…" - "Iestatiet ierīci" - "Ierīce ir pievienota" - "Nevarēja izveidot savienojumu" - "Gatavs" - "Saglabāt" - "Izveidot savienojumu" - "Iestatīt" - "Iestatījumi" - diff --git a/nearby/halfsheet/res/values-mk/strings.xml b/nearby/halfsheet/res/values-mk/strings.xml deleted file mode 100644 index e29dfa146b07cf1d9b450274c66efdd88a7aae35..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-mk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Се започнува со поставување…" - "Поставете го уредот" - "Уредот е поврзан" - "Не може да се поврзе" - "Готово" - "Зачувај" - "Поврзи" - "Поставете" - "Поставки" - diff --git a/nearby/halfsheet/res/values-ml/strings.xml b/nearby/halfsheet/res/values-ml/strings.xml deleted file mode 100644 index cbc171bf17f7b09dd4ff10e0142e95c5c6a9a94d..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ml/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "സജ്ജീകരിക്കൽ ആരംഭിക്കുന്നു…" - "ഉപകരണം സജ്ജീകരിക്കുക" - "ഉപകരണം കണക്റ്റ് ചെയ്‌തു" - "കണക്റ്റ് ചെയ്യാനായില്ല" - "പൂർത്തിയായി" - "സംരക്ഷിക്കുക" - "കണക്റ്റ് ചെയ്യുക" - "സജ്ജീകരിക്കുക" - "ക്രമീകരണം" - diff --git a/nearby/halfsheet/res/values-mn/strings.xml b/nearby/halfsheet/res/values-mn/strings.xml deleted file mode 100644 index 6d21effb4a71c424b78231bec7205a29df5ffefc..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-mn/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Тохируулгыг эхлүүлж байна…" - "Төхөөрөмж тохируулах" - "Төхөөрөмж холбогдсон" - "Холбогдож чадсангүй" - "Болсон" - "Хадгалах" - "Холбох" - "Тохируулах" - "Тохиргоо" - diff --git a/nearby/halfsheet/res/values-mr/strings.xml b/nearby/halfsheet/res/values-mr/strings.xml deleted file mode 100644 index a3e1d7a843059387e7d653eda0396b413653a033..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-mr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "सेटअप सुरू करत आहे…" - "डिव्हाइस सेट करा" - "डिव्हाइस कनेक्ट केले आहे" - "कनेक्ट करता आले नाही" - "पूर्ण झाले" - "सेव्ह करा" - "कनेक्ट करा" - "सेट करा" - "सेटिंग्ज" - diff --git a/nearby/halfsheet/res/values-ms/strings.xml b/nearby/halfsheet/res/values-ms/strings.xml deleted file mode 100644 index 4835c1b335d6b54bd8dd7abd68f72946d1167869..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ms/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Memulakan Persediaan…" - "Sediakan peranti" - "Peranti disambungkan" - "Tidak dapat menyambung" - "Selesai" - "Simpan" - "Sambung" - "Sediakan" - "Tetapan" - diff --git a/nearby/halfsheet/res/values-my/strings.xml b/nearby/halfsheet/res/values-my/strings.xml deleted file mode 100644 index 32c310508079b3824cb7717928de4316f2d9d209..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-my/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "စနစ်ထည့်သွင်းခြင်း စတင်နေသည်…" - "စက်ကို စနစ်ထည့်သွင်းရန်" - "စက်ကို ချိတ်ဆက်လိုက်ပြီ" - "ချိတ်ဆက်၍မရပါ" - "ပြီးပြီ" - "သိမ်းရန်" - "ချိတ်ဆက်ရန်" - "စနစ်ထည့်သွင်းရန်" - "ဆက်တင်များ" - diff --git a/nearby/halfsheet/res/values-nb/strings.xml b/nearby/halfsheet/res/values-nb/strings.xml deleted file mode 100644 index 9d725659a16e1be1c261059d7e9e67854c24ceed..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-nb/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Starter konfigureringen …" - "Konfigurer enheten" - "Enheten er tilkoblet" - "Kunne ikke koble til" - "Ferdig" - "Lagre" - "Koble til" - "Konfigurer" - "Innstillinger" - diff --git a/nearby/halfsheet/res/values-ne/strings.xml b/nearby/halfsheet/res/values-ne/strings.xml deleted file mode 100644 index 1370412b1ede0f6b34e50e46a7196d8405c38004..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ne/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "सेटअप प्रक्रिया सुरु गरिँदै छ…" - "डिभाइस सेटअप गर्नुहोस्" - "डिभाइस कनेक्ट गरियो" - "कनेक्ट गर्न सकिएन" - "सम्पन्न भयो" - "सेभ गर्नुहोस्" - "कनेक्ट गर्नुहोस्" - "सेटअप गर्नुहोस्" - "सेटिङ" - diff --git a/nearby/halfsheet/res/values-nl/strings.xml b/nearby/halfsheet/res/values-nl/strings.xml deleted file mode 100644 index 4eb7624b57fcc5cf1f7b348c2a45d98a39fc3f83..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-nl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Instellen starten…" - "Apparaat instellen" - "Apparaat verbonden" - "Kan geen verbinding maken" - "Klaar" - "Opslaan" - "Verbinden" - "Instellen" - "Instellingen" - diff --git a/nearby/halfsheet/res/values-or/strings.xml b/nearby/halfsheet/res/values-or/strings.xml deleted file mode 100644 index c5e8cfcc73647447be8cf9906730eaca743f42cb..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-or/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ସେଟଅପ ଆରମ୍ଭ କରାଯାଉଛି…" - "ଡିଭାଇସ ସେଟ ଅପ କରନ୍ତୁ" - "ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି" - "ସଂଯୋଗ କରାଯାଇପାରିଲା ନାହିଁ" - "ହୋଇଗଲା" - "ସେଭ କରନ୍ତୁ" - "ସଂଯୋଗ କରନ୍ତୁ" - "ସେଟ ଅପ କରନ୍ତୁ" - "ସେଟିଂସ" - diff --git a/nearby/halfsheet/res/values-pa/strings.xml b/nearby/halfsheet/res/values-pa/strings.xml deleted file mode 100644 index f0523a3a3962ba3df958bec64dd742283be7cafa..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-pa/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "ਸੈੱਟਅੱਪ ਸ਼ੁਰੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…" - "ਡੀਵਾਈਸ ਸੈੱਟਅੱਪ ਕਰੋ" - "ਡੀਵਾਈਸ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ" - "ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ" - "ਹੋ ਗਿਆ" - "ਰੱਖਿਅਤ ਕਰੋ" - "ਕਨੈਕਟ ਕਰੋ" - "ਸੈੱਟਅੱਪ ਕਰੋ" - "ਸੈਟਿੰਗਾਂ" - diff --git a/nearby/halfsheet/res/values-pl/strings.xml b/nearby/halfsheet/res/values-pl/strings.xml deleted file mode 100644 index 5abf5fd34b4bc75fa32bca3e17498ede0615ae1f..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-pl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Rozpoczynam konfigurowanie…" - "Skonfiguruj urządzenie" - "Urządzenie połączone" - "Nie udało się połączyć" - "Gotowe" - "Zapisz" - "Połącz" - "Skonfiguruj" - "Ustawienia" - diff --git a/nearby/halfsheet/res/values-pt-rBR/strings.xml b/nearby/halfsheet/res/values-pt-rBR/strings.xml deleted file mode 100644 index b021b3924fbfe90e01f31ad18bbc6bf9de0fe795..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iniciando a configuração…" - "Configurar dispositivo" - "Dispositivo conectado" - "Erro ao conectar" - "Concluído" - "Salvar" - "Conectar" - "Configurar" - "Configurações" - diff --git a/nearby/halfsheet/res/values-pt-rPT/strings.xml b/nearby/halfsheet/res/values-pt-rPT/strings.xml deleted file mode 100644 index 3285c73e9c8349f15929ea6a698622e40799573a..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "A iniciar a configuração…" - "Configure o dispositivo" - "Dispositivo ligado" - "Não foi possível ligar" - "Concluir" - "Guardar" - "Ligar" - "Configurar" - "Definições" - diff --git a/nearby/halfsheet/res/values-pt/strings.xml b/nearby/halfsheet/res/values-pt/strings.xml deleted file mode 100644 index b021b3924fbfe90e01f31ad18bbc6bf9de0fe795..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-pt/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iniciando a configuração…" - "Configurar dispositivo" - "Dispositivo conectado" - "Erro ao conectar" - "Concluído" - "Salvar" - "Conectar" - "Configurar" - "Configurações" - diff --git a/nearby/halfsheet/res/values-ro/strings.xml b/nearby/halfsheet/res/values-ro/strings.xml deleted file mode 100644 index 189f698dd9c7d8a2c799ece78948d939a740758e..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ro/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Începe configurarea…" - "Configurează dispozitivul" - "Dispozitivul s-a conectat" - "Nu s-a putut conecta" - "Gata" - "Salvează" - "Conectează" - "Configurează" - "Setări" - diff --git a/nearby/halfsheet/res/values-ru/strings.xml b/nearby/halfsheet/res/values-ru/strings.xml deleted file mode 100644 index ee869dff91df38caf4df10d6b422282e444efc48..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ru/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Начинаем настройку…" - "Настройка устройства" - "Устройство подключено" - "Ошибка подключения" - "Готово" - "Сохранить" - "Подключить" - "Настроить" - "Открыть настройки" - diff --git a/nearby/halfsheet/res/values-si/strings.xml b/nearby/halfsheet/res/values-si/strings.xml deleted file mode 100644 index f4274c27aa5cd35d187004f379d951360a6bfbee..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-si/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "පිහිටුවීම ආරම්භ කරමින්…" - "උපාංගය පිහිටුවන්න" - "උපාංගය සම්බන්ධිතයි" - "සම්බන්ධ කළ නොහැකි විය" - "නිමයි" - "සුරකින්න" - "සම්බන්ධ කරන්න" - "පිහිටුවන්න" - "සැකසීම්" - diff --git a/nearby/halfsheet/res/values-sk/strings.xml b/nearby/halfsheet/res/values-sk/strings.xml deleted file mode 100644 index 46c45af3e24db892705846ac2ddedb95edb13a30..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-sk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Spúšťa sa nastavenie…" - "Nastavte zariadenie" - "Zariadenie bolo pripojené" - "Nepodarilo sa pripojiť" - "Hotovo" - "Uložiť" - "Pripojiť" - "Nastaviť" - "Nastavenia" - diff --git a/nearby/halfsheet/res/values-sl/strings.xml b/nearby/halfsheet/res/values-sl/strings.xml deleted file mode 100644 index e4f3c91a3a1c95da2c2f22a28da806573b09ad01..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-sl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Začetek nastavitve …" - "Nastavitev naprave" - "Naprava je povezana" - "Povezava ni mogoča" - "Končano" - "Shrani" - "Poveži" - "Nastavi" - "Nastavitve" - diff --git a/nearby/halfsheet/res/values-sq/strings.xml b/nearby/halfsheet/res/values-sq/strings.xml deleted file mode 100644 index 9265d1f63d6c011ed59698b101ad1473a66603f9..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-sq/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Po nis konfigurimin…" - "Konfiguro pajisjen" - "Pajisja u lidh" - "Nuk mund të lidhej" - "U krye" - "Ruaj" - "Lidh" - "Konfiguro" - "Cilësimet" - diff --git a/nearby/halfsheet/res/values-sr/strings.xml b/nearby/halfsheet/res/values-sr/strings.xml deleted file mode 100644 index 094be03a6fb5a2171fab2e70b6a3367dad2e3e85..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-sr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Подешавање се покреће…" - "Подесите уређај" - "Уређај је повезан" - "Повезивање није успело" - "Готово" - "Сачувај" - "Повежи" - "Подеси" - "Подешавања" - diff --git a/nearby/halfsheet/res/values-sv/strings.xml b/nearby/halfsheet/res/values-sv/strings.xml deleted file mode 100644 index 297b7bc5622466d62751655a844d2041e3f06d39..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-sv/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Konfigureringen startas …" - "Konfigurera enheten" - "Enheten är ansluten" - "Det gick inte att ansluta" - "Klar" - "Spara" - "Anslut" - "Konfigurera" - "Inställningar" - diff --git a/nearby/halfsheet/res/values-sw/strings.xml b/nearby/halfsheet/res/values-sw/strings.xml deleted file mode 100644 index bf0bfebc877cb933c951e68940fc1064ef9a3162..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-sw/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Inaanza Kuweka Mipangilio…" - "Weka mipangilio ya kifaa" - "Kifaa kimeunganishwa" - "Imeshindwa kuunganisha" - "Imemaliza" - "Hifadhi" - "Unganisha" - "Weka mipangilio" - "Mipangilio" - diff --git a/nearby/halfsheet/res/values-ta/strings.xml b/nearby/halfsheet/res/values-ta/strings.xml deleted file mode 100644 index dfd67a6fb21148d10ef219c47110537414a8b0c2..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ta/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "அமைவைத் தொடங்குகிறது…" - "சாதனத்தை அமையுங்கள்" - "சாதனம் இணைக்கப்பட்டது" - "இணைக்க முடியவில்லை" - "முடிந்தது" - "சேமி" - "இணை" - "அமை" - "அமைப்புகள்" - diff --git a/nearby/halfsheet/res/values-te/strings.xml b/nearby/halfsheet/res/values-te/strings.xml deleted file mode 100644 index 87be14563ffcf8b35a267dca002e81b7a1db65f4..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-te/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "సెటప్ ప్రారంభమవుతోంది…" - "పరికరాన్ని సెటప్ చేయండి" - "పరికరం కనెక్ట్ చేయబడింది" - "కనెక్ట్ చేయడం సాధ్యపడలేదు" - "పూర్తయింది" - "సేవ్ చేయండి" - "కనెక్ట్ చేయండి" - "సెటప్ చేయండి" - "సెట్టింగ్‌లు" - diff --git a/nearby/halfsheet/res/values-th/strings.xml b/nearby/halfsheet/res/values-th/strings.xml deleted file mode 100644 index bc4296bd2250851aed4ff2a66e7bfa501e26fc22..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-th/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "กำลังเริ่มการตั้งค่า…" - "ตั้งค่าอุปกรณ์" - "เชื่อมต่ออุปกรณ์แล้ว" - "เชื่อมต่อไม่ได้" - "เสร็จสิ้น" - "บันทึก" - "เชื่อมต่อ" - "ตั้งค่า" - "การตั้งค่า" - diff --git a/nearby/halfsheet/res/values-tl/strings.xml b/nearby/halfsheet/res/values-tl/strings.xml deleted file mode 100644 index a6de0e8f9a564dcf7acbc93dc8ce65f5593c43a8..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-tl/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Sinisimulan ang Pag-set Up…" - "I-set up ang device" - "Naikonekta na ang device" - "Hindi makakonekta" - "Tapos na" - "I-save" - "Kumonekta" - "I-set up" - "Mga Setting" - diff --git a/nearby/halfsheet/res/values-tr/strings.xml b/nearby/halfsheet/res/values-tr/strings.xml deleted file mode 100644 index cd5a6ea9b0ca4178203241ca20ea557189819085..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-tr/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Kurulum Başlatılıyor…" - "Cihazı kur" - "Cihaz bağlandı" - "Bağlanamadı" - "Bitti" - "Kaydet" - "Bağlan" - "Kur" - "Ayarlar" - diff --git a/nearby/halfsheet/res/values-uk/strings.xml b/nearby/halfsheet/res/values-uk/strings.xml deleted file mode 100644 index 242ca07a06b521772481fed318067752ed67f4a8..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-uk/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Запуск налаштування…" - "Налаштуйте пристрій" - "Пристрій підключено" - "Не вдалося підключити" - "Готово" - "Зберегти" - "Підключити" - "Налаштувати" - "Налаштування" - diff --git a/nearby/halfsheet/res/values-ur/strings.xml b/nearby/halfsheet/res/values-ur/strings.xml deleted file mode 100644 index 4a4a59caa688ad8ac4ae68e795a2b218a3a77367..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-ur/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "سیٹ اپ شروع ہو رہا ہے…" - "آلہ سیٹ اپ کریں" - "آلہ منسلک ہے" - "منسلک نہیں ہو سکا" - "ہو گیا" - "محفوظ کریں" - "منسلک کریں" - "سیٹ اپ کریں" - "ترتیبات" - diff --git a/nearby/halfsheet/res/values-uz/strings.xml b/nearby/halfsheet/res/values-uz/strings.xml deleted file mode 100644 index 420512daa0d5aca37342a5b0722d7b3bc7e1141c..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-uz/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Sozlash boshlandi…" - "Qurilmani sozlash" - "Qurilma ulandi" - "Ulanmadi" - "Tayyor" - "Saqlash" - "Ulanish" - "Sozlash" - "Sozlamalar" - diff --git a/nearby/halfsheet/res/values-vi/strings.xml b/nearby/halfsheet/res/values-vi/strings.xml deleted file mode 100644 index 9c1e052981c544b472ab624738adf84115e2f4e4..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-vi/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Đang bắt đầu thiết lập…" - "Thiết lập thiết bị" - "Đã kết nối thiết bị" - "Không kết nối được" - "Xong" - "Lưu" - "Kết nối" - "Thiết lập" - "Cài đặt" - diff --git a/nearby/halfsheet/res/values-zh-rCN/strings.xml b/nearby/halfsheet/res/values-zh-rCN/strings.xml deleted file mode 100644 index 482b5c4f92ed88be0af63f55e6f82c9e15a49a99..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "正在启动设置…" - "设置设备" - "设备已连接" - "无法连接" - "完成" - "保存" - "连接" - "设置" - "设置" - diff --git a/nearby/halfsheet/res/values-zh-rHK/strings.xml b/nearby/halfsheet/res/values-zh-rHK/strings.xml deleted file mode 100644 index 3ca73e6aa89eda92bccee95bb0298b9112328c23..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-zh-rHK/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "開始設定…" - "設定裝置" - "已連接裝置" - "無法連接" - "完成" - "儲存" - "連接" - "設定" - "設定" - diff --git a/nearby/halfsheet/res/values-zh-rTW/strings.xml b/nearby/halfsheet/res/values-zh-rTW/strings.xml deleted file mode 100644 index b4e680d866210e7a25f5699b8772d283ee66502b..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "正在啟動設定程序…" - "設定裝置" - "裝置已連線" - "無法連線" - "完成" - "儲存" - "連線" - "設定" - "設定" - diff --git a/nearby/halfsheet/res/values-zu/strings.xml b/nearby/halfsheet/res/values-zu/strings.xml deleted file mode 100644 index 33fb405837921960e1b272f92d6953d2f343c66e..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values-zu/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - "Iqalisa Ukusetha…" - "Setha idivayisi" - "Idivayisi ixhunyiwe" - "Ayikwazanga ukuxhuma" - "Kwenziwe" - "Londoloza" - "Xhuma" - "Setha" - "Amasethingi" - diff --git a/nearby/halfsheet/res/values/colors.xml b/nearby/halfsheet/res/values/colors.xml deleted file mode 100644 index b0666655040d72f1f514de6934c80e2eef7ae1cf..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values/colors.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - #00000000 - - @android:color/system_accent1_100 - @android:color/system_neutral1_900 - @android:color/system_neutral1_900 - @android:color/system_accent1_600 - @android:color/system_neutral2_700 - @android:color/system_neutral1_900 - - - #4285F4 - - - #DE000000 - #24000000 - #D93025 - #80868B - #FFFFFF - #1A73E8 - #F44336 - #24000000 - diff --git a/nearby/halfsheet/res/values/dimens.xml b/nearby/halfsheet/res/values/dimens.xml deleted file mode 100644 index f843042adb64848b0e6355b48272ddc8665fb35f..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values/dimens.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - 160dp - 14sp - 11sp - 4dp - 8dp - 8dp - 40dp - 64dp - 32dp - 3dp - 350dp - 215dp - 136dp - 36dp - 48dp - 152dp - 100dp - 156 - 182 - - - 360dp - - 16dp - 70dp - 4dp - - 4dp - 32dp - 32dp - - 0dp - 0dp - 0dp - 0dp - 0dp - - 48dp - diff --git a/nearby/halfsheet/res/values/ints.xml b/nearby/halfsheet/res/values/ints.xml deleted file mode 100644 index 07bf9d2347c1b8c16653be075c51d2df3094a951..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values/ints.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 250 - 250 - diff --git a/nearby/halfsheet/res/values/overlayable.xml b/nearby/halfsheet/res/values/overlayable.xml deleted file mode 100644 index fffa2e357e4cbc101b0b11f1e122ef5a6b52296a..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values/overlayable.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/nearby/halfsheet/res/values/strings.xml b/nearby/halfsheet/res/values/strings.xml deleted file mode 100644 index 01a82e4c352374789b4063b14c6e37aeac3bf68e..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values/strings.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - Starting Setup… - - Set up device - - Device connected - - Couldn\'t connect - - - - - Done - - Save - - Connect - - Set up - - Settings - \ No newline at end of file diff --git a/nearby/halfsheet/res/values/styles.xml b/nearby/halfsheet/res/values/styles.xml deleted file mode 100644 index 917bb63cfd6fa508c9370c3854d5ea87858b0244..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/res/values/styles.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/FastPairUiServiceClient.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/FastPairUiServiceClient.java deleted file mode 100644 index bec0c0aff783743af9a8c9a8ca66032c26a65394..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/FastPairUiServiceClient.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.nearby.halfsheet; - -import android.content.Context; -import android.nearby.FastPairDevice; -import android.nearby.FastPairStatusCallback; -import android.nearby.PairStatusMetadata; -import android.nearby.aidl.IFastPairStatusCallback; -import android.nearby.aidl.IFastPairUiService; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import androidx.annotation.BinderThread; -import androidx.annotation.UiThread; - -import java.lang.ref.WeakReference; - -/** - * A utility class for connecting to the {@link IFastPairUiService} and receive callbacks. - * - * @hide - */ -@UiThread -public class FastPairUiServiceClient { - - private static final String TAG = "FastPairHalfSheet"; - - private final IBinder mBinder; - private final WeakReference mWeakContext; - IFastPairUiService mFastPairUiService; - PairStatusCallbackIBinder mPairStatusCallbackIBinder; - - /** - * The Ibinder instance should be from - * {@link com.android.server.nearby.fastpair.halfsheet.FastPairUiServiceImpl} so that the client can - * talk with the service. - */ - public FastPairUiServiceClient(Context context, IBinder binder) { - mBinder = binder; - mFastPairUiService = IFastPairUiService.Stub.asInterface(mBinder); - mWeakContext = new WeakReference<>(context); - } - - /** - * Registers a callback at service to get UI updates. - */ - public void registerHalfSheetStateCallBack(FastPairStatusCallback fastPairStatusCallback) { - if (mPairStatusCallbackIBinder != null) { - return; - } - mPairStatusCallbackIBinder = new PairStatusCallbackIBinder(fastPairStatusCallback); - try { - mFastPairUiService.registerCallback(mPairStatusCallbackIBinder); - } catch (RemoteException e) { - Log.w(TAG, "Failed to register fastPairStatusCallback", e); - } - } - - /** - * Pairs the device at service. - */ - public void connect(FastPairDevice fastPairDevice) { - try { - mFastPairUiService.connect(fastPairDevice); - } catch (RemoteException e) { - Log.w(TAG, "Failed to connect Fast Pair device" + fastPairDevice, e); - } - } - - /** - * Cancels Fast Pair connection and dismisses half sheet. - */ - public void cancel(FastPairDevice fastPairDevice) { - try { - mFastPairUiService.cancel(fastPairDevice); - } catch (RemoteException e) { - Log.w(TAG, "Failed to connect Fast Pair device" + fastPairDevice, e); - } - } - - private class PairStatusCallbackIBinder extends IFastPairStatusCallback.Stub { - private final FastPairStatusCallback mStatusCallback; - - private PairStatusCallbackIBinder(FastPairStatusCallback fastPairStatusCallback) { - mStatusCallback = fastPairStatusCallback; - } - - @BinderThread - @Override - public synchronized void onPairUpdate(FastPairDevice fastPairDevice, - PairStatusMetadata pairStatusMetadata) { - Context context = mWeakContext.get(); - if (context != null) { - Handler handler = new Handler(context.getMainLooper()); - handler.post(() -> - mStatusCallback.onPairUpdate(fastPairDevice, pairStatusMetadata)); - } - } - } -} diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java deleted file mode 100644 index 07e57763760ab1d3dac01d3a91c3fbb6ebc5ada6..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.nearby.halfsheet; - -import static android.Manifest.permission.ACCESS_FINE_LOCATION; - -import static com.android.nearby.halfsheet.fragment.DevicePairingFragment.APP_LAUNCH_FRAGMENT_TYPE; -import static com.android.server.nearby.common.bluetooth.fastpair.FastPairConstants.EXTRA_MODEL_ID; -import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_MAC_ADDRESS; -import static com.android.server.nearby.fastpair.Constant.ACTION_FAST_PAIR_HALF_SHEET_CANCEL; -import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE; -import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO; -import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_TYPE; - -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.FragmentActivity; - -import com.android.nearby.halfsheet.fragment.DevicePairingFragment; -import com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment; -import com.android.nearby.halfsheet.utils.BroadcastUtils; - -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.Locale; - -import service.proto.Cache; - -/** - * A class show Fast Pair related information in Half sheet format. - */ -public class HalfSheetActivity extends FragmentActivity { - - public static final String TAG = "FastPairHalfSheet"; - - public static final String EXTRA_HALF_SHEET_CONTENT = - "com.android.nearby.halfsheet.HALF_SHEET_CONTENT"; - public static final String EXTRA_TITLE = - "com.android.nearby.halfsheet.HALF_SHEET_TITLE"; - public static final String EXTRA_DESCRIPTION = - "com.android.nearby.halfsheet.HALF_SHEET_DESCRIPTION"; - public static final String EXTRA_HALF_SHEET_ID = - "com.android.nearby.halfsheet.HALF_SHEET_ID"; - public static final String EXTRA_HALF_SHEET_IS_RETROACTIVE = - "com.android.nearby.halfsheet.HALF_SHEET_IS_RETROACTIVE"; - public static final String EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR = - "com.android.nearby.halfsheet.HALF_SHEET_IS_SUBSEQUENT_PAIR"; - public static final String EXTRA_HALF_SHEET_PAIRING_RESURFACE = - "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PAIRING_RESURFACE"; - public static final String ACTION_HALF_SHEET_FOREGROUND_STATE = - "com.android.nearby.halfsheet.ACTION_HALF_SHEET_FOREGROUND_STATE"; - // Intent extra contains the user gmail name eg. testaccount@gmail.com. - public static final String EXTRA_HALF_SHEET_ACCOUNT_NAME = - "com.android.nearby.halfsheet.HALF_SHEET_ACCOUNT_NAME"; - public static final String EXTRA_HALF_SHEET_FOREGROUND = - "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_FOREGROUND"; - public static final String ARG_FRAGMENT_STATE = "ARG_FRAGMENT_STATE"; - @Nullable - private HalfSheetModuleFragment mHalfSheetModuleFragment; - @Nullable - private Cache.ScanFastPairStoreItem mScanFastPairStoreItem; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - byte[] infoArray = getIntent().getByteArrayExtra(EXTRA_HALF_SHEET_INFO); - String fragmentType = getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE); - if (infoArray == null || fragmentType == null) { - Log.d( - "HalfSheetActivity", - "exit flag off or do not have enough half sheet information."); - finish(); - return; - } - - switch (fragmentType) { - case DEVICE_PAIRING_FRAGMENT_TYPE: - mHalfSheetModuleFragment = DevicePairingFragment.newInstance(getIntent(), - savedInstanceState); - if (mHalfSheetModuleFragment == null) { - Log.d(TAG, "device pairing fragment has error."); - finish(); - return; - } - break; - case APP_LAUNCH_FRAGMENT_TYPE: - // currentFragment = AppLaunchFragment.newInstance(getIntent()); - if (mHalfSheetModuleFragment == null) { - Log.v(TAG, "app launch fragment has error."); - finish(); - return; - } - break; - default: - Log.w(TAG, "there is no valid type for half sheet"); - finish(); - return; - } - if (mHalfSheetModuleFragment != null) { - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.fragment_container, mHalfSheetModuleFragment) - .commit(); - } - setContentView(R.layout.fast_pair_half_sheet); - - // If the user taps on the background, then close the activity. - // Unless they tap on the card itself, then ignore the tap. - findViewById(R.id.background).setOnClickListener(v -> onCancelClicked()); - findViewById(R.id.card) - .setOnClickListener( - v -> Log.v(TAG, "card view is clicked noop")); - try { - mScanFastPairStoreItem = - Cache.ScanFastPairStoreItem.parseFrom(infoArray); - } catch (InvalidProtocolBufferException e) { - Log.w( - TAG, "error happens when pass info to half sheet"); - } - } - - @Override - protected void onStart() { - super.onStart(); - } - - @Override - protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) { - super.onSaveInstanceState(savedInstanceState); - if (mHalfSheetModuleFragment != null) { - mHalfSheetModuleFragment.onSaveInstanceState(savedInstanceState); - } - } - - @Override - public void onBackPressed() { - super.onBackPressed(); - sendHalfSheetCancelBroadcast(); - } - - @Override - protected void onUserLeaveHint() { - super.onUserLeaveHint(); - sendHalfSheetCancelBroadcast(); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - String fragmentType = getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE); - if (fragmentType == null) { - return; - } - if (fragmentType.equals(DEVICE_PAIRING_FRAGMENT_TYPE) - && intent.getExtras() != null - && intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO) != null) { - try { - Cache.ScanFastPairStoreItem testScanFastPairStoreItem = - Cache.ScanFastPairStoreItem.parseFrom( - intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO)); - if (mScanFastPairStoreItem != null - && !testScanFastPairStoreItem.getAddress().equals( - mScanFastPairStoreItem.getAddress()) - && testScanFastPairStoreItem.getModelId().equals( - mScanFastPairStoreItem.getModelId())) { - Log.d(TAG, "possible factory reset happens"); - halfSheetStateChange(); - } - } catch (InvalidProtocolBufferException | NullPointerException e) { - Log.w(TAG, "error happens when pass info to half sheet"); - } - } - } - - /** This function should be called when user click empty area and cancel button. */ - public void onCancelClicked() { - Log.d(TAG, "Cancels the half sheet and paring."); - sendHalfSheetCancelBroadcast(); - finish(); - } - - /** Changes the half sheet foreground state to false. */ - public void halfSheetStateChange() { - BroadcastUtils.sendBroadcast( - this, - new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE) - .putExtra(EXTRA_HALF_SHEET_FOREGROUND, false)); - finish(); - } - - private void sendHalfSheetCancelBroadcast() { - BroadcastUtils.sendBroadcast( - this, - new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE) - .putExtra(EXTRA_HALF_SHEET_FOREGROUND, false)); - if (mScanFastPairStoreItem != null) { - BroadcastUtils.sendBroadcast( - this, - new Intent(ACTION_FAST_PAIR_HALF_SHEET_CANCEL) - .putExtra(EXTRA_MODEL_ID, - mScanFastPairStoreItem.getModelId().toLowerCase(Locale.ROOT)) - .putExtra(EXTRA_HALF_SHEET_TYPE, - getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE)) - .putExtra( - EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR, - getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR, - false)) - .putExtra( - EXTRA_HALF_SHEET_IS_RETROACTIVE, - getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_RETROACTIVE, - false)) - .putExtra(EXTRA_MAC_ADDRESS, mScanFastPairStoreItem.getAddress()), - ACCESS_FINE_LOCATION); - } - } - - @Override - public void setTitle(CharSequence title) { - super.setTitle(title); - TextView toolbarTitle = findViewById(R.id.toolbar_title); - toolbarTitle.setText(title); - } -} diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java deleted file mode 100644 index 320965b424f6b585f06e456a65db3c362ac4d297..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.nearby.halfsheet.fragment; - -import static android.text.TextUtils.isEmpty; - -import static com.android.nearby.halfsheet.HalfSheetActivity.ARG_FRAGMENT_STATE; -import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_DESCRIPTION; -import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_ACCOUNT_NAME; -import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_CONTENT; -import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_ID; -import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_TITLE; -import static com.android.nearby.halfsheet.HalfSheetActivity.TAG; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.FAILED; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.FOUND_DEVICE; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.NOT_STARTED; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.PAIRED_LAUNCHABLE; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.PAIRED_UNLAUNCHABLE; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.PAIRING; -import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER; -import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE; -import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO; - -import android.bluetooth.BluetoothDevice; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.nearby.FastPairDevice; -import android.nearby.FastPairStatusCallback; -import android.nearby.NearbyDevice; -import android.nearby.PairStatusMetadata; -import android.os.Bundle; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import androidx.annotation.Nullable; - -import com.android.nearby.halfsheet.FastPairUiServiceClient; -import com.android.nearby.halfsheet.HalfSheetActivity; -import com.android.nearby.halfsheet.R; -import com.android.nearby.halfsheet.utils.FastPairUtils; -import com.android.nearby.halfsheet.utils.IconUtils; - -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.Objects; - -import service.proto.Cache.ScanFastPairStoreItem; - -/** - * Modularize half sheet for fast pair this fragment will show when half sheet does device pairing. - * - *

This fragment will handle initial pairing subsequent pairing and retroactive pairing. - */ -@SuppressWarnings("nullness") -public class DevicePairingFragment extends HalfSheetModuleFragment implements - FastPairStatusCallback { - private TextView mTitleView; - private TextView mSubTitleView; - private ImageView mImage; - - private Button mConnectButton; - private Button mSetupButton; - private Button mCancelButton; - // Opens Bluetooth Settings. - private Button mSettingsButton; - private ImageView mInfoIconButton; - private ProgressBar mConnectProgressBar; - - private Bundle mBundle; - - private ScanFastPairStoreItem mScanFastPairStoreItem; - private FastPairUiServiceClient mFastPairUiServiceClient; - - private @PairStatusMetadata.Status int mPairStatus = PairStatusMetadata.Status.UNKNOWN; - // True when there is a companion app to open. - private boolean mIsLaunchable; - private boolean mIsConnecting; - // Indicates that the setup button is clicked before. - private boolean mSetupButtonClicked = false; - - // Holds the new text while we transition between the two. - private static final int TAG_PENDING_TEXT = R.id.toolbar_title; - public static final String APP_LAUNCH_FRAGMENT_TYPE = "APP_LAUNCH"; - - private static final String ARG_SETUP_BUTTON_CLICKED = "SETUP_BUTTON_CLICKED"; - private static final String ARG_PAIRING_RESULT = "PAIRING_RESULT"; - - /** - * Create certain fragment according to the intent. - */ - @Nullable - public static HalfSheetModuleFragment newInstance( - Intent intent, @Nullable Bundle saveInstanceStates) { - Bundle args = new Bundle(); - byte[] infoArray = intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO); - - Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE); - String title = intent.getStringExtra(EXTRA_TITLE); - String description = intent.getStringExtra(EXTRA_DESCRIPTION); - String accountName = intent.getStringExtra(EXTRA_HALF_SHEET_ACCOUNT_NAME); - String result = intent.getStringExtra(EXTRA_HALF_SHEET_CONTENT); - int halfSheetId = intent.getIntExtra(EXTRA_HALF_SHEET_ID, 0); - - args.putByteArray(EXTRA_HALF_SHEET_INFO, infoArray); - args.putString(EXTRA_HALF_SHEET_ACCOUNT_NAME, accountName); - args.putString(EXTRA_TITLE, title); - args.putString(EXTRA_DESCRIPTION, description); - args.putInt(EXTRA_HALF_SHEET_ID, halfSheetId); - args.putString(EXTRA_HALF_SHEET_CONTENT, result == null ? "" : result); - args.putBundle(EXTRA_BUNDLE, bundle); - if (saveInstanceStates != null) { - if (saveInstanceStates.containsKey(ARG_FRAGMENT_STATE)) { - args.putSerializable( - ARG_FRAGMENT_STATE, saveInstanceStates.getSerializable(ARG_FRAGMENT_STATE)); - } - if (saveInstanceStates.containsKey(BluetoothDevice.EXTRA_DEVICE)) { - args.putParcelable( - BluetoothDevice.EXTRA_DEVICE, - saveInstanceStates.getParcelable(BluetoothDevice.EXTRA_DEVICE)); - } - if (saveInstanceStates.containsKey(BluetoothDevice.EXTRA_PAIRING_KEY)) { - args.putInt( - BluetoothDevice.EXTRA_PAIRING_KEY, - saveInstanceStates.getInt(BluetoothDevice.EXTRA_PAIRING_KEY)); - } - if (saveInstanceStates.containsKey(ARG_SETUP_BUTTON_CLICKED)) { - args.putBoolean( - ARG_SETUP_BUTTON_CLICKED, - saveInstanceStates.getBoolean(ARG_SETUP_BUTTON_CLICKED)); - } - if (saveInstanceStates.containsKey(ARG_PAIRING_RESULT)) { - args.putBoolean(ARG_PAIRING_RESULT, - saveInstanceStates.getBoolean(ARG_PAIRING_RESULT)); - } - } - DevicePairingFragment fragment = new DevicePairingFragment(); - fragment.setArguments(args); - return fragment; - } - - @Nullable - @Override - public View onCreateView( - LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - /* attachToRoot= */ - View rootView = inflater.inflate( - R.layout.fast_pair_device_pairing_fragment, container, /* attachToRoot= */ - false); - if (getContext() == null) { - Log.d(TAG, "can't find the attached activity"); - return rootView; - } - - Bundle args = getArguments(); - byte[] storeFastPairItemBytesArray = args.getByteArray(EXTRA_HALF_SHEET_INFO); - mBundle = args.getBundle(EXTRA_BUNDLE); - if (mBundle != null) { - mFastPairUiServiceClient = - new FastPairUiServiceClient(getContext(), mBundle.getBinder(EXTRA_BINDER)); - mFastPairUiServiceClient.registerHalfSheetStateCallBack(this); - } - if (args.containsKey(ARG_FRAGMENT_STATE)) { - mFragmentState = (HalfSheetFragmentState) args.getSerializable(ARG_FRAGMENT_STATE); - } - if (args.containsKey(ARG_SETUP_BUTTON_CLICKED)) { - mSetupButtonClicked = args.getBoolean(ARG_SETUP_BUTTON_CLICKED); - } - if (args.containsKey(ARG_PAIRING_RESULT)) { - mPairStatus = args.getInt(ARG_PAIRING_RESULT); - } - - // Initiate views. - mTitleView = Objects.requireNonNull(getActivity()).findViewById(R.id.toolbar_title); - mSubTitleView = rootView.findViewById(R.id.header_subtitle); - mImage = rootView.findViewById(R.id.pairing_pic); - mConnectProgressBar = rootView.findViewById(R.id.connect_progressbar); - mConnectButton = rootView.findViewById(R.id.connect_btn); - mCancelButton = rootView.findViewById(R.id.cancel_btn); - mSettingsButton = rootView.findViewById(R.id.settings_btn); - mSetupButton = rootView.findViewById(R.id.setup_btn); - mInfoIconButton = rootView.findViewById(R.id.info_icon); - mInfoIconButton.setImageResource(R.drawable.fast_pair_ic_info); - - try { - setScanFastPairStoreItem(ScanFastPairStoreItem.parseFrom(storeFastPairItemBytesArray)); - } catch (InvalidProtocolBufferException e) { - Log.w(TAG, - "DevicePairingFragment: error happens when pass info to half sheet"); - return rootView; - } - - // Config for landscape mode - DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); - if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - rootView.getLayoutParams().height = displayMetrics.heightPixels * 4 / 5; - rootView.getLayoutParams().width = displayMetrics.heightPixels * 4 / 5; - mImage.getLayoutParams().height = displayMetrics.heightPixels / 2; - mImage.getLayoutParams().width = displayMetrics.heightPixels / 2; - mConnectProgressBar.getLayoutParams().width = displayMetrics.heightPixels / 2; - mConnectButton.getLayoutParams().width = displayMetrics.heightPixels / 2; - //TODO(b/213373051): Add cancel button - } - - Bitmap icon = IconUtils.getIcon(mScanFastPairStoreItem.getIconPng().toByteArray(), - mScanFastPairStoreItem.getIconPng().size()); - if (icon != null) { - mImage.setImageBitmap(icon); - } - mConnectButton.setOnClickListener(v -> onConnectClick()); - mCancelButton.setOnClickListener(v -> - ((HalfSheetActivity) getActivity()).onCancelClicked()); - mSettingsButton.setOnClickListener(v -> onSettingsClicked()); - mSetupButton.setOnClickListener(v -> onSetupClick()); - - return rootView; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // Get access to the activity's menu - setHasOptionsMenu(true); - } - - @Override - public void onStart() { - super.onStart(); - Log.v(TAG, "onStart: invalidate states"); - invalidateState(); - } - - @Override - public void onSaveInstanceState(Bundle savedInstanceState) { - super.onSaveInstanceState(savedInstanceState); - - savedInstanceState.putSerializable(ARG_FRAGMENT_STATE, mFragmentState); - savedInstanceState.putBoolean(ARG_SETUP_BUTTON_CLICKED, mSetupButtonClicked); - savedInstanceState.putInt(ARG_PAIRING_RESULT, mPairStatus); - } - - private void onSettingsClicked() { - startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)); - } - - private void onSetupClick() { - String companionApp = - FastPairUtils.getCompanionAppFromActionUrl(mScanFastPairStoreItem.getActionUrl()); - Intent intent = - FastPairUtils.createCompanionAppIntent( - Objects.requireNonNull(getContext()), - companionApp, - mScanFastPairStoreItem.getAddress()); - mSetupButtonClicked = true; - if (mFragmentState == PAIRED_LAUNCHABLE) { - if (intent != null) { - startActivity(intent); - } - } else { - Log.d(TAG, "onSetupClick: State is " + mFragmentState); - } - } - - private void onConnectClick() { - if (mScanFastPairStoreItem == null) { - Log.w(TAG, "No pairing related information in half sheet"); - return; - } - if (getFragmentState() == PAIRING) { - return; - } - mIsConnecting = true; - invalidateState(); - mFastPairUiServiceClient.connect( - new FastPairDevice.Builder() - .addMedium(NearbyDevice.Medium.BLE) - .setBluetoothAddress(mScanFastPairStoreItem.getAddress()) - .setData(FastPairUtils.convertFrom(mScanFastPairStoreItem) - .toByteArray()) - .build()); - } - - // Receives callback from service. - @Override - public void onPairUpdate(FastPairDevice fastPairDevice, PairStatusMetadata pairStatusMetadata) { - @PairStatusMetadata.Status int status = pairStatusMetadata.getStatus(); - if (status == PairStatusMetadata.Status.DISMISS && getActivity() != null) { - getActivity().finish(); - } - mIsConnecting = false; - mPairStatus = status; - invalidateState(); - } - - @Override - public void invalidateState() { - HalfSheetFragmentState newState = NOT_STARTED; - if (mIsConnecting) { - newState = PAIRING; - } else { - switch (mPairStatus) { - case PairStatusMetadata.Status.SUCCESS: - newState = mIsLaunchable ? PAIRED_LAUNCHABLE : PAIRED_UNLAUNCHABLE; - break; - case PairStatusMetadata.Status.FAIL: - newState = FAILED; - break; - default: - if (mScanFastPairStoreItem != null) { - newState = FOUND_DEVICE; - } - } - } - if (newState == mFragmentState) { - return; - } - setState(newState); - } - - @Override - public void setState(HalfSheetFragmentState state) { - super.setState(state); - invalidateTitles(); - invalidateButtons(); - } - - private void setScanFastPairStoreItem(ScanFastPairStoreItem item) { - mScanFastPairStoreItem = item; - invalidateLaunchable(); - } - - private void invalidateLaunchable() { - String companionApp = - FastPairUtils.getCompanionAppFromActionUrl(mScanFastPairStoreItem.getActionUrl()); - if (isEmpty(companionApp)) { - mIsLaunchable = false; - return; - } - mIsLaunchable = - FastPairUtils.isLaunchable(Objects.requireNonNull(getContext()), companionApp); - } - - private void invalidateButtons() { - mConnectProgressBar.setVisibility(View.INVISIBLE); - mConnectButton.setVisibility(View.INVISIBLE); - mCancelButton.setVisibility(View.INVISIBLE); - mSetupButton.setVisibility(View.INVISIBLE); - mSettingsButton.setVisibility(View.INVISIBLE); - mInfoIconButton.setVisibility(View.INVISIBLE); - - switch (mFragmentState) { - case FOUND_DEVICE: - mInfoIconButton.setVisibility(View.VISIBLE); - mConnectButton.setVisibility(View.VISIBLE); - break; - case PAIRING: - mConnectProgressBar.setVisibility(View.VISIBLE); - mCancelButton.setVisibility(View.VISIBLE); - setBackgroundClickable(false); - break; - case PAIRED_LAUNCHABLE: - mCancelButton.setVisibility(View.VISIBLE); - mSetupButton.setVisibility(View.VISIBLE); - setBackgroundClickable(true); - break; - case FAILED: - mSettingsButton.setVisibility(View.VISIBLE); - setBackgroundClickable(true); - break; - case NOT_STARTED: - case PAIRED_UNLAUNCHABLE: - default: - mCancelButton.setVisibility(View.VISIBLE); - setBackgroundClickable(true); - } - } - - private void setBackgroundClickable(boolean isClickable) { - HalfSheetActivity activity = (HalfSheetActivity) getActivity(); - if (activity == null) { - Log.w(TAG, "setBackgroundClickable: failed to set clickable to " + isClickable - + " because cannot get HalfSheetActivity."); - return; - } - View background = activity.findViewById(R.id.background); - if (background == null) { - Log.w(TAG, "setBackgroundClickable: failed to set clickable to " + isClickable - + " cannot find background at HalfSheetActivity."); - return; - } - Log.d(TAG, "setBackgroundClickable to " + isClickable); - background.setClickable(isClickable); - } - - private void invalidateTitles() { - String newTitle = getTitle(); - invalidateTextView(mTitleView, newTitle); - String newSubTitle = getSubTitle(); - invalidateTextView(mSubTitleView, newSubTitle); - } - - private void invalidateTextView(TextView textView, String newText) { - CharSequence oldText = - textView.getTag(TAG_PENDING_TEXT) != null - ? (CharSequence) textView.getTag(TAG_PENDING_TEXT) - : textView.getText(); - if (TextUtils.equals(oldText, newText)) { - return; - } - if (TextUtils.isEmpty(oldText)) { - // First time run. Don't animate since there's nothing to animate from. - textView.setText(newText); - } else { - textView.setTag(TAG_PENDING_TEXT, newText); - textView - .animate() - .alpha(0f) - .setDuration(TEXT_ANIMATION_DURATION_MILLISECONDS) - .withEndAction( - () -> { - textView.setText(newText); - textView - .animate() - .alpha(1f) - .setDuration(TEXT_ANIMATION_DURATION_MILLISECONDS); - }); - } - } - - private String getTitle() { - switch (mFragmentState) { - case PAIRED_LAUNCHABLE: - return getString(R.string.fast_pair_title_setup); - case FAILED: - return getString(R.string.fast_pair_title_fail); - case FOUND_DEVICE: - case NOT_STARTED: - case PAIRED_UNLAUNCHABLE: - default: - return mScanFastPairStoreItem.getDeviceName(); - } - } - - private String getSubTitle() { - switch (mFragmentState) { - case PAIRED_LAUNCHABLE: - return String.format( - mScanFastPairStoreItem - .getFastPairStrings() - .getPairingFinishedCompanionAppInstalled(), - mScanFastPairStoreItem.getDeviceName()); - case FAILED: - return mScanFastPairStoreItem.getFastPairStrings().getPairingFailDescription(); - case PAIRED_UNLAUNCHABLE: - getString(R.string.fast_pair_device_ready); - // fall through - case FOUND_DEVICE: - case NOT_STARTED: - return mScanFastPairStoreItem.getFastPairStrings().getInitialPairingDescription(); - default: - return ""; - } - } -} diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java deleted file mode 100644 index f1db4d0f5ee6a36b4bbcdd20d3376d75a6058cb3..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.nearby.halfsheet.fragment; - -import static com.android.nearby.halfsheet.HalfSheetActivity.TAG; -import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.NOT_STARTED; - -import android.os.Bundle; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - - -/** Base class for all of the half sheet fragment. */ -public abstract class HalfSheetModuleFragment extends Fragment { - - static final int TEXT_ANIMATION_DURATION_MILLISECONDS = 200; - - HalfSheetFragmentState mFragmentState = NOT_STARTED; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - - /** UI states of the half-sheet fragment. */ - public enum HalfSheetFragmentState { - NOT_STARTED, // Initial status - FOUND_DEVICE, // When a device is found found from Nearby scan service - PAIRING, // When user taps 'Connect' and Fast Pair stars pairing process - PAIRED_LAUNCHABLE, // When pair successfully - // and we found a launchable companion app installed - PAIRED_UNLAUNCHABLE, // When pair successfully - // but we cannot find a companion app to launch it - FAILED, // When paring was failed - FINISHED // When the activity is about to end finished. - } - - /** - * Returns the {@link HalfSheetFragmentState} to the parent activity. - * - *

Overrides this method if the fragment's state needs to be preserved in the parent - * activity. - */ - public HalfSheetFragmentState getFragmentState() { - return mFragmentState; - } - - void setState(HalfSheetFragmentState state) { - Log.v(TAG, "Settings state from " + mFragmentState + " to " + state); - mFragmentState = state; - } - - /** - * Populate data to UI widgets according to the latest {@link HalfSheetFragmentState}. - */ - abstract void invalidateState(); -} diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java deleted file mode 100644 index 2f1e90ae6930fd4865b876e2ff935d03562cc11b..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.nearby.halfsheet.utils; - -import android.content.Context; -import android.content.Intent; - -/** - * Broadcast util class - */ -public class BroadcastUtils { - - /** - * Helps send broadcast. - */ - public static void sendBroadcast(Context context, Intent intent) { - context.sendBroadcast(intent); - } - - /** - * Helps send a broadcast with specified receiver permission. - */ - public static void sendBroadcast(Context context, Intent intent, String receiverPermission) { - context.sendBroadcast(intent, receiverPermission); - } - - private BroadcastUtils() { - } -} diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java deleted file mode 100644 index 00a365c259ee8ad61371b8be93fc42ae86902609..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.nearby.halfsheet.utils; - -import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_COMPANION_APP; -import static com.android.server.nearby.fastpair.UserActionHandler.ACTION_FAST_PAIR; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.net.URISyntaxException; - -import service.proto.Cache; - -/** - * Util class in half sheet apk - */ -public class FastPairUtils { - - /** FastPair util method check certain app is install on the device or not. */ - public static boolean isAppInstalled(Context context, String packageName) { - try { - context.getPackageManager().getPackageInfo(packageName, 0); - return true; - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - - /** FastPair util method to properly format the action url extra. */ - @Nullable - public static String getCompanionAppFromActionUrl(String actionUrl) { - try { - Intent intent = Intent.parseUri(actionUrl, Intent.URI_INTENT_SCHEME); - if (!intent.getAction().equals(ACTION_FAST_PAIR)) { - Log.e("FastPairUtils", "Companion app launch attempted from malformed action url"); - return null; - } - return intent.getStringExtra(EXTRA_COMPANION_APP); - } catch (URISyntaxException e) { - Log.e("FastPairUtils", "FastPair: fail to get companion app info from discovery item"); - return null; - } - } - - /** - * Converts {@link service.proto.Cache.StoredDiscoveryItem} from - * {@link service.proto.Cache.ScanFastPairStoreItem} - */ - public static Cache.StoredDiscoveryItem convertFrom(Cache.ScanFastPairStoreItem item) { - return convertFrom(item, /* isSubsequentPair= */ false); - } - - /** - * Converts a {@link service.proto.Cache.ScanFastPairStoreItem} - * to a {@link service.proto.Cache.StoredDiscoveryItem}. - * - *

This is needed to make the new Fast Pair scanning stack compatible with the rest of the - * legacy Fast Pair code. - */ - public static Cache.StoredDiscoveryItem convertFrom( - Cache.ScanFastPairStoreItem item, boolean isSubsequentPair) { - return Cache.StoredDiscoveryItem.newBuilder() - .setId(item.getModelId()) - .setFirstObservationTimestampMillis(item.getFirstObservationTimestampMillis()) - .setLastObservationTimestampMillis(item.getLastObservationTimestampMillis()) - .setActionUrl(item.getActionUrl()) - .setActionUrlType(Cache.ResolvedUrlType.APP) - .setTitle( - isSubsequentPair - ? item.getFastPairStrings().getTapToPairWithoutAccount() - : item.getDeviceName()) - .setMacAddress(item.getAddress()) - .setState(Cache.StoredDiscoveryItem.State.STATE_ENABLED) - .setTriggerId(item.getModelId()) - .setIconPng(item.getIconPng()) - .setIconFifeUrl(item.getIconFifeUrl()) - .setDescription( - isSubsequentPair - ? item.getDeviceName() - : item.getFastPairStrings().getTapToPairWithoutAccount()) - .setAuthenticationPublicKeySecp256R1(item.getAntiSpoofingPublicKey()) - .setCompanionDetail(item.getCompanionDetail()) - .setFastPairStrings(item.getFastPairStrings()) - .setFastPairInformation( - Cache.FastPairInformation.newBuilder() - .setDataOnlyConnection(item.getDataOnlyConnection()) - .setTrueWirelessImages(item.getTrueWirelessImages()) - .setAssistantSupported(item.getAssistantSupported()) - .setCompanyName(item.getCompanyName())) - .build(); - } - - /** - * Returns true the application is installed and can be opened on device. - */ - public static boolean isLaunchable(@NonNull Context context, String companionApp) { - return isAppInstalled(context, companionApp) - && createCompanionAppIntent(context, companionApp, null) != null; - } - - /** - * Returns an intent to launch given the package name and bluetooth address (if provided). - * Returns null if no such an intent can be found. - */ - @Nullable - public static Intent createCompanionAppIntent(@NonNull Context context, String packageName, - @Nullable String address) { - Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName); - if (intent == null) { - return null; - } - if (address != null) { - BluetoothAdapter adapter = getBluetoothAdapter(context); - if (adapter != null) { - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, adapter.getRemoteDevice(address)); - } - } - return intent; - } - - @Nullable - private static BluetoothAdapter getBluetoothAdapter(@NonNull Context context) { - BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class); - return bluetoothManager == null ? null : bluetoothManager.getAdapter(); - } - - private FastPairUtils() {} -} diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java deleted file mode 100644 index 218c756070120372bf78b9ec6a4149b68d7b9308..0000000000000000000000000000000000000000 --- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/IconUtils.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.nearby.halfsheet.utils; - -import static com.android.nearby.halfsheet.HalfSheetActivity.TAG; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.core.graphics.ColorUtils; - -/** - * Utility class for icon size verification. - */ -public class IconUtils { - - private static final float NOTIFICATION_BACKGROUND_PADDING_PERCENT = 0.125f; - private static final float NOTIFICATION_BACKGROUND_ALPHA = 0.7f; - private static final int MIN_ICON_SIZE = 16; - private static final int DESIRED_ICON_SIZE = 32; - - /** - * Verify that the icon is non null and falls in the small bucket. Just because an icon isn't - * small doesn't guarantee it is large or exists. - */ - public static boolean isIconSizedSmall(@Nullable Bitmap bitmap) { - if (bitmap == null) { - return false; - } - return bitmap.getWidth() >= MIN_ICON_SIZE - && bitmap.getWidth() < DESIRED_ICON_SIZE - && bitmap.getHeight() >= MIN_ICON_SIZE - && bitmap.getHeight() < DESIRED_ICON_SIZE; - } - - /** - * Verify that the icon is non null and falls in the regular / default size bucket. Doesn't - * guarantee if not regular then it is small. - */ - static boolean isIconSizedRegular(@Nullable Bitmap bitmap) { - if (bitmap == null) { - return false; - } - return bitmap.getWidth() >= DESIRED_ICON_SIZE && bitmap.getHeight() >= DESIRED_ICON_SIZE; - } - - /** - * All icons that are sized correctly (larger than the MIN_ICON_SIZE icon size) - * are resize on the server to the DESIRED_ICON_SIZE icon size so that - * they appear correct. - */ - public static boolean isIconSizeCorrect(@Nullable Bitmap bitmap) { - if (bitmap == null) { - return false; - } - return isIconSizedSmall(bitmap) || isIconSizedRegular(bitmap); - } - - /** - * Returns the bitmap from the byte array. Returns null if cannot decode or not in correct size. - */ - @Nullable - public static Bitmap getIcon(byte[] imageData, int size) { - try { - Bitmap icon = - BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, size); - if (IconUtils.isIconSizeCorrect(icon)) { - // Do not add background for Half Sheet. - return IconUtils.addWhiteCircleBackground(icon); - } - } catch (OutOfMemoryError e) { - Log.w(TAG, "getIcon: Failed to decode icon, returning null.", e); - } - return null; - } - - /** Adds a circular, white background to the bitmap. */ - @Nullable - public static Bitmap addWhiteCircleBackground(Bitmap bitmap) { - if (bitmap == null) { - Log.w(TAG, "addWhiteCircleBackground: Bitmap is null, not adding background."); - return null; - } - - if (bitmap.getWidth() != bitmap.getHeight()) { - Log.w(TAG, "addWhiteCircleBackground: Bitmap dimensions not square. Skipping" - + "adding background."); - return bitmap; - } - - int padding = (int) (bitmap.getWidth() * NOTIFICATION_BACKGROUND_PADDING_PERCENT); - Bitmap bitmapWithBackground = - Bitmap.createBitmap( - bitmap.getWidth() + (2 * padding), - bitmap.getHeight() + (2 * padding), - bitmap.getConfig()); - Canvas canvas = new Canvas(bitmapWithBackground); - Paint paint = new Paint(); - paint.setColor( - ColorUtils.setAlphaComponent( - Color.WHITE, (int) (255 * NOTIFICATION_BACKGROUND_ALPHA))); - paint.setStyle(Paint.Style.FILL); - paint.setAntiAlias(true); - canvas.drawCircle( - bitmapWithBackground.getWidth() / 2, - bitmapWithBackground.getHeight() / 2, - bitmapWithBackground.getWidth() / 2, - paint); - canvas.drawBitmap(bitmap, padding, padding, null); - - return bitmapWithBackground; - } -} - diff --git a/nearby/service/Android.bp b/nearby/service/Android.bp index 684b1331888aa6063efb69724a5e3ba5013ec77c..46309026b6de1c2dc07242a420cca53f614bffcd 100644 --- a/nearby/service/Android.bp +++ b/nearby/service/Android.bp @@ -24,57 +24,6 @@ filegroup { ], } -filegroup { - name: "nearby-service-string-res", - srcs: [ - "java/**/Constant.java", - "java/**/UserActionHandlerBase.java", - "java/**/UserActionHandler.java", - "java/**/FastPairConstants.java", - ], -} - -java_library { - name: "nearby-service-string", - srcs: [":nearby-service-string-res"], - libs: ["framework-bluetooth"], - sdk_version: "module_current", -} - -// Common lib for nearby end-to-end testing. -java_library { - name: "nearby-common-lib", - srcs: [ - "java/com/android/server/nearby/common/bloomfilter/*.java", - "java/com/android/server/nearby/common/bluetooth/*.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/AesCtrMultipleBlockEncryption.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/AesEcbSingleBlockEncryption.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAddress.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothUuids.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/EllipticCurveDiffieHellmanExchange.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/HmacSha256.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/Ltv.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/MessageStreamHmacEncoder.java", - "java/com/android/server/nearby/common/bluetooth/fastpair/NamingEncoder.java", - "java/com/android/server/nearby/common/bluetooth/testability/**/*.java", - "java/com/android/server/nearby/common/bluetooth/gatt/*.java", - "java/com/android/server/nearby/common/bluetooth/util/*.java", - ], - libs: [ - "androidx.annotation_annotation", - "androidx.core_core", - "error_prone_annotations", - "framework-bluetooth", - "guava", - ], - sdk_version: "module_current", - visibility: [ - "//packages/modules/Connectivity/nearby/tests/multidevices/clients/test_support/fastpair_provider", - ], -} - // Main lib for nearby services. java_library { name: "service-nearby-pre-jarjar", @@ -95,7 +44,6 @@ java_library { "androidx.core_core", "guava", "libprotobuf-java-lite", - "fast-pair-lite-protos", "modules-utils-build", "modules-utils-handlerexecutor", "modules-utils-preconditions", diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java index 5ebf1e5edd4411ed90a4fd1c5d5c4da05cd2e288..12201048b927feb0bf481ff639a0099c2af206c2 100644 --- a/nearby/service/java/com/android/server/nearby/NearbyService.java +++ b/nearby/service/java/com/android/server/nearby/NearbyService.java @@ -18,7 +18,6 @@ package com.android.server.nearby; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; -import static com.android.server.SystemService.PHASE_THIRD_PARTY_APPS_CAN_START; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -39,13 +38,10 @@ import android.nearby.ScanRequest; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.nearby.common.locator.LocatorContextWrapper; -import com.android.server.nearby.fastpair.FastPairManager; import com.android.server.nearby.injector.ContextHubManagerAdapter; import com.android.server.nearby.injector.Injector; import com.android.server.nearby.provider.BroadcastProviderManager; import com.android.server.nearby.provider.DiscoveryProviderManager; -import com.android.server.nearby.provider.FastPairDataProvider; import com.android.server.nearby.util.identity.CallerIdentity; import com.android.server.nearby.util.permissions.BroadcastPermissions; import com.android.server.nearby.util.permissions.DiscoveryPermissions; @@ -56,7 +52,6 @@ public class NearbyService extends INearbyManager.Stub { private final Context mContext; private Injector mInjector; - private final FastPairManager mFastPairManager; private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() { @Override @@ -82,8 +77,6 @@ public class NearbyService extends INearbyManager.Stub { mInjector = new SystemInjector(context); mProviderManager = new DiscoveryProviderManager(context, mInjector); mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector); - final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null); - mFastPairManager = new FastPairManager(lcw); } @VisibleForTesting @@ -152,10 +145,6 @@ public class NearbyService extends INearbyManager.Stub { ((SystemInjector) mInjector).initializeAppOpsManager(); } break; - case PHASE_THIRD_PARTY_APPS_CAN_START: - // Ensures that a fast pair data provider exists which will work in direct boot. - FastPairDataProvider.init(mContext); - break; case PHASE_BOOT_COMPLETED: if (mInjector instanceof SystemInjector) { // The nearby service must be functioning after this boot phase. @@ -166,7 +155,6 @@ public class NearbyService extends INearbyManager.Stub { mContext.registerReceiver( mBluetoothReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); - mFastPairManager.initiate(); break; } } diff --git a/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java b/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java deleted file mode 100644 index 23d5170947b454c62c6dcd0c12b007d020dc81b4..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java +++ /dev/null @@ -1,746 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.ScanFilter; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.text.TextUtils; - -import androidx.annotation.Nullable; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -/** - * Criteria for filtering BLE devices. A {@link BleFilter} allows clients to restrict BLE devices to - * only those that are of interest to them. - * - * - *

Current filtering on the following fields are supported: - *

  • Service UUIDs which identify the bluetooth gatt services running on the device. - *
  • Name of remote Bluetooth LE device. - *
  • Mac address of the remote device. - *
  • Service data which is the data associated with a service. - *
  • Manufacturer specific data which is the data associated with a particular manufacturer. - * - * @see BleSighting - */ -public final class BleFilter implements Parcelable { - - @Nullable - private String mDeviceName; - - @Nullable - private String mDeviceAddress; - - @Nullable - private ParcelUuid mServiceUuid; - - @Nullable - private ParcelUuid mServiceUuidMask; - - @Nullable - private ParcelUuid mServiceDataUuid; - - @Nullable - private byte[] mServiceData; - - @Nullable - private byte[] mServiceDataMask; - - private int mManufacturerId; - - @Nullable - private byte[] mManufacturerData; - - @Nullable - private byte[] mManufacturerDataMask; - - @Override - public int describeContents() { - return 0; - } - - BleFilter() { - } - - BleFilter( - @Nullable String deviceName, - @Nullable String deviceAddress, - @Nullable ParcelUuid serviceUuid, - @Nullable ParcelUuid serviceUuidMask, - @Nullable ParcelUuid serviceDataUuid, - @Nullable byte[] serviceData, - @Nullable byte[] serviceDataMask, - int manufacturerId, - @Nullable byte[] manufacturerData, - @Nullable byte[] manufacturerDataMask) { - this.mDeviceName = deviceName; - this.mDeviceAddress = deviceAddress; - this.mServiceUuid = serviceUuid; - this.mServiceUuidMask = serviceUuidMask; - this.mServiceDataUuid = serviceDataUuid; - this.mServiceData = serviceData; - this.mServiceDataMask = serviceDataMask; - this.mManufacturerId = manufacturerId; - this.mManufacturerData = manufacturerData; - this.mManufacturerDataMask = manufacturerDataMask; - } - - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public BleFilter createFromParcel(Parcel source) { - BleFilter nBleFilter = new BleFilter(); - nBleFilter.mDeviceName = source.readString(); - nBleFilter.mDeviceAddress = source.readString(); - nBleFilter.mManufacturerId = source.readInt(); - nBleFilter.mManufacturerData = source.marshall(); - nBleFilter.mManufacturerDataMask = source.marshall(); - nBleFilter.mServiceDataUuid = source.readParcelable(null); - nBleFilter.mServiceData = source.marshall(); - nBleFilter.mServiceDataMask = source.marshall(); - nBleFilter.mServiceUuid = source.readParcelable(null); - nBleFilter.mServiceUuidMask = source.readParcelable(null); - return nBleFilter; - } - - @Override - public BleFilter[] newArray(int size) { - return new BleFilter[size]; - } - }; - - - /** Returns the filter set on the device name field of Bluetooth advertisement data. */ - @Nullable - public String getDeviceName() { - return mDeviceName; - } - - /** Returns the filter set on the service uuid. */ - @Nullable - public ParcelUuid getServiceUuid() { - return mServiceUuid; - } - - /** Returns the mask for the service uuid. */ - @Nullable - public ParcelUuid getServiceUuidMask() { - return mServiceUuidMask; - } - - /** Returns the filter set on the device address. */ - @Nullable - public String getDeviceAddress() { - return mDeviceAddress; - } - - /** Returns the filter set on the service data. */ - @Nullable - public byte[] getServiceData() { - return mServiceData; - } - - /** Returns the mask for the service data. */ - @Nullable - public byte[] getServiceDataMask() { - return mServiceDataMask; - } - - /** Returns the filter set on the service data uuid. */ - @Nullable - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; - } - - /** Returns the manufacturer id. -1 if the manufacturer filter is not set. */ - public int getManufacturerId() { - return mManufacturerId; - } - - /** Returns the filter set on the manufacturer data. */ - @Nullable - public byte[] getManufacturerData() { - return mManufacturerData; - } - - /** Returns the mask for the manufacturer data. */ - @Nullable - public byte[] getManufacturerDataMask() { - return mManufacturerDataMask; - } - - /** - * Check if the filter matches a {@code BleSighting}. A BLE sighting is considered as a match if - * it matches all the field filters. - */ - public boolean matches(@Nullable BleSighting bleSighting) { - if (bleSighting == null) { - return false; - } - BluetoothDevice device = bleSighting.getDevice(); - // Device match. - if (mDeviceAddress != null && (device == null || !mDeviceAddress.equals( - device.getAddress()))) { - return false; - } - - BleRecord bleRecord = bleSighting.getBleRecord(); - - // Scan record is null but there exist filters on it. - if (bleRecord == null - && (mDeviceName != null - || mServiceUuid != null - || mManufacturerData != null - || mServiceData != null)) { - return false; - } - - // Local name match. - if (mDeviceName != null && !mDeviceName.equals(bleRecord.getDeviceName())) { - return false; - } - - // UUID match. - if (mServiceUuid != null - && !matchesServiceUuids(mServiceUuid, mServiceUuidMask, - bleRecord.getServiceUuids())) { - return false; - } - - // Service data match - if (mServiceDataUuid != null - && !matchesPartialData( - mServiceData, mServiceDataMask, bleRecord.getServiceData(mServiceDataUuid))) { - return false; - } - - // Manufacturer data match. - if (mManufacturerId >= 0 - && !matchesPartialData( - mManufacturerData, - mManufacturerDataMask, - bleRecord.getManufacturerSpecificData(mManufacturerId))) { - return false; - } - - // All filters match. - return true; - } - - /** - * Determines if the characteristics of this filter are a superset of the characteristics of the - * given filter. - */ - public boolean isSuperset(@Nullable BleFilter bleFilter) { - if (bleFilter == null) { - return false; - } - - if (equals(bleFilter)) { - return true; - } - - // Verify device address matches. - if (mDeviceAddress != null && !mDeviceAddress.equals(bleFilter.getDeviceAddress())) { - return false; - } - - // Verify device name matches. - if (mDeviceName != null && !mDeviceName.equals(bleFilter.getDeviceName())) { - return false; - } - - // Verify UUID is a superset. - if (mServiceUuid != null - && !serviceUuidIsSuperset( - mServiceUuid, - mServiceUuidMask, - bleFilter.getServiceUuid(), - bleFilter.getServiceUuidMask())) { - return false; - } - - // Verify service data is a superset. - if (mServiceDataUuid != null - && (!mServiceDataUuid.equals(bleFilter.getServiceDataUuid()) - || !partialDataIsSuperset( - mServiceData, - mServiceDataMask, - bleFilter.getServiceData(), - bleFilter.getServiceDataMask()))) { - return false; - } - - // Verify manufacturer data is a superset. - if (mManufacturerId >= 0 - && (mManufacturerId != bleFilter.getManufacturerId() - || !partialDataIsSuperset( - mManufacturerData, - mManufacturerDataMask, - bleFilter.getManufacturerData(), - bleFilter.getManufacturerDataMask()))) { - return false; - } - - return true; - } - - /** Determines if the first uuid and mask are a superset of the second uuid and mask. */ - private static boolean serviceUuidIsSuperset( - @Nullable ParcelUuid uuid1, - @Nullable ParcelUuid uuidMask1, - @Nullable ParcelUuid uuid2, - @Nullable ParcelUuid uuidMask2) { - // First uuid1 is null so it can match any service UUID. - if (uuid1 == null) { - return true; - } - - // uuid2 is a superset of uuid1, but not the other way around. - if (uuid2 == null) { - return false; - } - - // Without a mask, the uuids must match. - if (uuidMask1 == null) { - return uuid1.equals(uuid2); - } - - // Mask2 should be at least as specific as mask1. - if (uuidMask2 != null) { - long uuid1MostSig = uuidMask1.getUuid().getMostSignificantBits(); - long uuid1LeastSig = uuidMask1.getUuid().getLeastSignificantBits(); - long uuid2MostSig = uuidMask2.getUuid().getMostSignificantBits(); - long uuid2LeastSig = uuidMask2.getUuid().getLeastSignificantBits(); - if (((uuid1MostSig & uuid2MostSig) != uuid1MostSig) - || ((uuid1LeastSig & uuid2LeastSig) != uuid1LeastSig)) { - return false; - } - } - - if (!matchesServiceUuids(uuid1, uuidMask1, Arrays.asList(uuid2))) { - return false; - } - - return true; - } - - /** Determines if the first data and mask are the superset of the second data and mask. */ - private static boolean partialDataIsSuperset( - @Nullable byte[] data1, - @Nullable byte[] dataMask1, - @Nullable byte[] data2, - @Nullable byte[] dataMask2) { - if (Arrays.equals(data1, data2) && Arrays.equals(dataMask1, dataMask2)) { - return true; - } - - if (data1 == null) { - return true; - } - - if (data2 == null) { - return false; - } - - // Mask2 should be at least as specific as mask1. - if (dataMask1 != null && dataMask2 != null) { - for (int i = 0, j = 0; i < dataMask1.length && j < dataMask2.length; i++, j++) { - if ((dataMask1[i] & dataMask2[j]) != dataMask1[i]) { - return false; - } - } - } - - if (!matchesPartialData(data1, dataMask1, data2)) { - return false; - } - - return true; - } - - /** Check if the uuid pattern is contained in a list of parcel uuids. */ - private static boolean matchesServiceUuids( - @Nullable ParcelUuid uuid, @Nullable ParcelUuid parcelUuidMask, - List uuids) { - if (uuid == null) { - // No service uuid filter has been set, so there's a match. - return true; - } - - UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); - for (ParcelUuid parcelUuid : uuids) { - if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { - return true; - } - } - return false; - } - - /** Check if the uuid pattern matches the particular service uuid. */ - private static boolean matchesServiceUuid(UUID uuid, @Nullable UUID mask, UUID data) { - if (mask == null) { - return uuid.equals(data); - } - if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) - != (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { - return false; - } - return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) - == (data.getMostSignificantBits() & mask.getMostSignificantBits())); - } - - /** - * Check whether the data pattern matches the parsed data. Assumes that {@code data} and {@code - * dataMask} have the same length. - */ - /* package */ - static boolean matchesPartialData( - @Nullable byte[] data, @Nullable byte[] dataMask, @Nullable byte[] parsedData) { - if (data == null || parsedData == null || parsedData.length < data.length) { - return false; - } - if (dataMask == null) { - for (int i = 0; i < data.length; ++i) { - if (parsedData[i] != data[i]) { - return false; - } - } - return true; - } - for (int i = 0; i < data.length; ++i) { - if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { - return false; - } - } - return true; - } - - @Override - public String toString() { - return "BleFilter [deviceName=" - + mDeviceName - + ", deviceAddress=" - + mDeviceAddress - + ", uuid=" - + mServiceUuid - + ", uuidMask=" - + mServiceUuidMask - + ", serviceDataUuid=" - + mServiceDataUuid - + ", serviceData=" - + Arrays.toString(mServiceData) - + ", serviceDataMask=" - + Arrays.toString(mServiceDataMask) - + ", manufacturerId=" - + mManufacturerId - + ", manufacturerData=" - + Arrays.toString(mManufacturerData) - + ", manufacturerDataMask=" - + Arrays.toString(mManufacturerDataMask) - + "]"; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(mDeviceName); - out.writeString(mDeviceAddress); - out.writeInt(mManufacturerId); - out.writeByteArray(mManufacturerData); - out.writeByteArray(mManufacturerDataMask); - out.writeParcelable(mServiceDataUuid, flags); - out.writeByteArray(mServiceData); - out.writeByteArray(mServiceDataMask); - out.writeParcelable(mServiceUuid, flags); - out.writeParcelable(mServiceUuidMask, flags); - } - - @Override - public int hashCode() { - return Objects.hash( - mDeviceName, - mDeviceAddress, - mManufacturerId, - Arrays.hashCode(mManufacturerData), - Arrays.hashCode(mManufacturerDataMask), - mServiceDataUuid, - Arrays.hashCode(mServiceData), - Arrays.hashCode(mServiceDataMask), - mServiceUuid, - mServiceUuidMask); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - BleFilter other = (BleFilter) obj; - return mDeviceName.equals(other.mDeviceName) - && mDeviceAddress.equals(other.mDeviceAddress) - && mManufacturerId == other.mManufacturerId - && Arrays.equals(mManufacturerData, other.mManufacturerData) - && Arrays.equals(mManufacturerDataMask, other.mManufacturerDataMask) - && mServiceDataUuid.equals(other.mServiceDataUuid) - && Arrays.equals(mServiceData, other.mServiceData) - && Arrays.equals(mServiceDataMask, other.mServiceDataMask) - && mServiceUuid.equals(other.mServiceUuid) - && mServiceUuidMask.equals(other.mServiceUuidMask); - } - - /** Builder class for {@link BleFilter}. */ - public static final class Builder { - - private String mDeviceName; - private String mDeviceAddress; - - @Nullable - private ParcelUuid mServiceUuid; - @Nullable - private ParcelUuid mUuidMask; - - private ParcelUuid mServiceDataUuid; - @Nullable - private byte[] mServiceData; - @Nullable - private byte[] mServiceDataMask; - - private int mManufacturerId = -1; - private byte[] mManufacturerData; - @Nullable - private byte[] mManufacturerDataMask; - - /** Set filter on device name. */ - public Builder setDeviceName(String deviceName) { - this.mDeviceName = deviceName; - return this; - } - - /** - * Set filter on device address. - * - * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the - * format of "01:02:03:AB:CD:EF". The device address can be validated - * using {@link - * BluetoothAdapter#checkBluetoothAddress}. - * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. - */ - public Builder setDeviceAddress(String deviceAddress) { - if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { - throw new IllegalArgumentException("invalid device address " + deviceAddress); - } - this.mDeviceAddress = deviceAddress; - return this; - } - - /** Set filter on service uuid. */ - public Builder setServiceUuid(@Nullable ParcelUuid serviceUuid) { - this.mServiceUuid = serviceUuid; - mUuidMask = null; // clear uuid mask - return this; - } - - /** - * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the {@code - * serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the bit in - * {@code serviceUuid}, and 0 to ignore that bit. - * - * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code - * uuidMask} - * is not {@code null}. - */ - public Builder setServiceUuid(@Nullable ParcelUuid serviceUuid, - @Nullable ParcelUuid uuidMask) { - if (uuidMask != null && serviceUuid == null) { - throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); - } - this.mServiceUuid = serviceUuid; - this.mUuidMask = uuidMask; - return this; - } - - /** - * Set filtering on service data. - */ - public Builder setServiceData(ParcelUuid serviceDataUuid, @Nullable byte[] serviceData) { - this.mServiceDataUuid = serviceDataUuid; - this.mServiceData = serviceData; - mServiceDataMask = null; // clear service data mask - return this; - } - - /** - * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to - * match - * the one in service data, otherwise set it to 0 to ignore that bit. - * - *

    The {@code serviceDataMask} must have the same length of the {@code serviceData}. - * - * @throws IllegalArgumentException If {@code serviceDataMask} is {@code null} while {@code - * serviceData} is not or {@code serviceDataMask} and - * {@code serviceData} has different - * length. - */ - public Builder setServiceData( - ParcelUuid serviceDataUuid, - @Nullable byte[] serviceData, - @Nullable byte[] serviceDataMask) { - if (serviceDataMask != null) { - if (serviceData == null) { - throw new IllegalArgumentException( - "serviceData is null while serviceDataMask is not null"); - } - // Since the serviceDataMask is a bit mask for serviceData, the lengths of the two - // byte array need to be the same. - if (serviceData.length != serviceDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for service data and service data mask"); - } - } - this.mServiceDataUuid = serviceDataUuid; - this.mServiceData = serviceData; - this.mServiceDataMask = serviceDataMask; - return this; - } - - /** - * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. - * - *

    Note the first two bytes of the {@code manufacturerData} is the manufacturerId. - * - * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. - */ - public Builder setManufacturerData(int manufacturerId, @Nullable byte[] manufacturerData) { - return setManufacturerData(manufacturerId, manufacturerData, null /* mask */); - } - - /** - * Set filter on partial manufacture data. For any bit in the mask, set it to 1 if it needs - * to - * match the one in manufacturer data, otherwise set it to 0. - * - *

    The {@code manufacturerDataMask} must have the same length of {@code - * manufacturerData}. - * - * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code - * manufacturerData} is null while {@code - * manufacturerDataMask} is not, or {@code - * manufacturerData} and {@code manufacturerDataMask} have - * different length. - */ - public Builder setManufacturerData( - int manufacturerId, - @Nullable byte[] manufacturerData, - @Nullable byte[] manufacturerDataMask) { - if (manufacturerData != null && manufacturerId < 0) { - throw new IllegalArgumentException("invalid manufacture id"); - } - if (manufacturerDataMask != null) { - if (manufacturerData == null) { - throw new IllegalArgumentException( - "manufacturerData is null while manufacturerDataMask is not null"); - } - // Since the manufacturerDataMask is a bit mask for manufacturerData, the lengths - // of the two byte array need to be the same. - if (manufacturerData.length != manufacturerDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for manufacturerData and manufacturerDataMask"); - } - } - this.mManufacturerId = manufacturerId; - this.mManufacturerData = manufacturerData == null ? new byte[0] : manufacturerData; - this.mManufacturerDataMask = manufacturerDataMask; - return this; - } - - - /** - * Builds the filter. - * - * @throws IllegalArgumentException If the filter cannot be built. - */ - public BleFilter build() { - return new BleFilter( - mDeviceName, - mDeviceAddress, - mServiceUuid, - mUuidMask, - mServiceDataUuid, - mServiceData, - mServiceDataMask, - mManufacturerId, - mManufacturerData, - mManufacturerDataMask); - } - } - - /** - * Changes ble filter to os filter - */ - public ScanFilter toOsFilter() { - ScanFilter.Builder osFilterBuilder = new ScanFilter.Builder(); - if (!TextUtils.isEmpty(getDeviceAddress())) { - osFilterBuilder.setDeviceAddress(getDeviceAddress()); - } - if (!TextUtils.isEmpty(getDeviceName())) { - osFilterBuilder.setDeviceName(getDeviceName()); - } - - byte[] manufacturerData = getManufacturerData(); - if (getManufacturerId() != -1 && manufacturerData != null) { - byte[] manufacturerDataMask = getManufacturerDataMask(); - if (manufacturerDataMask != null) { - osFilterBuilder.setManufacturerData( - getManufacturerId(), manufacturerData, manufacturerDataMask); - } else { - osFilterBuilder.setManufacturerData(getManufacturerId(), manufacturerData); - } - } - - ParcelUuid serviceDataUuid = getServiceDataUuid(); - byte[] serviceData = getServiceData(); - if (serviceDataUuid != null && serviceData != null) { - byte[] serviceDataMask = getServiceDataMask(); - if (serviceDataMask != null) { - osFilterBuilder.setServiceData(serviceDataUuid, serviceData, serviceDataMask); - } else { - osFilterBuilder.setServiceData(serviceDataUuid, serviceData); - } - } - - ParcelUuid serviceUuid = getServiceUuid(); - if (serviceUuid != null) { - ParcelUuid serviceUuidMask = getServiceUuidMask(); - if (serviceUuidMask != null) { - osFilterBuilder.setServiceUuid(serviceUuid, serviceUuidMask); - } else { - osFilterBuilder.setServiceUuid(serviceUuid); - } - } - return osFilterBuilder.build(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/ble/BleRecord.java b/nearby/service/java/com/android/server/nearby/common/ble/BleRecord.java deleted file mode 100644 index 103a27fe8036812154510a8c6bcc3ddc3ed46540..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/BleRecord.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble; - -import android.os.ParcelUuid; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.Nullable; - -import com.android.server.nearby.common.ble.util.StringUtils; - -import com.google.common.collect.ImmutableList; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * Represents a BLE record from Bluetooth LE scan. - */ -public final class BleRecord { - - // The following data type values are assigned by Bluetooth SIG. - // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18. - private static final int DATA_TYPE_FLAGS = 0x01; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; - private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; - private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; - private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; - private static final int DATA_TYPE_SERVICE_DATA = 0x16; - private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; - - /** The base 128-bit UUID representation of a 16-bit UUID. */ - private static final ParcelUuid BASE_UUID = - ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); - /** Length of bytes for 16 bit UUID. */ - private static final int UUID_BYTES_16_BIT = 2; - /** Length of bytes for 32 bit UUID. */ - private static final int UUID_BYTES_32_BIT = 4; - /** Length of bytes for 128 bit UUID. */ - private static final int UUID_BYTES_128_BIT = 16; - - // Flags of the advertising data. - // -1 when the scan record is not valid. - private final int mAdvertiseFlags; - - private final ImmutableList mServiceUuids; - - // null when the scan record is not valid. - @Nullable - private final SparseArray mManufacturerSpecificData; - - // null when the scan record is not valid. - @Nullable - private final Map mServiceData; - - // Transmission power level(in dB). - // Integer.MIN_VALUE when the scan record is not valid. - private final int mTxPowerLevel; - - // Local name of the Bluetooth LE device. - // null when the scan record is not valid. - @Nullable - private final String mDeviceName; - - // Raw bytes of scan record. - // Never null, whether valid or not. - private final byte[] mBytes; - - // If the raw scan record byte[] cannot be parsed, all non-primitive args here other than the - // raw scan record byte[] and serviceUudis will be null. See parsefromBytes(). - private BleRecord( - List serviceUuids, - @Nullable SparseArray manufacturerData, - @Nullable Map serviceData, - int advertiseFlags, - int txPowerLevel, - @Nullable String deviceName, - byte[] bytes) { - this.mServiceUuids = ImmutableList.copyOf(serviceUuids); - mManufacturerSpecificData = manufacturerData; - this.mServiceData = serviceData; - this.mDeviceName = deviceName; - this.mAdvertiseFlags = advertiseFlags; - this.mTxPowerLevel = txPowerLevel; - this.mBytes = bytes; - } - - /** - * Returns a list of service UUIDs within the advertisement that are used to identify the - * bluetooth GATT services. - */ - public ImmutableList getServiceUuids() { - return mServiceUuids; - } - - /** - * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific - * data. - */ - @Nullable - public SparseArray getManufacturerSpecificData() { - return mManufacturerSpecificData; - } - - /** - * Returns the manufacturer specific data associated with the manufacturer id. Returns {@code - * null} if the {@code manufacturerId} is not found. - */ - @Nullable - public byte[] getManufacturerSpecificData(int manufacturerId) { - if (mManufacturerSpecificData == null) { - return null; - } - return mManufacturerSpecificData.get(manufacturerId); - } - - /** Returns a map of service UUID and its corresponding service data. */ - @Nullable - public Map getServiceData() { - return mServiceData; - } - - /** - * Returns the service data byte array associated with the {@code serviceUuid}. Returns {@code - * null} if the {@code serviceDataUuid} is not found. - */ - @Nullable - public byte[] getServiceData(ParcelUuid serviceDataUuid) { - if (serviceDataUuid == null || mServiceData == null) { - return null; - } - return mServiceData.get(serviceDataUuid); - } - - /** - * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE} - * if - * the field is not set. This value can be used to calculate the path loss of a received packet - * using the following equation: - * - *

    pathloss = txPowerLevel - rssi - */ - public int getTxPowerLevel() { - return mTxPowerLevel; - } - - /** Returns the local name of the BLE device. The is a UTF-8 encoded string. */ - @Nullable - public String getDeviceName() { - return mDeviceName; - } - - /** Returns raw bytes of scan record. */ - public byte[] getBytes() { - return mBytes; - } - - /** - * Parse scan record bytes to {@link BleRecord}. - * - *

    The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18. - * - *

    All numerical multi-byte entities and values shall use little-endian byte - * order. - * - * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. - */ - public static BleRecord parseFromBytes(byte[] scanRecord) { - int currentPos = 0; - int advertiseFlag = -1; - List serviceUuids = new ArrayList<>(); - String localName = null; - int txPowerLevel = Integer.MIN_VALUE; - - SparseArray manufacturerData = new SparseArray<>(); - Map serviceData = new HashMap<>(); - - try { - while (currentPos < scanRecord.length) { - // length is unsigned int. - int length = scanRecord[currentPos++] & 0xFF; - if (length == 0) { - break; - } - // Note the length includes the length of the field type itself. - int dataLength = length - 1; - // fieldType is unsigned int. - int fieldType = scanRecord[currentPos++] & 0xFF; - switch (fieldType) { - case DATA_TYPE_FLAGS: - advertiseFlag = scanRecord[currentPos] & 0xFF; - break; - case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, UUID_BYTES_16_BIT, - serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, UUID_BYTES_32_BIT, - serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, UUID_BYTES_128_BIT, - serviceUuids); - break; - case DATA_TYPE_LOCAL_NAME_SHORT: - case DATA_TYPE_LOCAL_NAME_COMPLETE: - localName = new String(extractBytes(scanRecord, currentPos, dataLength)); - break; - case DATA_TYPE_TX_POWER_LEVEL: - txPowerLevel = scanRecord[currentPos]; - break; - case DATA_TYPE_SERVICE_DATA: - // The first two bytes of the service data are service data UUID in little - // endian. The rest bytes are service data. - int serviceUuidLength = UUID_BYTES_16_BIT; - byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, - serviceUuidLength); - ParcelUuid serviceDataUuid = parseUuidFrom(serviceDataUuidBytes); - byte[] serviceDataArray = - extractBytes( - scanRecord, currentPos + serviceUuidLength, - dataLength - serviceUuidLength); - serviceData.put(serviceDataUuid, serviceDataArray); - break; - case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: - // The first two bytes of the manufacturer specific data are - // manufacturer ids in little endian. - int manufacturerId = - ((scanRecord[currentPos + 1] & 0xFF) << 8) + (scanRecord[currentPos] - & 0xFF); - byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2, - dataLength - 2); - manufacturerData.put(manufacturerId, manufacturerDataBytes); - break; - default: - // Just ignore, we don't handle such data type. - break; - } - currentPos += dataLength; - } - - return new BleRecord( - serviceUuids, - manufacturerData, - serviceData, - advertiseFlag, - txPowerLevel, - localName, - scanRecord); - } catch (Exception e) { - Log.w("BleRecord", "Unable to parse scan record: " + Arrays.toString(scanRecord), e); - // As the record is invalid, ignore all the parsed results for this packet - // and return an empty record with raw scanRecord bytes in results - // check at the top of this method does? Maybe we expect callers to use the - // scanRecord part in - // some fallback. But if that's the reason, it would seem we still can return null. - // They still - // have the raw scanRecord in hand, 'cause they passed it to us. It seems too easy for a - // caller to misuse this "empty" BleRecord (as in b/22693067). - return new BleRecord(ImmutableList.of(), null, null, -1, Integer.MIN_VALUE, null, - scanRecord); - } - } - - // Parse service UUIDs. - private static int parseServiceUuid( - byte[] scanRecord, - int currentPos, - int dataLength, - int uuidLength, - List serviceUuids) { - while (dataLength > 0) { - byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength); - serviceUuids.add(parseUuidFrom(uuidBytes)); - dataLength -= uuidLength; - currentPos += uuidLength; - } - return currentPos; - } - - // Helper method to extract bytes from byte array. - private static byte[] extractBytes(byte[] scanRecord, int start, int length) { - byte[] bytes = new byte[length]; - System.arraycopy(scanRecord, start, bytes, 0, length); - return bytes; - } - - @Override - public String toString() { - return "BleRecord [advertiseFlags=" - + mAdvertiseFlags - + ", serviceUuids=" - + mServiceUuids - + ", manufacturerSpecificData=" - + StringUtils.toString(mManufacturerSpecificData) - + ", serviceData=" - + StringUtils.toString(mServiceData) - + ", txPowerLevel=" - + mTxPowerLevel - + ", deviceName=" - + mDeviceName - + "]"; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof BleRecord)) { - return false; - } - BleRecord record = (BleRecord) obj; - // BleRecord objects are built from bytes, so we only need that field. - return Arrays.equals(mBytes, record.mBytes); - } - - @Override - public int hashCode() { - // BleRecord objects are built from bytes, so we only need that field. - return Arrays.hashCode(mBytes); - } - - /** - * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID, - * but the returned UUID is always in 128-bit format. Note UUID is little endian in Bluetooth. - * - * @param uuidBytes Byte representation of uuid. - * @return {@link ParcelUuid} parsed from bytes. - * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. - */ - private static ParcelUuid parseUuidFrom(byte[] uuidBytes) { - if (uuidBytes == null) { - throw new IllegalArgumentException("uuidBytes cannot be null"); - } - int length = uuidBytes.length; - if (length != UUID_BYTES_16_BIT - && length != UUID_BYTES_32_BIT - && length != UUID_BYTES_128_BIT) { - throw new IllegalArgumentException("uuidBytes length invalid - " + length); - } - // Construct a 128 bit UUID. - if (length == UUID_BYTES_128_BIT) { - ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); - long msb = buf.getLong(8); - long lsb = buf.getLong(0); - return new ParcelUuid(new UUID(msb, lsb)); - } - // For 16 bit and 32 bit UUID we need to convert them to 128 bit value. - // 128_bit_value = uuid * 2^96 + BASE_UUID - long shortUuid; - if (length == UUID_BYTES_16_BIT) { - shortUuid = uuidBytes[0] & 0xFF; - shortUuid += (uuidBytes[1] & 0xFF) << 8; - } else { - shortUuid = uuidBytes[0] & 0xFF; - shortUuid += (uuidBytes[1] & 0xFF) << 8; - shortUuid += (uuidBytes[2] & 0xFF) << 16; - shortUuid += (uuidBytes[3] & 0xFF) << 24; - } - long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32); - long lsb = BASE_UUID.getUuid().getLeastSignificantBits(); - return new ParcelUuid(new UUID(msb, lsb)); - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/ble/BleSighting.java b/nearby/service/java/com/android/server/nearby/common/ble/BleSighting.java deleted file mode 100644 index 71ec10cef1b27741e055db440a78db217e51f8e9..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/BleSighting.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.ScanRecord; -import android.bluetooth.le.ScanResult; -import android.os.Build.VERSION_CODES; -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.annotation.VisibleForTesting; - -import java.util.Arrays; -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -/** - * A sighting of a BLE device found in a Bluetooth LE scan. - */ - -public class BleSighting implements Parcelable { - - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public BleSighting createFromParcel(Parcel source) { - BleSighting nBleSighting = new BleSighting(source.readParcelable(null), - source.marshall(), source.readInt(), source.readLong()); - return null; - } - - @Override - public BleSighting[] newArray(int size) { - return new BleSighting[size]; - } - }; - - // Max and min rssi value which is from {@link android.bluetooth.le.ScanResult#getRssi()}. - @VisibleForTesting - public static final int MAX_RSSI_VALUE = 126; - @VisibleForTesting - public static final int MIN_RSSI_VALUE = -127; - - /** Remote bluetooth device. */ - private final BluetoothDevice mDevice; - - /** - * BLE record, including advertising data and response data. BleRecord is not parcelable, so - * this - * is created from bleRecordBytes. - */ - private final BleRecord mBleRecord; - - /** The bytes of a BLE record. */ - private final byte[] mBleRecordBytes; - - /** Received signal strength. */ - private final int mRssi; - - /** Nanos timestamp when the ble device was observed (epoch time). */ - private final long mTimestampEpochNanos; - - /** - * Constructor of a BLE sighting. - * - * @param device Remote bluetooth device that is found. - * @param bleRecordBytes The bytes that will create a BleRecord. - * @param rssi Received signal strength. - * @param timestampEpochNanos Nanos timestamp when the BLE device was observed (epoch time). - */ - public BleSighting(BluetoothDevice device, byte[] bleRecordBytes, int rssi, - long timestampEpochNanos) { - this.mDevice = device; - this.mBleRecordBytes = bleRecordBytes; - this.mRssi = rssi; - this.mTimestampEpochNanos = timestampEpochNanos; - mBleRecord = BleRecord.parseFromBytes(bleRecordBytes); - } - - @Override - public int describeContents() { - return 0; - } - - /** Returns the remote bluetooth device identified by the bluetooth device address. */ - public BluetoothDevice getDevice() { - return mDevice; - } - - /** Returns the BLE record, which is a combination of advertisement and scan response. */ - public BleRecord getBleRecord() { - return mBleRecord; - } - - /** Returns the bytes of the BLE record. */ - public byte[] getBleRecordBytes() { - return mBleRecordBytes; - } - - /** Returns the received signal strength in dBm. The valid range is [-127, 127]. */ - public int getRssi() { - return mRssi; - } - - /** - * Returns the received signal strength normalized with the offset specific to the given device. - * 3 is the rssi offset to calculate fast init distance. - *

    This method utilized the rssi offset maintained by Nearby Sharing. - * - * @return normalized rssi which is between [-127, 126] according to {@link - * android.bluetooth.le.ScanResult#getRssi()}. - */ - public int getNormalizedRSSI() { - int adjustedRssi = mRssi + 3; - if (adjustedRssi < MIN_RSSI_VALUE) { - return MIN_RSSI_VALUE; - } else if (adjustedRssi > MAX_RSSI_VALUE) { - return MAX_RSSI_VALUE; - } else { - return adjustedRssi; - } - } - - /** Returns timestamp in epoch time when the scan record was observed. */ - public long getTimestampNanos() { - return mTimestampEpochNanos; - } - - /** Returns timestamp in epoch time when the scan record was observed, in millis. */ - public long getTimestampMillis() { - return TimeUnit.NANOSECONDS.toMillis(mTimestampEpochNanos); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mDevice, flags); - dest.writeByteArray(mBleRecordBytes); - dest.writeInt(mRssi); - dest.writeLong(mTimestampEpochNanos); - } - - @Override - public int hashCode() { - return Objects.hash(mDevice, mRssi, mTimestampEpochNanos, Arrays.hashCode(mBleRecordBytes)); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BleSighting)) { - return false; - } - BleSighting other = (BleSighting) obj; - return Objects.equals(mDevice, other.mDevice) - && mRssi == other.mRssi - && Arrays.equals(mBleRecordBytes, other.mBleRecordBytes) - && mTimestampEpochNanos == other.mTimestampEpochNanos; - } - - @Override - public String toString() { - return "BleSighting{" - + "device=" - + mDevice - + ", bleRecord=" - + mBleRecord - + ", rssi=" - + mRssi - + ", timestampNanos=" - + mTimestampEpochNanos - + "}"; - } - - /** Creates {@link BleSighting} using the {@link ScanResult}. */ - @RequiresApi(api = VERSION_CODES.LOLLIPOP) - @Nullable - public static BleSighting createFromOsScanResult(ScanResult osResult) { - ScanRecord osScanRecord = osResult.getScanRecord(); - if (osScanRecord == null) { - return null; - } - - return new BleSighting( - osResult.getDevice(), - osScanRecord.getBytes(), - osResult.getRssi(), - // The timestamp from ScanResult is 'nanos since boot', Beacon lib will change it - // as 'nanos - // since epoch', but Nearby never reference this field, just pass it as 'nanos - // since boot'. - // ref to beacon/scan/impl/LBluetoothLeScannerCompat.fromOs for beacon design - // about how to - // convert nanos since boot to epoch. - osResult.getTimestampNanos()); - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/ble/decode/BeaconDecoder.java b/nearby/service/java/com/android/server/nearby/common/ble/decode/BeaconDecoder.java deleted file mode 100644 index 9e795ac7a1c26db224b7af4abb4f56160015744d..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/decode/BeaconDecoder.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble.decode; - -import androidx.annotation.Nullable; - -import com.android.server.nearby.common.ble.BleRecord; - -/** - * This class encapsulates the logic specific to each manufacturer for parsing formats for beacons, - * and presents a common API to access important ADV/EIR packet fields such as: - * - *

      - *
    • UUID (universally unique identifier), a value uniquely identifying a group of one or - * more beacons as belonging to an organization or of a certain type, up to 128 bits. - *
    • Instance a 32-bit unsigned integer that can be used to group related beacons that - * have the same UUID. - *
    • the mathematics of TX signal strength, used for proximity calculations. - *
    - * - * ...and others. - * - * @see BLE Glossary - * @see Bluetooth - * Data Types Specification - */ -public abstract class BeaconDecoder { - /** - * Returns true if the bleRecord corresponds to a beacon format that contains sufficient - * information to construct a BeaconId and contains the Tx power. - */ - public boolean supportsBeaconIdAndTxPower(@SuppressWarnings("unused") BleRecord bleRecord) { - return true; - } - - /** - * Returns true if this decoder supports returning TxPower via {@link - * #getCalibratedBeaconTxPower(BleRecord)}. - */ - public boolean supportsTxPower() { - return true; - } - - /** - * Reads the calibrated transmitted power at 1 meter of the beacon in dBm. This value is - * contained - * in the scan record, as set by the transmitting beacon. Suitable for use in computing path - * loss, - * distance, and related derived values. - * - * @param bleRecord the parsed payload contained in the beacon packet - * @return integer value of the calibrated Tx power in dBm or null if the bleRecord doesn't - * contain sufficient information to calculate the Tx power. - */ - @Nullable - public abstract Integer getCalibratedBeaconTxPower(BleRecord bleRecord); - - /** - * Extract telemetry information from the beacon. Byte 0 of the returned telemetry block should - * encode the telemetry format. - * - * @return telemetry block for this beacon, or null if no telemetry data is found in the scan - * record. - */ - @Nullable - public byte[] getTelemetry(@SuppressWarnings("unused") BleRecord bleRecord) { - return null; - } - - /** Returns the appropriate type for this scan record. */ - public abstract int getBeaconIdType(); - - /** - * Returns an array of bytes which uniquely identify this beacon, for beacons from any of the - * supported beacon types. This unique identifier is the indexing key for various internal - * services. Returns null if the bleRecord doesn't contain sufficient information to construct - * the - * ID. - */ - @Nullable - public abstract byte[] getBeaconIdBytes(BleRecord bleRecord); - - /** - * Returns the URL of the beacon. Returns null if the bleRecord doesn't contain a URL or - * contains - * a malformed URL. - */ - @Nullable - public String getUrl(BleRecord bleRecord) { - return null; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/ble/decode/FastPairDecoder.java b/nearby/service/java/com/android/server/nearby/common/ble/decode/FastPairDecoder.java deleted file mode 100644 index c1ff9fd31e7c9c681adfecb241fcaab2d7f494a3..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/decode/FastPairDecoder.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble.decode; - -import android.bluetooth.le.ScanRecord; -import android.os.ParcelUuid; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.Nullable; - -import com.android.server.nearby.common.ble.BleFilter; -import com.android.server.nearby.common.ble.BleRecord; - -import java.util.Arrays; - -/** - * Parses Fast Pair information out of {@link BleRecord}s. - * - *

    There are 2 different packet formats that are supported, which is used can be determined by - * packet length: - * - *

    For 3-byte packets, the full packet is the model ID. - * - *

    For all other packets, the first byte is the header, followed by the model ID, followed by - * zero or more extra fields. Each field has its own header byte followed by the field value. The - * packet header is formatted as 0bVVVLLLLR (V = version, L = model ID length, R = reserved) and - * each extra field header is 0bLLLLTTTT (L = field length, T = field type). - * - * @see go/fast-pair-2-service-data - */ -public class FastPairDecoder extends BeaconDecoder { - - private static final int FIELD_TYPE_BLOOM_FILTER = 0; - private static final int FIELD_TYPE_BLOOM_FILTER_SALT = 1; - private static final int FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION = 2; - private static final int FIELD_TYPE_BATTERY = 3; - private static final int FIELD_TYPE_BATTERY_NO_NOTIFICATION = 4; - public static final int FIELD_TYPE_CONNECTION_STATE = 5; - private static final int FIELD_TYPE_RANDOM_RESOLVABLE_DATA = 6; - - /** FE2C is the 16-bit Service UUID. The rest is the base UUID. See BluetoothUuid (hidden). */ - private static final ParcelUuid FAST_PAIR_SERVICE_PARCEL_UUID = - ParcelUuid.fromString("0000FE2C-0000-1000-8000-00805F9B34FB"); - - /** The filter you use to scan for Fast Pair BLE advertisements. */ - public static final BleFilter FILTER = - new BleFilter.Builder().setServiceData(FAST_PAIR_SERVICE_PARCEL_UUID, - new byte[0]).build(); - - // NOTE: Ensure that all bitmasks are always ints, not bytes so that bitshifting works correctly - // without needing worry about signing errors. - private static final int HEADER_VERSION_BITMASK = 0b11100000; - private static final int HEADER_LENGTH_BITMASK = 0b00011110; - private static final int HEADER_VERSION_OFFSET = 5; - private static final int HEADER_LENGTH_OFFSET = 1; - - private static final int EXTRA_FIELD_LENGTH_BITMASK = 0b11110000; - private static final int EXTRA_FIELD_TYPE_BITMASK = 0b00001111; - private static final int EXTRA_FIELD_LENGTH_OFFSET = 4; - private static final int EXTRA_FIELD_TYPE_OFFSET = 0; - - private static final int MIN_ID_LENGTH = 3; - private static final int MAX_ID_LENGTH = 14; - private static final int HEADER_INDEX = 0; - private static final int HEADER_LENGTH = 1; - private static final int FIELD_HEADER_LENGTH = 1; - - // Not using java.util.IllegalFormatException because it is unchecked. - private static class IllegalFormatException extends Exception { - private IllegalFormatException(String message) { - super(message); - } - } - - @Nullable - @Override - public Integer getCalibratedBeaconTxPower(BleRecord bleRecord) { - return null; - } - - // TODO(b/205320613) create beacon type - @Override - public int getBeaconIdType() { - return 1; - } - - /** Returns the Model ID from our service data, if present. */ - @Nullable - @Override - public byte[] getBeaconIdBytes(BleRecord bleRecord) { - return getModelId(bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID)); - } - - /** Returns the Model ID from our service data, if present. */ - @Nullable - public static byte[] getModelId(@Nullable byte[] serviceData) { - if (serviceData == null) { - return null; - } - - if (serviceData.length >= MIN_ID_LENGTH) { - if (serviceData.length == MIN_ID_LENGTH) { - // If the length == 3, all bytes are the ID. See flag docs for more about - // endianness. - return serviceData; - } else { - // Otherwise, the first byte is a header which contains the length of the - // big-endian model - // ID that follows. The model ID will be trimmed if it contains leading zeros. - int idIndex = 1; - int end = idIndex + getIdLength(serviceData); - while (serviceData[idIndex] == 0 && end - idIndex > MIN_ID_LENGTH) { - idIndex++; - } - return Arrays.copyOfRange(serviceData, idIndex, end); - } - } - return null; - } - - /** Gets the FastPair service data array if available, otherwise returns null. */ - @Nullable - public static byte[] getServiceDataArray(BleRecord bleRecord) { - return bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID); - } - - /** Gets the FastPair service data array if available, otherwise returns null. */ - @Nullable - public static byte[] getServiceDataArray(ScanRecord scanRecord) { - return scanRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID); - } - - /** Gets the bloom filter from the extra fields if available, otherwise returns null. */ - @Nullable - public static byte[] getBloomFilter(@Nullable byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER); - } - - /** Gets the bloom filter salt from the extra fields if available, otherwise returns null. */ - @Nullable - public static byte[] getBloomFilterSalt(byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_SALT); - } - - /** - * Gets the suppress notification with bloom filter from the extra fields if available, - * otherwise - * returns null. - */ - @Nullable - public static byte[] getBloomFilterNoNotification(@Nullable byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION); - } - - /** Gets the battery level from extra fields if available, otherwise return null. */ - @Nullable - public static byte[] getBatteryLevel(byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BATTERY); - } - - /** - * Gets the suppress notification with battery level from extra fields if available, otherwise - * return null. - */ - @Nullable - public static byte[] getBatteryLevelNoNotification(byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BATTERY_NO_NOTIFICATION); - } - - /** - * Gets the random resolvable data from extra fields if available, otherwise - * return null. - */ - @Nullable - public static byte[] getRandomResolvableData(byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_RANDOM_RESOLVABLE_DATA); - } - - @Nullable - private static byte[] getExtraField(@Nullable byte[] serviceData, int fieldId) { - if (serviceData == null || serviceData.length < HEADER_INDEX + HEADER_LENGTH) { - return null; - } - try { - return getExtraFields(serviceData).get(fieldId); - } catch (IllegalFormatException e) { - Log.v("FastPairDecode", "Extra fields incorrectly formatted."); - return null; - } - } - - /** Gets extra field data at the end of the packet, defined by the extra field header. */ - private static SparseArray getExtraFields(byte[] serviceData) - throws IllegalFormatException { - SparseArray extraFields = new SparseArray<>(); - if (getVersion(serviceData) != 0) { - return extraFields; - } - int headerIndex = getFirstExtraFieldHeaderIndex(serviceData); - while (headerIndex < serviceData.length) { - int length = getExtraFieldLength(serviceData, headerIndex); - int index = headerIndex + FIELD_HEADER_LENGTH; - int type = getExtraFieldType(serviceData, headerIndex); - int end = index + length; - if (extraFields.get(type) == null) { - if (end <= serviceData.length) { - extraFields.put(type, Arrays.copyOfRange(serviceData, index, end)); - } else { - throw new IllegalFormatException( - "Invalid length, " + end + " is longer than service data size " - + serviceData.length); - } - } - headerIndex = end; - } - return extraFields; - } - - /** Checks whether or not a valid ID is included in the service data packet. */ - public static boolean hasBeaconIdBytes(BleRecord bleRecord) { - byte[] serviceData = bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID); - return checkModelId(serviceData); - } - - /** Check whether byte array is FastPair model id or not. */ - public static boolean checkModelId(@Nullable byte[] scanResult) { - return scanResult != null - // The 3-byte format has no header byte (all bytes are the ID). - && (scanResult.length == MIN_ID_LENGTH - // Header byte exists. We support only format version 0. (A different version - // indicates - // a breaking change in the format.) - || (scanResult.length > MIN_ID_LENGTH - && getVersion(scanResult) == 0 - && isIdLengthValid(scanResult))); - } - - /** Checks whether or not bloom filter is included in the service data packet. */ - public static boolean hasBloomFilter(BleRecord bleRecord) { - return (getBloomFilter(getServiceDataArray(bleRecord)) != null - || getBloomFilterNoNotification(getServiceDataArray(bleRecord)) != null); - } - - /** Checks whether or not bloom filter is included in the service data packet. */ - public static boolean hasBloomFilter(ScanRecord scanRecord) { - return (getBloomFilter(getServiceDataArray(scanRecord)) != null - || getBloomFilterNoNotification(getServiceDataArray(scanRecord)) != null); - } - - private static int getVersion(byte[] serviceData) { - return serviceData.length == MIN_ID_LENGTH - ? 0 - : (serviceData[HEADER_INDEX] & HEADER_VERSION_BITMASK) >> HEADER_VERSION_OFFSET; - } - - private static int getIdLength(byte[] serviceData) { - return serviceData.length == MIN_ID_LENGTH - ? MIN_ID_LENGTH - : (serviceData[HEADER_INDEX] & HEADER_LENGTH_BITMASK) >> HEADER_LENGTH_OFFSET; - } - - private static int getFirstExtraFieldHeaderIndex(byte[] serviceData) { - return HEADER_INDEX + HEADER_LENGTH + getIdLength(serviceData); - } - - private static int getExtraFieldLength(byte[] serviceData, int extraFieldIndex) { - return (serviceData[extraFieldIndex] & EXTRA_FIELD_LENGTH_BITMASK) - >> EXTRA_FIELD_LENGTH_OFFSET; - } - - private static int getExtraFieldType(byte[] serviceData, int extraFieldIndex) { - return (serviceData[extraFieldIndex] & EXTRA_FIELD_TYPE_BITMASK) >> EXTRA_FIELD_TYPE_OFFSET; - } - - private static boolean isIdLengthValid(byte[] serviceData) { - int idLength = getIdLength(serviceData); - return MIN_ID_LENGTH <= idLength - && idLength <= MAX_ID_LENGTH - && idLength + HEADER_LENGTH <= serviceData.length; - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java b/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java deleted file mode 100644 index f27899f3e91608122d16d90d9c997b24a318aa75..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.common.ble.testing; - -import com.android.server.nearby.util.ArrayUtils; -import com.android.server.nearby.util.Hex; - -/** - * Test class to provide example to unit test. - */ -public class FastPairTestData { - private static final byte[] FAST_PAIR_RECORD_BIG_ENDIAN = - Hex.stringToBytes("02011E020AF006162CFEAABBCC"); - - /** - * A Fast Pair frame, Note: The service UUID is FE2C, but in the - * packet it's 2CFE, since the core Bluetooth data types are little-endian. - * - *

    However, the model ID is big-endian (multi-byte values in our spec are now big-endian, aka - * network byte order). - * - * @see {http://go/fast-pair-service-data} - */ - public static byte[] getFastPairRecord() { - return FAST_PAIR_RECORD_BIG_ENDIAN; - } - - /** A Fast Pair frame, with a shared account key. */ - public static final byte[] FAST_PAIR_SHARED_ACCOUNT_KEY_RECORD = - Hex.stringToBytes("02011E020AF00C162CFE007011223344556677"); - - /** Model ID in {@link #getFastPairRecord()}. */ - public static final byte[] FAST_PAIR_MODEL_ID = Hex.stringToBytes("AABBCC"); - - /** @see #getFastPairRecord() */ - public static byte[] newFastPairRecord(byte header, byte[] modelId) { - return newFastPairRecord( - modelId.length == 3 ? modelId : ArrayUtils.concatByteArrays(new byte[] {header}, - modelId)); - } - - /** @see #getFastPairRecord() */ - public static byte[] newFastPairRecord(byte[] serviceData) { - int length = /* length of type and service UUID = */ 3 + serviceData.length; - return Hex.stringToBytes( - String.format("02011E020AF0%02X162CFE%s", length, - Hex.bytesToStringUppercase(serviceData))); - } - - // This is an example of advertising data with AD types - public static byte[] adv_1 = { - 0x02, // Length of this Data - 0x01, // <> - 0x01, // LE Limited Discoverable Mode - 0x0A, // Length of this Data - 0x09, // <> - 'P', 'e', 'd', 'o', 'm', 'e', 't', 'e', 'r' - }; - - // This is an example of advertising data with positive TX Power - // Level. - public static byte[] adv_2 = { - 0x02, // Length of this Data - 0x0a, // <> - 127 // Level = 127 - }; - - // Example data including a service data block - public static byte[] sd1 = { - 0x02, // Length of this Data - 0x01, // <> - 0x04, // BR/EDR Not Supported. - 0x03, // Length of this Data - 0x02, // <> - 0x04, - 0x18, // TX Power Service UUID - 0x1e, // Length of this Data - (byte) 0x16, // <> - // Service UUID - (byte) 0xe0, - 0x00, - // gBeacon Header - 0x15, - // Running time ENCRYPT - (byte) 0xd2, - 0x77, - 0x01, - 0x00, - // Scan Freq ENCRYPT - 0x32, - 0x05, - // Time in slow mode - 0x00, - 0x00, - // Time in fast mode - 0x7f, - 0x17, - // Subset of UID - 0x56, - 0x00, - // ID Mask - (byte) 0xd4, - 0x7c, - 0x18, - // RFU (reserved) - 0x00, - // GUID = decimal 1297482358 - 0x76, - 0x02, - 0x56, - 0x4d, - 0x00, - // Ranging Payload Header - 0x24, - // MAC of scanning address - (byte) 0xa4, - (byte) 0xbb, - // NORM RX RSSI -67dBm - (byte) 0xb0, - // NORM TX POWER -77dBm, so actual TX POWER = -36dBm - (byte) 0xb3, - // Note based on the values aboves PATH LOSS = (-36) - (-67) = 31dBm - // Below zero padding added to test it is handled correctly - 0x00 - }; - -} diff --git a/nearby/service/java/com/android/server/nearby/common/ble/util/RangingUtils.java b/nearby/service/java/com/android/server/nearby/common/ble/util/RangingUtils.java deleted file mode 100644 index eec52ad14d50e81335142a840c24e248454e6354..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/util/RangingUtils.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble.util; - - -/** - * Ranging utilities embody the physics of converting RF path loss to distance. The free space path - * loss is proportional to the square of the distance from transmitter to receiver, and to the - * square of the frequency of the propagation signal. - */ -public final class RangingUtils { - private static final int MAX_RSSI_VALUE = 126; - private static final int MIN_RSSI_VALUE = -127; - - private RangingUtils() { - } - - /* This was original derived in {@link com.google.android.gms.beacon.util.RangingUtils} from - * Free-space_path_loss. - * Duplicated here for easy reference. - * - * c = speed of light (2.9979 x 10^8 m/s); - * f = frequency (Bluetooth center frequency is 2.44175GHz = 2.44175x10^9 Hz); - * l = wavelength (in meters); - * d = distance (from transmitter to receiver in meters); - * dB = decibels - * dBm = decibel milliwatts - * - * - * Free-space path loss (FSPL) is proportional to the square of the distance between the - * transmitter and the receiver, and also proportional to the square of the frequency of the - * radio signal. - * - * FSPL = (4 * pi * d / l)^2 = (4 * pi * d * f / c)^2 - * - * FSPL (dB) = 10 * log10((4 * pi * d * f / c)^2) - * = 20 * log10(4 * pi * d * f / c) - * = (20 * log10(d)) + (20 * log10(f)) + (20 * log10(4 * pi/c)) - * - * Calculating constants: - * - * FSPL_FREQ = 20 * log10(f) - * = 20 * log10(2.44175 * 10^9) - * = 187.75 - * - * FSPL_LIGHT = 20 * log10(4 * pi/c) - * = 20 * log10(4 * pi/(2.9979 * 10^8)) - * = 20 * log10(4 * pi/(2.9979 * 10^8)) - * = 20 * log10(41.9172441s * 10^-9) - * = -147.55 - * - * FSPL_DISTANCE_1M = 20 * log10(1) - * = 0 - * - * PATH_LOSS_AT_1M = FSPL_DISTANCE_1M + FSPL_FREQ + FSPL_LIGHT - * = 0 + 187.75 + (-147.55) - * = 40.20db [round to 41db] - * - * Note: Rounding up makes us "closer" and makes us more aggressive at showing notifications. - */ - private static final int RSSI_DROP_OFF_AT_1_M = 41; - - /** - * Convert target distance and txPower to a RSSI value using the Log-distance path loss model - * with Path Loss at 1m of 41db. - * - * @return RSSI expected at distanceInMeters with device broadcasting at txPower. - */ - public static int rssiFromTargetDistance(double distanceInMeters, int txPower) { - /* - * See - * Log-distance path loss model. - * - * PL = total path loss in db - * txPower = TxPower in dbm - * rssi = Received signal strength in dbm - * PL_0 = Path loss at reference distance d_0 {@link RSSI_DROP_OFF_AT_1_M} dbm - * d = length of path - * d_0 = reference distance (1 m) - * gamma = path loss exponent (2 in free space) - * - * Log-distance path loss (LDPL) formula: - * - * PL = txPower - rssi = PL_0 + 10 * gamma * log_10(d / d_0) - * txPower - rssi = RSSI_DROP_OFF_AT_1_M + 10 * 2 * log_10 - * (distanceInMeters / 1) - * - rssi = -txPower + RSSI_DROP_OFF_AT_1_M + 20 * log_10(distanceInMeters) - * rssi = txPower - RSSI_DROP_OFF_AT_1_M - 20 * log_10(distanceInMeters) - */ - txPower = adjustPower(txPower); - return distanceInMeters == 0 - ? txPower - : (int) Math.floor((txPower - RSSI_DROP_OFF_AT_1_M) - - 20 * Math.log10(distanceInMeters)); - } - - /** - * Convert RSSI and txPower to a distance value using the Log-distance path loss model with Path - * Loss at 1m of 41db. - * - * @return distance in meters with device broadcasting at txPower and given RSSI. - */ - public static double distanceFromRssiAndTxPower(int rssi, int txPower) { - /* - * See Log-distance - * path - * loss model. - * - * PL = total path loss in db - * txPower = TxPower in dbm - * rssi = Received signal strength in dbm - * PL_0 = Path loss at reference distance d_0 {@link RSSI_DROP_OFF_AT_1_M} dbm - * d = length of path - * d_0 = reference distance (1 m) - * gamma = path loss exponent (2 in free space) - * - * Log-distance path loss (LDPL) formula: - * - * PL = txPower - rssi = PL_0 + 10 * gamma * log_10(d / - * d_0) - * txPower - rssi = RSSI_DROP_OFF_AT_1_M + 10 * gamma * log_10(d / - * d_0) - * txPower - rssi - RSSI_DROP_OFF_AT_1_M = 10 * 2 * log_10 - * (distanceInMeters / 1) - * txPower - rssi - RSSI_DROP_OFF_AT_1_M = 20 * log_10(distanceInMeters / 1) - * (txPower - rssi - RSSI_DROP_OFF_AT_1_M) / 20 = log_10(distanceInMeters) - * 10 ^ ((txPower - rssi - RSSI_DROP_OFF_AT_1_M) / 20) = distanceInMeters - */ - txPower = adjustPower(txPower); - rssi = adjustPower(rssi); - return Math.pow(10, (txPower - rssi - RSSI_DROP_OFF_AT_1_M) / 20.0); - } - - /** - * Prevents the power from becoming too large or too small. - */ - private static int adjustPower(int power) { - if (power > MAX_RSSI_VALUE) { - return MAX_RSSI_VALUE; - } - if (power < MIN_RSSI_VALUE) { - return MIN_RSSI_VALUE; - } - return power; - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/ble/util/StringUtils.java b/nearby/service/java/com/android/server/nearby/common/ble/util/StringUtils.java deleted file mode 100644 index 4d90b6d554388456144fac2f67d55903fe7e5f20..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/ble/util/StringUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.ble.util; - -import android.annotation.Nullable; -import android.util.SparseArray; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; - -/** Helper class for Bluetooth LE utils. */ -public final class StringUtils { - private StringUtils() { - } - - /** Returns a string composed from a {@link SparseArray}. */ - public static String toString(@Nullable SparseArray array) { - if (array == null) { - return "null"; - } - if (array.size() == 0) { - return "{}"; - } - StringBuilder buffer = new StringBuilder(); - buffer.append('{'); - for (int i = 0; i < array.size(); ++i) { - buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i))); - } - buffer.append('}'); - return buffer.toString(); - } - - /** Returns a string composed from a {@link Map}. */ - public static String toString(@Nullable Map map) { - if (map == null) { - return "null"; - } - if (map.isEmpty()) { - return "{}"; - } - StringBuilder buffer = new StringBuilder(); - buffer.append('{'); - Iterator> it = map.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - Object key = entry.getKey(); - buffer.append(key).append("=").append(Arrays.toString(map.get(key))); - if (it.hasNext()) { - buffer.append(", "); - } - } - buffer.append('}'); - return buffer.toString(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bloomfilter/BloomFilter.java b/nearby/service/java/com/android/server/nearby/common/bloomfilter/BloomFilter.java deleted file mode 100644 index 6d4275f94350441fef51fc3fc27b38dcbbf5aa39..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bloomfilter/BloomFilter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.common.bloomfilter; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.common.primitives.UnsignedInts; - -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.BitSet; - -/** - * A bloom filter that gives access to the underlying BitSet. - */ -public class BloomFilter { - private static final Charset CHARSET = UTF_8; - - /** - * Receives a value and converts it into an array of ints that will be converted to indexes for - * the filter. - */ - public interface Hasher { - /** - * Generate hash value. - */ - int[] getHashes(byte[] value); - } - - // The backing data for this bloom filter. As additions are made, they're OR'd until it - // eventually reaches 0xFF. - private final BitSet mBits; - // The max length of bits. - private final int mBitLength; - // The hasher to use for converting a value into an array of hashes. - private final Hasher mHasher; - - public BloomFilter(byte[] bytes, Hasher hasher) { - this.mBits = BitSet.valueOf(bytes); - this.mBitLength = bytes.length * 8; - this.mHasher = hasher; - } - - /** - * Return the bloom filter check bit set as byte array. - */ - public byte[] asBytes() { - // BitSet.toByteArray() truncates all the unset bits after the last set bit (eg. [0,0,1,0] - // becomes [0,0,1]) so we re-add those bytes if needed with Arrays.copy(). - byte[] b = mBits.toByteArray(); - if (b.length == mBitLength / 8) { - return b; - } - return Arrays.copyOf(b, mBitLength / 8); - } - - /** - * Add string value to bloom filter hash. - */ - public void add(String s) { - add(s.getBytes(CHARSET)); - } - - /** - * Adds value to bloom filter hash. - */ - public void add(byte[] value) { - int[] hashes = mHasher.getHashes(value); - for (int hash : hashes) { - mBits.set(UnsignedInts.remainder(hash, mBitLength)); - } - } - - /** - * Check if the string format has collision. - */ - public boolean possiblyContains(String s) { - return possiblyContains(s.getBytes(CHARSET)); - } - - /** - * Checks if value after hash will have collision. - */ - public boolean possiblyContains(byte[] value) { - int[] hashes = mHasher.getHashes(value); - for (int hash : hashes) { - if (!mBits.get(UnsignedInts.remainder(hash, mBitLength))) { - return false; - } - } - return true; - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/bloomfilter/FastPairBloomFilterHasher.java b/nearby/service/java/com/android/server/nearby/common/bloomfilter/FastPairBloomFilterHasher.java deleted file mode 100644 index 0ccee97255b3aef61097e85f41a7344aa88bea7c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bloomfilter/FastPairBloomFilterHasher.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.common.bloomfilter; - -import com.google.common.hash.Hashing; - -import java.nio.ByteBuffer; - -/** - * Hasher which hashes a value using SHA-256 and splits it into parts, each of which can be - * converted to an index. - */ -public class FastPairBloomFilterHasher implements BloomFilter.Hasher { - - private static final int NUM_INDEXES = 8; - - @Override - public int[] getHashes(byte[] value) { - byte[] hash = Hashing.sha256().hashBytes(value).asBytes(); - ByteBuffer buffer = ByteBuffer.wrap(hash); - int[] hashes = new int[NUM_INDEXES]; - for (int i = 0; i < NUM_INDEXES; i++) { - hashes[i] = buffer.getInt(); - } - return hashes; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothConsts.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothConsts.java deleted file mode 100644 index 3a02b18fb8f800a056dd9ddbb748b4bd8fa50f1f..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothConsts.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth; - -import java.util.UUID; - -/** - * Bluetooth constants. - */ -public class BluetoothConsts { - - /** - * Default MTU when value is unknown. - */ - public static final int DEFAULT_MTU = 23; - - // The following random uuids are used to indicate that the device has dynamic services. - /** - * UUID of dynamic service. - */ - public static final UUID SERVICE_DYNAMIC_SERVICE = - UUID.fromString("00000100-0af3-11e5-a6c0-1697f925ec7b"); - - /** - * UUID of dynamic characteristic. - */ - public static final UUID SERVICE_DYNAMIC_CHARACTERISTIC = - UUID.fromString("00002A05-0af3-11e5-a6c0-1697f925ec7b"); -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothException.java deleted file mode 100644 index db2e1ccfe385ac725964d73da24636ebdcf77142..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth; - -/** - * {@link Exception} thrown during a Bluetooth operation. - */ -public class BluetoothException extends Exception { - /** Constructor. */ - public BluetoothException(String message) { - super(message); - } - - /** Constructor. */ - public BluetoothException(String message, Throwable throwable) { - super(message, throwable); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothGattException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothGattException.java deleted file mode 100644 index 5ac4882fc93747cf64c5f7924a2b7b691b961431..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothGattException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth; - -/** - * Exception for Bluetooth GATT operations. - */ -public class BluetoothGattException extends BluetoothException { - private final int mErrorCode; - - /** Constructor. */ - public BluetoothGattException(String message, int errorCode) { - super(message); - mErrorCode = errorCode; - } - - /** Constructor. */ - public BluetoothGattException(String message, int errorCode, Throwable cause) { - super(message, cause); - mErrorCode = errorCode; - } - - /** Returns Gatt error code. */ - public int getGattErrorCode() { - return mErrorCode; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothTimeoutException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothTimeoutException.java deleted file mode 100644 index 30fd1887ec2f4a7c56c0067bf5d7ceeeab4b0c0a..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/BluetoothTimeoutException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth; - -/** - * {@link Exception} thrown during a Bluetooth operation when a timeout occurs. - */ -public class BluetoothTimeoutException extends BluetoothException { - - /** Constructor. */ - public BluetoothTimeoutException(String message) { - super(message); - } - - /** Constructor. */ - public BluetoothTimeoutException(String message, Throwable throwable) { - super(message, throwable); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/ReservedUuids.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/ReservedUuids.java deleted file mode 100644 index 249011a827e6f9dfa5ea281e692aed519bb952a0..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/ReservedUuids.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth; - -import java.util.UUID; - -/** - * Reserved UUIDS by BT SIG. - *

    - * See https://developer.bluetooth.org for more details. - */ -public class ReservedUuids { - /** UUIDs reserved for services. */ - public static class Services { - /** - * The Device Information Service exposes manufacturer and/or vendor info about a device. - *

    - * See reserved UUID org.bluetooth.service.device_information. - */ - public static final UUID DEVICE_INFORMATION = fromShortUuid((short) 0x180A); - - /** - * Generic attribute service. - *

    - * See reserved UUID org.bluetooth.service.generic_attribute. - */ - public static final UUID GENERIC_ATTRIBUTE = fromShortUuid((short) 0x1801); - } - - /** UUIDs reserved for characteristics. */ - public static class Characteristics { - /** - * The value of this characteristic is a UTF-8 string representing the firmware revision for - * the firmware within the device. - *

    - * See reserved UUID org.bluetooth.characteristic.firmware_revision_string. - */ - public static final UUID FIRMWARE_REVISION_STRING = fromShortUuid((short) 0x2A26); - - /** - * Service change characteristic. - *

    - * See reserved UUID org.bluetooth.characteristic.gatt.service_changed. - */ - public static final UUID SERVICE_CHANGE = fromShortUuid((short) 0x2A05); - } - - /** UUIDs reserved for descriptors. */ - public static class Descriptors { - /** - * This descriptor shall be persistent across connections for bonded devices. The Client - * Characteristic Configuration descriptor is unique for each client. A client may read and - * write this descriptor to determine and set the configuration for that client. - * Authentication and authorization may be required by the server to write this descriptor. - * The default value for the Client Characteristic Configuration descriptor is 0x00. Upon - * connection of non-binded clients, this descriptor is set to the default value. - *

    - * See reserved UUID org.bluetooth.descriptor.gatt.client_characteristic_configuration. - */ - public static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION = - fromShortUuid((short) 0x2902); - } - - /** The base 128-bit UUID representation of a 16-bit UUID */ - public static final UUID BASE_16_BIT_UUID = - UUID.fromString("00000000-0000-1000-8000-00805F9B34FB"); - - /** Converts from short UUId to UUID. */ - public static UUID fromShortUuid(short shortUuid) { - return new UUID(((((long) shortUuid) << 32) & 0x0000FFFF00000000L) - | ReservedUuids.BASE_16_BIT_UUID.getMostSignificantBits(), - ReservedUuids.BASE_16_BIT_UUID.getLeastSignificantBits()); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGenerator.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGenerator.java deleted file mode 100644 index 28a9c33a4f822e0ddbc9515d289e1cfa7d4aecfd..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AccountKeyGenerator.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.generateKey; - -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AccountKeyCharacteristic; - -import java.security.NoSuchAlgorithmException; - -/** - * This is to generate account key with fast-pair style. - */ -public final class AccountKeyGenerator { - - // Generate a key where the first byte is always defined as the type, 0x04. This maintains 15 - // bytes of entropy in the key while also allowing providers to verify that they have received - // a properly formatted key and decrypted it correctly, minimizing the risk of replay attacks. - - /** - * Creates account key. - */ - public static byte[] createAccountKey() throws NoSuchAlgorithmException { - byte[] accountKey = generateKey(); - accountKey[0] = AccountKeyCharacteristic.TYPE; - return accountKey; - } - - private AccountKeyGenerator() { - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoder.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoder.java deleted file mode 100644 index c9ccfd503501b36e0787b66f47a3d6779ad9624d..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoder.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesCtrMultipleBlockEncryption.NONCE_SIZE; - -import static com.google.common.primitives.Bytes.concat; - -import java.security.GeneralSecurityException; -import java.util.Arrays; - -/** - * Utilities for encoding/decoding the additional data packet and verifying both the data integrity - * and the authentication. - * - *

    Additional Data packet is: - * - *

      - *
    1. AdditionalData_Packet[0 - 7]: the first 8-byte of HMAC. - *
    2. AdditionalData_Packet[8 - var]: the encrypted message by AES-CTR, with 8-byte nonce - * appended to the front. - *
    - * - * See https://developers.google.com/nearby/fast-pair/spec#AdditionalData. - */ -public final class AdditionalDataEncoder { - - static final int EXTRACT_HMAC_SIZE = 8; - static final int MAX_LENGTH_OF_DATA = 64; - - /** - * Encodes the given data to additional data packet by the given secret. - */ - static byte[] encodeAdditionalDataPacket(byte[] secret, byte[] additionalData) - throws GeneralSecurityException { - if (secret == null || secret.length != AesCtrMultipleBlockEncryption.KEY_LENGTH) { - throw new GeneralSecurityException( - "Incorrect secret for encoding additional data packet, secret.length = " - + (secret == null ? "NULL" : secret.length)); - } - - if ((additionalData == null) - || (additionalData.length == 0) - || (additionalData.length > MAX_LENGTH_OF_DATA)) { - throw new GeneralSecurityException( - "Invalid data for encoding additional data packet, data = " - + (additionalData == null ? "NULL" : additionalData.length)); - } - - byte[] encryptedData = AesCtrMultipleBlockEncryption.encrypt(secret, additionalData); - byte[] extractedHmac = - Arrays.copyOf(HmacSha256.build(secret, encryptedData), EXTRACT_HMAC_SIZE); - - return concat(extractedHmac, encryptedData); - } - - /** - * Decodes additional data packet by the given secret. - * - * @param secret AES-128 key used in the encryption to decrypt data - * @param additionalDataPacket additional data packet which is encoded by the given secret - * @return the data byte array decoded from the given packet - * @throws GeneralSecurityException if the given key or additional data packet is invalid for - * decoding - */ - static byte[] decodeAdditionalDataPacket(byte[] secret, byte[] additionalDataPacket) - throws GeneralSecurityException { - if (secret == null || secret.length != AesCtrMultipleBlockEncryption.KEY_LENGTH) { - throw new GeneralSecurityException( - "Incorrect secret for decoding additional data packet, secret.length = " - + (secret == null ? "NULL" : secret.length)); - } - if (additionalDataPacket == null - || additionalDataPacket.length <= EXTRACT_HMAC_SIZE - || additionalDataPacket.length - > (MAX_LENGTH_OF_DATA + EXTRACT_HMAC_SIZE + NONCE_SIZE)) { - throw new GeneralSecurityException( - "Additional data packet size is incorrect, additionalDataPacket.length is " - + (additionalDataPacket == null ? "NULL" - : additionalDataPacket.length)); - } - - if (!verifyHmac(secret, additionalDataPacket)) { - throw new GeneralSecurityException( - "Verify HMAC failed, could be incorrect key or packet."); - } - byte[] encryptedData = - Arrays.copyOfRange( - additionalDataPacket, EXTRACT_HMAC_SIZE, additionalDataPacket.length); - return AesCtrMultipleBlockEncryption.decrypt(secret, encryptedData); - } - - // Computes the HMAC of the given key and additional data, and compares the first 8-byte of the - // HMAC result with the one from additional data packet. - // Must call constant-time comparison to prevent a possible timing attack, e.g. time the same - // MAC with all different first byte for a given ciphertext, the right one will take longer as - // it will fail on the second byte's verification. - private static boolean verifyHmac(byte[] key, byte[] additionalDataPacket) - throws GeneralSecurityException { - byte[] packetHmac = - Arrays.copyOfRange(additionalDataPacket, /* from= */ 0, EXTRACT_HMAC_SIZE); - byte[] encryptedData = - Arrays.copyOfRange( - additionalDataPacket, EXTRACT_HMAC_SIZE, additionalDataPacket.length); - byte[] computedHmac = Arrays.copyOf( - HmacSha256.build(key, encryptedData), EXTRACT_HMAC_SIZE); - - return HmacSha256.compareTwoHMACs(packetHmac, computedHmac); - } - - private AdditionalDataEncoder() { - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AesCtrMultipleBlockEncryption.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AesCtrMultipleBlockEncryption.java deleted file mode 100644 index 50a818b722f07e08634f98db301d2ad099827846..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AesCtrMultipleBlockEncryption.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.google.common.primitives.Bytes.concat; - -import androidx.annotation.VisibleForTesting; - -import java.security.GeneralSecurityException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; - -/** - * AES-CTR utilities used for encrypting and decrypting Fast Pair packets that contain multiple - * blocks. Encrypts input data by: - * - *
      - *
    1. encryptedBlock[i] = clearBlock[i] ^ AES(counter), and - *
    2. concat(encryptedBlock[0], encryptedBlock[1],...) to create the encrypted result, where - *
    3. counter: the 16-byte input of AES. counter = iv + block_index. - *
    4. iv: extend 8-byte nonce to 16 bytes with zero padding. i.e. concat(0x0000000000000000, - * nonce). - *
    5. nonce: the cryptographically random 8 bytes, must never be reused with the same key. - *
    - */ -final class AesCtrMultipleBlockEncryption { - - /** Length for AES-128 key. */ - static final int KEY_LENGTH = AesEcbSingleBlockEncryption.KEY_LENGTH; - - @VisibleForTesting - static final int AES_BLOCK_LENGTH = AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH; - - /** Length of the nonce, a byte array of cryptographically random bytes. */ - static final int NONCE_SIZE = 8; - - private static final int IV_SIZE = AES_BLOCK_LENGTH; - private static final int MAX_NUMBER_OF_BLOCKS = 4; - - private AesCtrMultipleBlockEncryption() {} - - /** Generates a 16-byte AES key. */ - static byte[] generateKey() throws NoSuchAlgorithmException { - return AesEcbSingleBlockEncryption.generateKey(); - } - - /** - * Encrypts data using AES-CTR by the given secret. - * - * @param secret AES-128 key. - * @param data the plaintext to be encrypted. - * @return the encrypted data with the 8-byte nonce appended to the front. - */ - static byte[] encrypt(byte[] secret, byte[] data) throws GeneralSecurityException { - byte[] nonce = generateNonce(); - return concat(nonce, doAesCtr(secret, data, nonce)); - } - - /** - * Decrypts data using AES-CTR by the given secret and nonce. - * - * @param secret AES-128 key. - * @param data the first 8 bytes is the nonce, and the remaining is the encrypted data to be - * decrypted. - * @return the decrypted data. - */ - static byte[] decrypt(byte[] secret, byte[] data) throws GeneralSecurityException { - if (data == null || data.length <= NONCE_SIZE) { - throw new GeneralSecurityException( - "Incorrect data length " - + (data == null ? "NULL" : data.length) - + " to decrypt, the data should contain nonce."); - } - byte[] nonce = Arrays.copyOf(data, NONCE_SIZE); - byte[] encryptedData = Arrays.copyOfRange(data, NONCE_SIZE, data.length); - return doAesCtr(secret, encryptedData, nonce); - } - - /** - * Generates cryptographically random NONCE_SIZE bytes nonce. This nonce can be used only once. - * Always call this function to generate a new nonce before a new encryption. - */ - // Suppression for a warning for potentially insecure random numbers on Android 4.3 and older. - // Fast Pair service is only for Android 6.0+ devices. - static byte[] generateNonce() { - SecureRandom random = new SecureRandom(); - byte[] nonce = new byte[NONCE_SIZE]; - random.nextBytes(nonce); - - return nonce; - } - - // AES-CTR implementation. - @VisibleForTesting - static byte[] doAesCtr(byte[] secret, byte[] data, byte[] nonce) - throws GeneralSecurityException { - if (secret.length != KEY_LENGTH) { - throw new IllegalArgumentException( - "Incorrect key length for encryption, only supports 16-byte AES Key."); - } - if (nonce.length != NONCE_SIZE) { - throw new IllegalArgumentException( - "Incorrect nonce length for encryption, " - + "Fast Pair naming scheme only supports 8-byte nonce."); - } - - // Keeps the following operations on this byte[], returns it as the final AES-CTR result. - byte[] aesCtrResult = new byte[data.length]; - System.arraycopy(data, /*srcPos=*/ 0, aesCtrResult, /*destPos=*/ 0, data.length); - - // Initializes counter as IV. - byte[] counter = createIv(nonce); - // The length of the given data is permitted to non-align block size. - int numberOfBlocks = - (data.length / AES_BLOCK_LENGTH) + ((data.length % AES_BLOCK_LENGTH == 0) ? 0 : 1); - - if (numberOfBlocks > MAX_NUMBER_OF_BLOCKS) { - throw new IllegalArgumentException( - "Incorrect data size, Fast Pair naming scheme only supports 4 blocks."); - } - - for (int i = 0; i < numberOfBlocks; i++) { - // Performs the operation: encryptedBlock[i] = clearBlock[i] ^ AES(counter). - counter[0] = (byte) (i & 0xFF); - byte[] aesOfCounter = doAesSingleBlock(secret, counter); - int start = i * AES_BLOCK_LENGTH; - // The size of the last block of data may not be 16 bytes. If not, still do xor to the - // last byte of data. - int end = Math.min(start + AES_BLOCK_LENGTH, data.length); - for (int j = 0; start < end; j++, start++) { - aesCtrResult[start] ^= aesOfCounter[j]; - } - } - return aesCtrResult; - } - - private static byte[] doAesSingleBlock(byte[] secret, byte[] counter) - throws GeneralSecurityException { - return AesEcbSingleBlockEncryption.encrypt(secret, counter); - } - - /** Extends 8-byte nonce to 16 bytes with zero padding to create IV. */ - private static byte[] createIv(byte[] nonce) { - return concat(new byte[IV_SIZE - NONCE_SIZE], nonce); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AesEcbSingleBlockEncryption.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AesEcbSingleBlockEncryption.java deleted file mode 100644 index 547931e651069c83a509b73ec8177e91400973bd..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/AesEcbSingleBlockEncryption.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.annotation.SuppressLint; - -import java.security.GeneralSecurityException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.spec.SecretKeySpec; - -/** - * Utilities used for encrypting and decrypting Fast Pair packets. - */ -// SuppressLint for ""ecb encryption mode should not be used". -// Reasons: -// 1. FastPair data is guaranteed to be only 1 AES block in size, ECB is secure. -// 2. In each case, the encrypted data is less than 16-bytes and is -// padded up to 16-bytes using random data to fill the rest of the byte array, -// so the plaintext will never be the same. -@SuppressLint("GetInstance") -public final class AesEcbSingleBlockEncryption { - - public static final int AES_BLOCK_LENGTH = 16; - public static final int KEY_LENGTH = 16; - - private AesEcbSingleBlockEncryption() { - } - - /** - * Generates a 16-byte AES key. - */ - public static byte[] generateKey() throws NoSuchAlgorithmException { - KeyGenerator generator = KeyGenerator.getInstance("AES"); - generator.init(KEY_LENGTH * 8); // Ensure a 16-byte key is always used. - return generator.generateKey().getEncoded(); - } - - /** - * Encrypts data with the provided secret. - */ - public static byte[] encrypt(byte[] secret, byte[] data) throws GeneralSecurityException { - return doEncryption(Cipher.ENCRYPT_MODE, secret, data); - } - - /** - * Decrypts data with the provided secret. - */ - public static byte[] decrypt(byte[] secret, byte[] data) throws GeneralSecurityException { - return doEncryption(Cipher.DECRYPT_MODE, secret, data); - } - - private static byte[] doEncryption(int mode, byte[] secret, byte[] data) - throws GeneralSecurityException { - if (data.length != AES_BLOCK_LENGTH) { - throw new IllegalArgumentException("This encrypter only supports 16-byte inputs."); - } - Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); - cipher.init(mode, new SecretKeySpec(secret, "AES")); - return cipher.doFinal(data); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAddress.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAddress.java deleted file mode 100644 index 9bb5a8601c3abff84ffb5bada12223a8988ec94b..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAddress.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.google.common.io.BaseEncoding.base16; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.content.Context; -import android.provider.Settings; - -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import com.google.common.base.Ascii; -import com.google.common.io.BaseEncoding; - -import java.util.Locale; - -/** Utils for dealing with Bluetooth addresses. */ -public final class BluetoothAddress { - - private static final BaseEncoding ENCODING = base16().upperCase().withSeparator(":", 2); - - @VisibleForTesting - static final String SECURE_SETTINGS_KEY_BLUETOOTH_ADDRESS = "bluetooth_address"; - - /** - * @return The string format used by e.g. {@link android.bluetooth.BluetoothDevice}. Upper case. - * Example: "AA:BB:CC:11:22:33" - */ - public static String encode(byte[] address) { - return ENCODING.encode(address); - } - - /** - * @param address The string format used by e.g. {@link android.bluetooth.BluetoothDevice}. - * Case-insensitive. Example: "AA:BB:CC:11:22:33" - */ - public static byte[] decode(String address) { - return ENCODING.decode(address.toUpperCase(Locale.US)); - } - - /** - * Get public bluetooth address. - * - * @param context a valid {@link Context} instance. - */ - public static @Nullable byte[] getPublicAddress(Context context) { - String publicAddress = - Settings.Secure.getString( - context.getContentResolver(), SECURE_SETTINGS_KEY_BLUETOOTH_ADDRESS); - return publicAddress != null && BluetoothAdapter.checkBluetoothAddress(publicAddress) - ? decode(publicAddress) - : null; - } - - /** - * Hides partial information of Bluetooth address. - * ex1: input is null, output should be empty string - * ex2: input is String(AA:BB:CC), output should be AA:BB:CC - * ex3: input is String(AA:BB:CC:DD:EE:FF), output should be XX:XX:XX:XX:EE:FF - * ex4: input is String(Aa:Bb:Cc:Dd:Ee:Ff), output should be XX:XX:XX:XX:EE:FF - * ex5: input is BluetoothDevice(AA:BB:CC:DD:EE:FF), output should be XX:XX:XX:XX:EE:FF - */ - public static String maskBluetoothAddress(@Nullable Object address) { - if (address == null) { - return ""; - } - - if (address instanceof String) { - String originalAddress = (String) address; - String upperCasedAddress = Ascii.toUpperCase(originalAddress); - if (!BluetoothAdapter.checkBluetoothAddress(upperCasedAddress)) { - return originalAddress; - } - return convert(upperCasedAddress); - } else if (address instanceof BluetoothDevice) { - return convert(((BluetoothDevice) address).getAddress()); - } - - // For others, returns toString(). - return address.toString(); - } - - private static String convert(String address) { - return "XX:XX:XX:XX:" + address.substring(12); - } - - private BluetoothAddress() {} -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairer.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairer.java deleted file mode 100644 index 07306c199f9bbcbae23c0cb0098bc913ccfeba6e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairer.java +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static android.bluetooth.BluetoothDevice.BOND_BONDED; -import static android.bluetooth.BluetoothDevice.BOND_BONDING; -import static android.bluetooth.BluetoothDevice.BOND_NONE; -import static android.bluetooth.BluetoothDevice.ERROR; -import static android.bluetooth.BluetoothProfile.A2DP; -import static android.bluetooth.BluetoothProfile.HEADSET; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; - -import static java.util.concurrent.Executors.newSingleThreadExecutor; - -import android.Manifest.permission; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Parcelable; -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; -import androidx.core.content.ContextCompat; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.PasskeyCharacteristic; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.Profile; -import com.android.server.nearby.common.bluetooth.fastpair.TimingLogger.ScopedTiming; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection.ChangeObserver; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.ConnectErrorCode; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.CreateBondErrorCode; -import com.android.server.nearby.intdefs.NearbyEventIntDefs.EventCode; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.SettableFuture; - -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Pairs to Bluetooth audio devices. - */ -public class BluetoothAudioPairer { - - private static final String TAG = BluetoothAudioPairer.class.getSimpleName(); - - /** - * Hidden, see {@link BluetoothDevice}. - */ - // TODO(b/202549655): remove Hidden usage. - private static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; - - /** - * Hidden, see {@link BluetoothDevice}. - */ - // TODO(b/202549655): remove Hidden usage. - private static final int PAIRING_VARIANT_CONSENT = 3; - - /** - * Hidden, see {@link BluetoothDevice}. - */ - // TODO(b/202549655): remove Hidden usage. - public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; - - private static final int DISCOVERY_STATE_CHANGE_TIMEOUT_MS = 3000; - - private final Context mContext; - private final Preferences mPreferences; - private final EventLoggerWrapper mEventLogger; - private final BluetoothDevice mDevice; - @Nullable - private final KeyBasedPairingInfo mKeyBasedPairingInfo; - @Nullable - private final PasskeyConfirmationHandler mPasskeyConfirmationHandler; - private final TimingLogger mTimingLogger; - - private static boolean sTestMode = false; - - static void enableTestMode() { - sTestMode = true; - } - - static class KeyBasedPairingInfo { - - private final byte[] mSecret; - private final GattConnectionManager mGattConnectionManager; - private final boolean mProviderInitiatesBonding; - - /** - * @param secret The secret negotiated during the initial BLE handshake for Key-based - * Pairing. See {@link FastPairConnection#handshake}. - * @param gattConnectionManager A manager that knows how to get and create Gatt connections - * to the remote device. - */ - KeyBasedPairingInfo( - byte[] secret, - GattConnectionManager gattConnectionManager, - boolean providerInitiatesBonding) { - this.mSecret = secret; - this.mGattConnectionManager = gattConnectionManager; - this.mProviderInitiatesBonding = providerInitiatesBonding; - } - } - - public BluetoothAudioPairer( - Context context, - BluetoothDevice device, - Preferences preferences, - EventLoggerWrapper eventLogger, - @Nullable KeyBasedPairingInfo keyBasedPairingInfo, - @Nullable PasskeyConfirmationHandler passkeyConfirmationHandler, - TimingLogger timingLogger) - throws PairingException { - this.mContext = context; - this.mDevice = device; - this.mPreferences = preferences; - this.mEventLogger = eventLogger; - this.mKeyBasedPairingInfo = keyBasedPairingInfo; - this.mPasskeyConfirmationHandler = passkeyConfirmationHandler; - this.mTimingLogger = timingLogger; - - // TODO(b/203455314): follow up with the following comments. - // The OS should give the user some UI to choose if they want to allow access, but there - // seems to be a bug where if we don't reject access, it's auto-granted in some cases - // (Plantronics headset gets contacts access when pairing with my Taimen via Bluetooth - // Settings, without me seeing any UI about it). b/64066631 - // - // If that OS bug doesn't get fixed, we can flip these flags to force-reject the - // permissions. - if (preferences.getRejectPhonebookAccess() && (sTestMode ? false : - !device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED))) { - throw new PairingException("Failed to deny contacts (phonebook) access."); - } - if (preferences.getRejectMessageAccess() - && (sTestMode ? false : - !device.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED))) { - throw new PairingException("Failed to deny message access."); - } - if (preferences.getRejectSimAccess() - && (sTestMode ? false : - !device.setSimAccessPermission(BluetoothDevice.ACCESS_REJECTED))) { - throw new PairingException("Failed to deny SIM access."); - } - } - - boolean isPaired() { - return (sTestMode ? false : mDevice.getBondState() == BOND_BONDED); - } - - /** - * Unpairs from the device. Throws an exception if any error occurs. - */ - @WorkerThread - void unpair() - throws InterruptedException, ExecutionException, TimeoutException, PairingException { - int bondState = sTestMode ? BOND_NONE : mDevice.getBondState(); - try (UnbondedReceiver unbondedReceiver = new UnbondedReceiver(); - ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Unpair for state: " + bondState)) { - // We'll only get a state change broadcast if we're actually unbonding (method returns - // true). - if (bondState == BluetoothDevice.BOND_BONDED) { - mEventLogger.setCurrentEvent(EventCode.REMOVE_BOND); - Log.i(TAG, "removeBond with " + maskBluetoothAddress(mDevice)); - mDevice.removeBond(); - unbondedReceiver.await( - mPreferences.getRemoveBondTimeoutSeconds(), TimeUnit.SECONDS); - } else if (bondState == BluetoothDevice.BOND_BONDING) { - mEventLogger.setCurrentEvent(EventCode.CANCEL_BOND); - Log.i(TAG, "cancelBondProcess with " + maskBluetoothAddress(mDevice)); - mDevice.cancelBondProcess(); - unbondedReceiver.await( - mPreferences.getRemoveBondTimeoutSeconds(), TimeUnit.SECONDS); - } else { - // The OS may have beaten us in a race, unbonding before we called the method. So if - // we're (somehow) in the desired state then we're happy, if not then bail. - if (bondState != BluetoothDevice.BOND_NONE) { - throw new PairingException("returned false, state=%s", bondState); - } - } - } - - // This seems to improve the probability that createBond will succeed after removeBond. - SystemClock.sleep(mPreferences.getRemoveBondSleepMillis()); - mEventLogger.logCurrentEventSucceeded(); - } - - /** - * Pairs with the device. Throws an exception if any error occurs. - */ - @WorkerThread - void pair() - throws InterruptedException, ExecutionException, TimeoutException, PairingException { - // Unpair first, because if we have a bond, but the other device has forgotten its bond, - // it can send us a pairing request that we're not ready for (which can pop up a dialog). - // Or, if we're in the middle of a (too-long) bonding attempt, we want to cancel. - unpair(); - - mEventLogger.setCurrentEvent(EventCode.CREATE_BOND); - try (BondedReceiver bondedReceiver = new BondedReceiver(); - ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, "Create bond")) { - // If the provider's initiating the bond, we do nothing but wait for broadcasts. - if (mKeyBasedPairingInfo == null || !mKeyBasedPairingInfo.mProviderInitiatesBonding) { - if (!sTestMode) { - Log.i(TAG, "createBond with " + maskBluetoothAddress(mDevice) + ", type=" - + mDevice.getType()); - if (mPreferences.getSpecifyCreateBondTransportType()) { - mDevice.createBond(mPreferences.getCreateBondTransportType()); - } else { - mDevice.createBond(); - } - } - } - try { - bondedReceiver.await(mPreferences.getCreateBondTimeoutSeconds(), TimeUnit.SECONDS); - } catch (TimeoutException e) { - Log.w(TAG, "bondedReceiver time out after " + mPreferences - .getCreateBondTimeoutSeconds() + " seconds"); - if (mPreferences.getIgnoreUuidTimeoutAfterBonded() && isPaired()) { - Log.w(TAG, "Created bond but never received UUIDs, attempting to continue."); - } else { - // Rethrow e to cause the pairing to fail and be retried if necessary. - throw e; - } - } - } - mEventLogger.logCurrentEventSucceeded(); - } - - /** - * Connects to the given profile. Throws an exception if any error occurs. - * - *

    If remote device clears the link key, the BOND_BONDED state would transit to BOND_BONDING - * (and go through the pairing process again) when directly connecting the profile. By enabling - * enablePairingBehavior, we provide both pairing and connecting behaviors at the same time. See - * b/145699390 for more details. - */ - // Suppression for possible null from ImmutableMap#get. See go/lsc-get-nullable - @SuppressWarnings("nullness:argument") - @WorkerThread - public void connect(short profileUuid, boolean enablePairingBehavior) - throws InterruptedException, ReflectionException, TimeoutException, ExecutionException, - ConnectException { - if (!mPreferences.isSupportedProfile(profileUuid)) { - throw new ConnectException( - ConnectErrorCode.UNSUPPORTED_PROFILE, "Unsupported profile=%s", profileUuid); - } - Profile profile = Constants.PROFILES.get(profileUuid); - Log.i(TAG, - "Connecting to profile=" + profile + " on device=" + maskBluetoothAddress(mDevice)); - try (BondedReceiver bondedReceiver = enablePairingBehavior ? new BondedReceiver() : null; - ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Connect: " + profile)) { - connectByProfileProxy(profile); - } - } - - private void connectByProfileProxy(Profile profile) - throws ReflectionException, InterruptedException, ExecutionException, TimeoutException, - ConnectException { - try (BluetoothProfileWrapper autoClosingProxy = new BluetoothProfileWrapper(profile); - ConnectedReceiver connectedReceiver = new ConnectedReceiver(profile)) { - BluetoothProfile proxy = autoClosingProxy.mProxy; - - // Try to connect via reflection - Log.v(TAG, "Connect to proxy=" + proxy); - - if (!sTestMode) { - if (!(Boolean) Reflect.on(proxy).withMethod("connect", BluetoothDevice.class) - .get(mDevice)) { - // If we're already connecting, connect() may return false. :/ - Log.w(TAG, "connect returned false, expected if connecting, state=" - + proxy.getConnectionState(mDevice)); - } - } - - // If we're already connected, the OS may not send the connection state broadcast, so - // return immediately for that case. - if (!sTestMode) { - if (proxy.getConnectionState(mDevice) == BluetoothProfile.STATE_CONNECTED) { - Log.v(TAG, "connectByProfileProxy: already connected to device=" - + maskBluetoothAddress(mDevice)); - return; - } - } - - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, "Wait connection")) { - // Wait for connecting to succeed or fail (via event or timeout). - connectedReceiver - .await(mPreferences.getCreateBondTimeoutSeconds(), TimeUnit.SECONDS); - } - } - } - - private class BluetoothProfileWrapper implements AutoCloseable { - - // incompatible types in assignment. - @SuppressWarnings("nullness:assignment") - private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - - private final Profile mProfile; - private final BluetoothProfile mProxy; - - /** - * Blocks until we get the proxy. Throws on error. - */ - private BluetoothProfileWrapper(Profile profile) - throws InterruptedException, ExecutionException, TimeoutException, - ConnectException { - this.mProfile = profile; - mProxy = getProfileProxy(profile); - } - - @Override - public void close() { - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Close profile: " + mProfile)) { - if (!sTestMode) { - mBluetoothAdapter.closeProfileProxy(mProfile.type, mProxy); - } - } - } - - private BluetoothProfile getProfileProxy(BluetoothProfileWrapper this, Profile profile) - throws InterruptedException, ExecutionException, TimeoutException, - ConnectException { - if (profile.type != A2DP && profile.type != HEADSET) { - throw new IllegalArgumentException("Unsupported profile type=" + profile.type); - } - - SettableFuture proxyFuture = SettableFuture.create(); - BluetoothProfile.ServiceListener listener = - new BluetoothProfile.ServiceListener() { - @UiThread - @Override - public void onServiceConnected(int profileType, BluetoothProfile proxy) { - proxyFuture.set(proxy); - } - - @Override - public void onServiceDisconnected(int profileType) { - Log.v(TAG, "proxy disconnected for profile=" + profile); - } - }; - - if (!mBluetoothAdapter.getProfileProxy(mContext, listener, profile.type)) { - throw new ConnectException( - ConnectErrorCode.GET_PROFILE_PROXY_FAILED, - "getProfileProxy failed immediately"); - } - - return proxyFuture.get(mPreferences.getProxyTimeoutSeconds(), TimeUnit.SECONDS); - } - } - - private class UnbondedReceiver extends DeviceIntentReceiver { - - private UnbondedReceiver() { - super(mContext, mPreferences, mDevice, BluetoothDevice.ACTION_BOND_STATE_CHANGED); - } - - @Override - protected void onReceiveDeviceIntent(Intent intent) throws Exception { - if (mDevice.getBondState() == BOND_NONE) { - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Close UnbondedReceiver")) { - close(); - } - } - } - } - - /** - * Receiver that closes after bonding has completed. - */ - class BondedReceiver extends DeviceIntentReceiver { - - private boolean mReceivedUuids = false; - private boolean mReceivedPasskey = false; - - private BondedReceiver() { - super( - mContext, - mPreferences, - mDevice, - BluetoothDevice.ACTION_PAIRING_REQUEST, - BluetoothDevice.ACTION_BOND_STATE_CHANGED, - BluetoothDevice.ACTION_UUID); - } - - // switching on a possibly-null value (intent.getAction()) - // incompatible types in argument. - @SuppressWarnings({"nullness:switching.nullable", "nullness:argument"}) - @Override - protected void onReceiveDeviceIntent(Intent intent) - throws PairingException, InterruptedException, ExecutionException, TimeoutException, - BluetoothException, GeneralSecurityException { - switch (intent.getAction()) { - case BluetoothDevice.ACTION_PAIRING_REQUEST: - int variant = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, ERROR); - int passkey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR); - handlePairingRequest(variant, passkey); - break; - case BluetoothDevice.ACTION_BOND_STATE_CHANGED: - // Use the state in the intent, not device.getBondState(), to avoid a race where - // we log the wrong failure reason during a rapid transition. - int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, ERROR); - int reason = intent.getIntExtra(EXTRA_REASON, ERROR); - handleBondStateChanged(bondState, reason); - break; - case BluetoothDevice.ACTION_UUID: - // According to eisenbach@ and pavlin@, there's always a UUID broadcast when - // pairing (it can happen either before or after the transition to BONDED). - if (mPreferences.getWaitForUuidsAfterBonding()) { - Parcelable[] uuids = intent - .getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID); - handleUuids(uuids); - } - break; - default: - break; - } - } - - private void handlePairingRequest(int variant, int passkey) { - Log.i(TAG, "Pairing request, variant=" + variant + ", passkey=" + (passkey == ERROR - ? "(none)" : String.valueOf(passkey))); - if (mPreferences.getMoreEventLogForQuality()) { - mEventLogger.setCurrentEvent(EventCode.HANDLE_PAIRING_REQUEST); - } - - if (mPreferences.getSupportHidDevice() && variant == PAIRING_VARIANT_DISPLAY_PASSKEY) { - mReceivedPasskey = true; - extendAwaitSecond( - mPreferences.getHidCreateBondTimeoutSeconds() - - mPreferences.getCreateBondTimeoutSeconds()); - triggerDiscoverStateChange(); - if (mPreferences.getMoreEventLogForQuality()) { - mEventLogger.logCurrentEventSucceeded(); - } - return; - - } else { - // Prevent Bluetooth Settings from getting the pairing request and showing its own - // UI. - abortBroadcast(); - - if (variant == PAIRING_VARIANT_CONSENT - && mKeyBasedPairingInfo == null // Fast Pair 1.0 device - && mPreferences.getAcceptConsentForFastPairOne()) { - // Previously, if Bluetooth decided to use the Just Works variant (e.g. Fast - // Pair 1.0), we don't get a pairing request broadcast at all. - // However, after CVE-2019-2225, Bluetooth will decide to ask consent from - // users. Details: - // https://source.android.com/security/bulletin/2019-12-01#system - // Since we've certified the Fast Pair 1.0 devices, and user taps to pair it - // (with the device's image), we could help user to accept the consent. - if (!sTestMode) { - mDevice.setPairingConfirmation(true); - } - if (mPreferences.getMoreEventLogForQuality()) { - mEventLogger.logCurrentEventSucceeded(); - } - return; - } else if (variant != BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) { - if (!sTestMode) { - mDevice.setPairingConfirmation(false); - } - if (mPreferences.getMoreEventLogForQuality()) { - mEventLogger.logCurrentEventFailed( - new CreateBondException( - CreateBondErrorCode.INCORRECT_VARIANT, 0, - "Incorrect variant for FastPair")); - } - return; - } - mReceivedPasskey = true; - - if (mKeyBasedPairingInfo == null) { - if (mPreferences.getAcceptPasskey()) { - // Must be the simulator using FP 1.0 (no Key-based Pairing). Real - // headphones using FP 1.0 use Just Works instead (and maybe we should - // disable this flag for them). - if (!sTestMode) { - mDevice.setPairingConfirmation(true); - } - } - if (mPreferences.getMoreEventLogForQuality()) { - if (!sTestMode) { - mEventLogger.logCurrentEventSucceeded(); - } - } - return; - } - } - - if (mPreferences.getMoreEventLogForQuality()) { - mEventLogger.logCurrentEventSucceeded(); - } - - newSingleThreadExecutor() - .execute( - () -> { - try (ScopedTiming scopedTiming1 = - new ScopedTiming(mTimingLogger, "Exchange passkey")) { - mEventLogger.setCurrentEvent(EventCode.PASSKEY_EXCHANGE); - - // We already check above, but the static analyzer's not - // convinced without this. - Preconditions.checkNotNull(mKeyBasedPairingInfo); - BluetoothGattConnection connection = - mKeyBasedPairingInfo.mGattConnectionManager - .getConnection(); - UUID characteristicUuid = - PasskeyCharacteristic.getId(connection); - ChangeObserver remotePasskeyObserver = - connection.enableNotification(FastPairService.ID, - characteristicUuid); - Log.i(TAG, "Sending local passkey."); - byte[] encryptedData; - try (ScopedTiming scopedTiming2 = - new ScopedTiming(mTimingLogger, "Encrypt passkey")) { - encryptedData = - PasskeyCharacteristic.encrypt( - PasskeyCharacteristic.Type.SEEKER, - mKeyBasedPairingInfo.mSecret, passkey); - } - try (ScopedTiming scopedTiming3 = - new ScopedTiming(mTimingLogger, - "Send passkey to remote")) { - connection.writeCharacteristic( - FastPairService.ID, characteristicUuid, - encryptedData); - } - Log.i(TAG, "Waiting for remote passkey."); - byte[] encryptedRemotePasskey; - try (ScopedTiming scopedTiming4 = - new ScopedTiming(mTimingLogger, - "Wait for remote passkey")) { - encryptedRemotePasskey = - remotePasskeyObserver.waitForUpdate( - TimeUnit.SECONDS.toMillis(mPreferences - .getGattOperationTimeoutSeconds())); - } - int remotePasskey; - try (ScopedTiming scopedTiming5 = - new ScopedTiming(mTimingLogger, "Decrypt passkey")) { - remotePasskey = - PasskeyCharacteristic.decrypt( - PasskeyCharacteristic.Type.PROVIDER, - mKeyBasedPairingInfo.mSecret, - encryptedRemotePasskey); - } - - // We log success if we made it through with no exceptions. - // If the passkey was wrong, pairing will fail and we'll log - // BOND_BROKEN with reason = AUTH_FAILED. - mEventLogger.logCurrentEventSucceeded(); - - boolean isPasskeyCorrect = passkey == remotePasskey; - if (isPasskeyCorrect) { - Log.i(TAG, "Passkey correct."); - } else { - Log.e(TAG, "Passkey incorrect, local= " + passkey - + ", remote=" + remotePasskey); - } - - // Don't estimate the {@code ScopedTiming} because the - // passkey confirmation is done by UI. - if (isPasskeyCorrect - && mPreferences.getHandlePasskeyConfirmationByUi() - && mPasskeyConfirmationHandler != null) { - Log.i(TAG, "Callback the passkey to UI for confirmation."); - mPasskeyConfirmationHandler - .onPasskeyConfirmation(mDevice, passkey); - } else { - try (ScopedTiming scopedTiming6 = - new ScopedTiming( - mTimingLogger, "Confirm the pairing: " - + isPasskeyCorrect)) { - mDevice.setPairingConfirmation(isPasskeyCorrect); - } - } - } catch (BluetoothException - | GeneralSecurityException - | InterruptedException - | ExecutionException - | TimeoutException e) { - mEventLogger.logCurrentEventFailed(e); - closeWithError(e); - } - }); - } - - /** - * Workaround to let Settings popup a pairing dialog instead of notification. When pairing - * request intent passed to Settings, it'll check several conditions to decide that it - * should show a dialog or a notification. One of those conditions is to check if the device - * is in discovery mode recently, which can be fulfilled by calling {@link - * BluetoothAdapter#startDiscovery()}. This method aims to fulfill the condition, and block - * the pairing broadcast for at most - * {@link BluetoothAudioPairer#DISCOVERY_STATE_CHANGE_TIMEOUT_MS} - * to make sure that we fulfill the condition first and successful. - */ - // dereference of possibly-null reference bluetoothAdapter - @SuppressWarnings("nullness:dereference.of.nullable") - private void triggerDiscoverStateChange() { - BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - - if (bluetoothAdapter.isDiscovering()) { - return; - } - - HandlerThread backgroundThread = new HandlerThread("TriggerDiscoverStateChangeThread"); - backgroundThread.start(); - - AtomicBoolean result = new AtomicBoolean(false); - SimpleBroadcastReceiver receiver = - new SimpleBroadcastReceiver( - mContext, - mPreferences, - new Handler(backgroundThread.getLooper()), - BluetoothAdapter.ACTION_DISCOVERY_STARTED, - BluetoothAdapter.ACTION_DISCOVERY_FINISHED) { - - @Override - protected void onReceive(Intent intent) throws Exception { - result.set(true); - close(); - } - }; - - Log.i(TAG, "triggerDiscoverStateChange call startDiscovery."); - // Uses startDiscovery to trigger Settings show pairing dialog instead of notification. - if (!sTestMode) { - bluetoothAdapter.startDiscovery(); - bluetoothAdapter.cancelDiscovery(); - } - try { - receiver.await(DISCOVERY_STATE_CHANGE_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Log.w(TAG, "triggerDiscoverStateChange failed!"); - } - - backgroundThread.quitSafely(); - try { - backgroundThread.join(); - } catch (InterruptedException e) { - Log.i(TAG, "triggerDiscoverStateChange backgroundThread.join meet exception!", e); - } - - if (result.get()) { - Log.i(TAG, "triggerDiscoverStateChange successful."); - } - } - - private void handleBondStateChanged(int bondState, int reason) - throws PairingException, InterruptedException, ExecutionException, - TimeoutException { - Log.i(TAG, "Bond state changed to " + bondState + ", reason=" + reason); - switch (bondState) { - case BOND_BONDED: - if (mKeyBasedPairingInfo != null && !mReceivedPasskey) { - // The device bonded with Just Works, although we did the Key-based Pairing - // GATT handshake and agreed on a pairing secret. It might be a Person In - // The Middle Attack! - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, - "Close BondedReceiver: POSSIBLE_MITM")) { - closeWithError( - new CreateBondException( - CreateBondErrorCode.POSSIBLE_MITM, - reason, - "Unexpectedly bonded without a passkey. It might be a " - + "Person In The Middle Attack! Unbonding!")); - } - unpair(); - } else if (!mPreferences.getWaitForUuidsAfterBonding() - || (mPreferences.getReceiveUuidsAndBondedEventBeforeClose() - && mReceivedUuids)) { - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Close BondedReceiver")) { - close(); - } - } - break; - case BOND_NONE: - throw new CreateBondException( - CreateBondErrorCode.BOND_BROKEN, reason, "Bond broken, reason=%d", - reason); - case BOND_BONDING: - default: - break; - } - } - - private void handleUuids(Parcelable[] uuids) { - Log.i(TAG, "Got UUIDs for " + maskBluetoothAddress(mDevice) + ": " - + Arrays.toString(uuids)); - mReceivedUuids = true; - if (!mPreferences.getReceiveUuidsAndBondedEventBeforeClose() || isPaired()) { - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Close BondedReceiver")) { - close(); - } - } - } - } - - private class ConnectedReceiver extends DeviceIntentReceiver { - - private ConnectedReceiver(Profile profile) throws ConnectException { - super(mContext, mPreferences, mDevice, profile.connectionStateAction); - } - - @Override - public void onReceiveDeviceIntent(Intent intent) throws PairingException { - int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, ERROR); - Log.i(TAG, "Connection state changed to " + state); - switch (state) { - case BluetoothAdapter.STATE_CONNECTED: - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Close ConnectedReceiver")) { - close(); - } - break; - case BluetoothAdapter.STATE_DISCONNECTED: - throw new ConnectException(ConnectErrorCode.DISCONNECTED, "Disconnected"); - case BluetoothAdapter.STATE_CONNECTING: - case BluetoothAdapter.STATE_DISCONNECTING: - default: - break; - } - } - } - - private boolean hasPermission(String permission) { - return ContextCompat.checkSelfPermission(mContext, permission) == PERMISSION_GRANTED; - } - - public BluetoothDevice getDevice() { - return mDevice; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairer.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairer.java deleted file mode 100644 index 6c467d3cf04f7ef3b9d5ee3e0b3948d12c5c60e0..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairer.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static android.bluetooth.BluetoothDevice.BOND_BONDED; -import static android.bluetooth.BluetoothDevice.BOND_BONDING; -import static android.bluetooth.BluetoothDevice.BOND_NONE; -import static android.bluetooth.BluetoothDevice.ERROR; -import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; - -import static java.util.concurrent.TimeUnit.SECONDS; - -import android.annotation.SuppressLint; -import android.bluetooth.BluetoothDevice; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -import androidx.annotation.WorkerThread; - -import com.google.common.base.Strings; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -/** - * Pairs to Bluetooth classic devices with passkey confirmation. - */ -// TODO(b/202524672): Add class unit test. -public class BluetoothClassicPairer { - - private static final String TAG = BluetoothClassicPairer.class.getSimpleName(); - /** - * Hidden, see {@link BluetoothDevice}. - */ - private static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; - - private final Context mContext; - private final BluetoothDevice mDevice; - private final Preferences mPreferences; - private final PasskeyConfirmationHandler mPasskeyConfirmationHandler; - - public BluetoothClassicPairer( - Context context, - BluetoothDevice device, - Preferences preferences, - PasskeyConfirmationHandler passkeyConfirmationHandler) { - this.mContext = context; - this.mDevice = device; - this.mPreferences = preferences; - this.mPasskeyConfirmationHandler = passkeyConfirmationHandler; - } - - /** - * Pairs with the device. Throws a {@link PairingException} if any error occurs. - */ - @WorkerThread - public void pair() throws PairingException { - Log.i(TAG, "BluetoothClassicPairer, createBond with " + maskBluetoothAddress(mDevice) - + ", type=" + mDevice.getType()); - try (BondedReceiver bondedReceiver = new BondedReceiver()) { - if (mDevice.createBond()) { - bondedReceiver.await(mPreferences.getCreateBondTimeoutSeconds(), SECONDS); - } else { - throw new PairingException( - "BluetoothClassicPairer, createBond got immediate error"); - } - } catch (TimeoutException | InterruptedException | ExecutionException e) { - throw new PairingException("BluetoothClassicPairer, createBond failed", e); - } - } - - protected boolean isPaired() { - return mDevice.getBondState() == BOND_BONDED; - } - - /** - * Receiver that closes after bonding has completed. - */ - private class BondedReceiver extends DeviceIntentReceiver { - - private BondedReceiver() { - super( - mContext, - mPreferences, - mDevice, - BluetoothDevice.ACTION_PAIRING_REQUEST, - BluetoothDevice.ACTION_BOND_STATE_CHANGED); - } - - /** - * Called with ACTION_PAIRING_REQUEST and ACTION_BOND_STATE_CHANGED about the interesting - * device (see {@link DeviceIntentReceiver}). - * - *

    The ACTION_PAIRING_REQUEST intent provides the passkey which will be sent to the - * {@link PasskeyConfirmationHandler} for showing the UI, and the ACTION_BOND_STATE_CHANGED - * will provide the result of the bonding. - */ - @Override - protected void onReceiveDeviceIntent(Intent intent) { - String intentAction = intent.getAction(); - BluetoothDevice remoteDevice = intent.getParcelableExtra(EXTRA_DEVICE); - if (Strings.isNullOrEmpty(intentAction) - || remoteDevice == null - || !remoteDevice.getAddress().equals(mDevice.getAddress())) { - Log.w(TAG, - "BluetoothClassicPairer, receives " + intentAction - + " from unexpected device " + maskBluetoothAddress(remoteDevice)); - return; - } - switch (intentAction) { - case BluetoothDevice.ACTION_PAIRING_REQUEST: - handlePairingRequest( - remoteDevice, - intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, ERROR), - intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, ERROR)); - break; - case BluetoothDevice.ACTION_BOND_STATE_CHANGED: - handleBondStateChanged( - intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, ERROR), - intent.getIntExtra(EXTRA_REASON, ERROR)); - break; - default: - break; - } - } - - private void handlePairingRequest(BluetoothDevice device, int variant, int passkey) { - Log.i(TAG, - "BluetoothClassicPairer, pairing request, " + device + ", " + variant + ", " - + passkey); - // Prevent Bluetooth Settings from getting the pairing request and showing its own UI. - abortBroadcast(); - mPasskeyConfirmationHandler.onPasskeyConfirmation(device, passkey); - } - - private void handleBondStateChanged(int bondState, int reason) { - Log.i(TAG, - "BluetoothClassicPairer, bond state changed to " + bondState + ", reason=" - + reason); - switch (bondState) { - case BOND_BONDING: - // Don't close! - return; - case BOND_BONDED: - close(); - return; - case BOND_NONE: - default: - closeWithError( - new PairingException( - "BluetoothClassicPairer, createBond failed, reason:" + reason)); - } - } - } - - // Applies UsesPermission annotation will create circular dependency. - @SuppressLint("MissingPermission") - static void setPairingConfirmation(BluetoothDevice device, boolean confirm) { - Log.i(TAG, "BluetoothClassicPairer: setPairingConfirmation " + maskBluetoothAddress(device) - + ", confirm: " + confirm); - device.setPairingConfirmation(confirm); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothUuids.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothUuids.java deleted file mode 100644 index c5475a69ed09ce212cbd8ddbfe89e5bb45b84552..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BluetoothUuids.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import java.util.UUID; - -/** - * Utilities for dealing with UUIDs assigned by the Bluetooth SIG. Has a lot in common with - * com.android.BluetoothUuid, but that class is hidden. - */ -public class BluetoothUuids { - - /** - * The Base UUID is used for calculating 128-bit UUIDs from "short UUIDs" (16- and 32-bit). - * - * @see {https://www.bluetooth.com/specifications/assigned-numbers/service-discovery} - */ - private static final UUID BASE_UUID = UUID.fromString("00000000-0000-1000-8000-00805F9B34FB"); - - /** - * Fast Pair custom GATT characteristics 128-bit UUIDs base. - * - *

    Notes: The 16-bit value locates at the 3rd and 4th bytes. - * - * @see {go/fastpair-128bit-gatt} - */ - private static final UUID FAST_PAIR_BASE_UUID = - UUID.fromString("FE2C0000-8366-4814-8EB0-01DE32100BEA"); - - private static final int BIT_INDEX_OF_16_BIT_UUID = 32; - - private BluetoothUuids() {} - - /** - * Returns the 16-bit version of the UUID. If this is not a 16-bit UUID, throws - * IllegalArgumentException. - */ - public static short get16BitUuid(UUID uuid) { - if (!is16BitUuid(uuid)) { - throw new IllegalArgumentException("Not a 16-bit Bluetooth UUID: " + uuid); - } - return (short) (uuid.getMostSignificantBits() >> BIT_INDEX_OF_16_BIT_UUID); - } - - /** Checks whether the UUID is 16 bit */ - public static boolean is16BitUuid(UUID uuid) { - // See Service Discovery Protocol in the Bluetooth Core Specification. Bits at index 32-48 - // are the 16-bit UUID, and the rest must match the Base UUID. - return uuid.getLeastSignificantBits() == BASE_UUID.getLeastSignificantBits() - && (uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) - == BASE_UUID.getMostSignificantBits(); - } - - /** Converts short UUID to 128 bit UUID */ - public static UUID to128BitUuid(short shortUuid) { - return new UUID( - ((shortUuid & 0xFFFFL) << BIT_INDEX_OF_16_BIT_UUID) - | BASE_UUID.getMostSignificantBits(), BASE_UUID.getLeastSignificantBits()); - } - - /** Transfers the 16-bit Fast Pair custom GATT characteristics to 128-bit. */ - public static UUID toFastPair128BitUuid(short shortUuid) { - return new UUID( - ((shortUuid & 0xFFFFL) << BIT_INDEX_OF_16_BIT_UUID) - | FAST_PAIR_BASE_UUID.getMostSignificantBits(), - FAST_PAIR_BASE_UUID.getLeastSignificantBits()); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BroadcastConstants.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BroadcastConstants.java deleted file mode 100644 index c26c6adf958075a49118c69100249456b29fc6c8..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/BroadcastConstants.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -/** - * Constants to share with the cloud syncing process. - */ -public class BroadcastConstants { - - // TODO: Set right value for AOSP. - /** Package name of the cloud syncing logic. */ - public static final String PACKAGE_NAME = "PACKAGE_NAME"; - /** Service name of the cloud syncing instance. */ - public static final String SERVICE_NAME = PACKAGE_NAME + ".SERVICE_NAME"; - private static final String PREFIX = PACKAGE_NAME + ".PREFIX_NAME."; - - /** Action when a fast pair device is added. */ - public static final String ACTION_FAST_PAIR_DEVICE_ADDED = - PREFIX + "ACTION_FAST_PAIR_DEVICE_ADDED"; - /** - * The BLE address of a device. BLE is used here instead of public because the caller of the - * library never knows what the device's public address is. - */ - public static final String EXTRA_ADDRESS = PREFIX + "BLE_ADDRESS"; - /** The public address of a device. */ - public static final String EXTRA_PUBLIC_ADDRESS = PREFIX + "PUBLIC_ADDRESS"; - /** Account key. */ - public static final String EXTRA_ACCOUNT_KEY = PREFIX + "ACCOUNT_KEY"; - /** Whether a paring is retroactive. */ - public static final String EXTRA_RETROACTIVE_PAIR = PREFIX + "EXTRA_RETROACTIVE_PAIR"; - - private BroadcastConstants() { - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java deleted file mode 100644 index 637cd03ba42c540b9e991419fdb7c4461eb8539e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import androidx.annotation.Nullable; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; -import java.util.Arrays; - -/** Represents a block of bytes, with hashCode and equals. */ -public abstract class Bytes { - private static final char[] sHexDigits = "0123456789abcdef".toCharArray(); - private final byte[] mBytes; - - /** - * A logical value consisting of one or more bytes in the given order (little-endian, i.e. - * LSO...MSO, or big-endian, i.e. MSO...LSO). E.g. the Fast Pair Model ID is a 3-byte value, - * and a Bluetooth device address is a 6-byte value. - */ - public static class Value extends Bytes { - private final ByteOrder mByteOrder; - - /** - * Constructor. - */ - public Value(byte[] bytes, ByteOrder byteOrder) { - super(bytes); - this.mByteOrder = byteOrder; - } - - /** - * Gets bytes. - */ - public byte[] getBytes(ByteOrder byteOrder) { - return this.mByteOrder.equals(byteOrder) ? getBytes() : reverse(getBytes()); - } - - private static byte[] reverse(byte[] bytes) { - byte[] reversedBytes = new byte[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - reversedBytes[i] = bytes[bytes.length - i - 1]; - } - return reversedBytes; - } - } - - Bytes(byte[] bytes) { - mBytes = bytes; - } - - private static String toHexString(byte[] bytes) { - StringBuilder sb = new StringBuilder(2 * bytes.length); - for (byte b : bytes) { - sb.append(sHexDigits[(b >> 4) & 0xf]).append(sHexDigits[b & 0xf]); - } - return sb.toString(); - } - - /** Returns 2-byte values in the same order, each using the given byte order. */ - public static byte[] toBytes(ByteOrder byteOrder, short... shorts) { - ByteBuffer byteBuffer = ByteBuffer.allocate(shorts.length * 2).order(byteOrder); - for (short s : shorts) { - byteBuffer.putShort(s); - } - return byteBuffer.array(); - } - - /** Returns the shorts in the same order, each converted using the given byte order. */ - static short[] toShorts(ByteOrder byteOrder, byte[] bytes) { - ShortBuffer shortBuffer = ByteBuffer.wrap(bytes).order(byteOrder).asShortBuffer(); - short[] shorts = new short[shortBuffer.remaining()]; - shortBuffer.get(shorts); - return shorts; - } - - /** @return The bytes. */ - public byte[] getBytes() { - return mBytes; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Bytes)) { - return false; - } - Bytes that = (Bytes) o; - return Arrays.equals(mBytes, that.mBytes); - } - - @Override - public int hashCode() { - return Arrays.hashCode(mBytes); - } - - @Override - public String toString() { - return toHexString(mBytes); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ConnectException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ConnectException.java deleted file mode 100644 index 9c8d292b2c1fed08ebca315a40730d75dce24f42..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ConnectException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import com.android.server.nearby.intdefs.FastPairEventIntDefs.ConnectErrorCode; - - -/** Thrown when connecting to a bluetooth device fails. */ -public class ConnectException extends PairingException { - final @ConnectErrorCode int mErrorCode; - - ConnectException(@ConnectErrorCode int errorCode, String format, Object... objects) { - super(format, objects); - this.mErrorCode = errorCode; - } - - /** Returns error code. */ - public @ConnectErrorCode int getErrorCode() { - return mErrorCode; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java deleted file mode 100644 index cfecd2f5be5a69400b8a8aae547f009ffa8ee41a..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Constants.java +++ /dev/null @@ -1,703 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static android.bluetooth.BluetoothProfile.A2DP; -import static android.bluetooth.BluetoothProfile.HEADSET; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.to128BitUuid; -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.toFastPair128BitUuid; - -import static com.google.common.primitives.Bytes.concat; - -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothHeadset; -import android.util.Log; - -import androidx.annotation.IntDef; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.common.primitives.Shorts; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.Random; -import java.util.UUID; - -/** - * Fast Pair and Transport Discovery Service constants. - * - *

    Unless otherwise specified, these numbers come from - * {https://www.bluetooth.com/specifications/gatt}. - */ -public final class Constants { - - /** A2DP sink service uuid. */ - public static final short A2DP_SINK_SERVICE_UUID = 0x110B; - - /** Headset service uuid. */ - public static final short HEADSET_SERVICE_UUID = 0x1108; - - /** Hands free sink service uuid. */ - public static final short HANDS_FREE_SERVICE_UUID = 0x111E; - - /** Bluetooth address length. */ - public static final int BLUETOOTH_ADDRESS_LENGTH = 6; - - private static final String TAG = Constants.class.getSimpleName(); - - /** - * Defined by https://developers.google.com/nearby/fast-pair/spec. - */ - public static final class FastPairService { - - /** Fast Pair service UUID. */ - public static final UUID ID = to128BitUuid((short) 0xFE2C); - - /** - * Characteristic to write verification bytes to during the key handshake. - */ - public static final class KeyBasedPairingCharacteristic { - - private static final short SHORT_UUID = 0x1234; - - /** - * Gets the new 128-bit UUID of this characteristic. - * - *

    Note: For GATT server only. GATT client should use {@link - * KeyBasedPairingCharacteristic#getId(BluetoothGattConnection)}. - */ - public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID); - - /** - * Gets the {@link UUID} of this characteristic. - * - *

    This method is designed for being backward compatible with old version of UUID - * therefore needs the {@link BluetoothGattConnection} parameter to check the supported - * status of the Fast Pair provider. - */ - public static UUID getId(BluetoothGattConnection gattConnection) { - return getSupportedUuid(gattConnection, SHORT_UUID); - } - - /** - * Constants related to the decrypted request written to this characteristic. - */ - public static final class Request { - - /** - * The size of this message. - */ - public static final int SIZE = 16; - - /** - * The index of this message for indicating the type byte. - */ - public static final int TYPE_INDEX = 0; - - /** - * The index of this message for indicating the flags byte. - */ - public static final int FLAGS_INDEX = 1; - - /** - * The index of this message for indicating the verification data start from. - */ - public static final int VERIFICATION_DATA_INDEX = 2; - - /** - * The length of verification data, it is Provider’s current BLE address or public - * address. - */ - public static final int VERIFICATION_DATA_LENGTH = BLUETOOTH_ADDRESS_LENGTH; - - /** - * The index of this message for indicating the seeker's public address start from. - */ - public static final int SEEKER_PUBLIC_ADDRESS_INDEX = 8; - - /** - * The index of this message for indicating event group. - */ - public static final int EVENT_GROUP_INDEX = 8; - - /** - * The index of this message for indicating event code. - */ - public static final int EVENT_CODE_INDEX = 9; - - /** - * The index of this message for indicating the length of additional data of the - * event. - */ - public static final int EVENT_ADDITIONAL_DATA_LENGTH_INDEX = 10; - - /** - * The index of this message for indicating the event additional data start from. - */ - public static final int EVENT_ADDITIONAL_DATA_INDEX = 11; - - /** - * The index of this message for indicating the additional data type used in the - * following Additional Data characteristic. - */ - public static final int ADDITIONAL_DATA_TYPE_INDEX = 10; - - /** - * The type of this message for Key-based Pairing Request. - */ - public static final byte TYPE_KEY_BASED_PAIRING_REQUEST = 0x00; - - /** - * The bit indicating that the Fast Pair device should temporarily become - * discoverable. - */ - public static final byte REQUEST_DISCOVERABLE = (byte) (1 << 7); - - /** - * The bit indicating that the requester (Seeker) has included their public address - * in bytes [7,12] of the request, and the Provider should initiate bonding to that - * address. - */ - public static final byte PROVIDER_INITIATES_BONDING = (byte) (1 << 6); - - /** - * The bit indicating that Seeker requests Provider shall return the existing name. - */ - public static final byte REQUEST_DEVICE_NAME = (byte) (1 << 5); - - /** - * The bit to request retroactive pairing. - */ - public static final byte REQUEST_RETROACTIVE_PAIR = (byte) (1 << 4); - - /** - * The type of this message for action over BLE. - */ - public static final byte TYPE_ACTION_OVER_BLE = 0x10; - - private Request() { - } - } - - /** - * Enumerates all flags of key-based pairing request. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - KeyBasedPairingRequestFlag.REQUEST_DISCOVERABLE, - KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING, - KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME, - KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR, - }) - public @interface KeyBasedPairingRequestFlag { - /** - * The bit indicating that the Fast Pair device should temporarily become - * discoverable. - */ - int REQUEST_DISCOVERABLE = (byte) (1 << 7); - /** - * The bit indicating that the requester (Seeker) has included their public address - * in bytes [7,12] of the request, and the Provider should initiate bonding to that - * address. - */ - int PROVIDER_INITIATES_BONDING = (byte) (1 << 6); - /** - * The bit indicating that Seeker requests Provider shall return the existing name. - */ - int REQUEST_DEVICE_NAME = (byte) (1 << 5); - /** - * The bit indicating that the Seeker request retroactive pairing. - */ - int REQUEST_RETROACTIVE_PAIR = (byte) (1 << 4); - } - - /** - * Enumerates all flags of action over BLE request, see Fast Pair spec for details. - */ - @IntDef( - value = { - ActionOverBleFlag.DEVICE_ACTION, - ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC, - }) - public @interface ActionOverBleFlag { - /** - * The bit indicating that the handshaking is for Device Action. - */ - int DEVICE_ACTION = (byte) (1 << 7); - /** - * The bit indicating that this handshake will be followed by Additional Data - * characteristic. - */ - int ADDITIONAL_DATA_CHARACTERISTIC = (byte) (1 << 6); - } - - - /** - * Constants related to the decrypted response sent back in a notify. - */ - public static final class Response { - - /** - * The type of this message = Key-based Pairing Response. - */ - public static final byte TYPE = 0x01; - - private Response() { - } - } - - private KeyBasedPairingCharacteristic() { - } - } - - /** - * Characteristic used during Key-based Pairing, to exchange the encrypted passkey. - */ - public static final class PasskeyCharacteristic { - - private static final short SHORT_UUID = 0x1235; - - /** - * Gets the new 128-bit UUID of this characteristic. - * - *

    Note: For GATT server only. GATT client should use {@link - * PasskeyCharacteristic#getId(BluetoothGattConnection)}. - */ - public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID); - - /** - * Gets the {@link UUID} of this characteristic. - * - *

    This method is designed for being backward compatible with old version of UUID - * therefore - * needs the {@link BluetoothGattConnection} parameter to check the supported status of - * the Fast Pair provider. - */ - public static UUID getId(BluetoothGattConnection gattConnection) { - return getSupportedUuid(gattConnection, SHORT_UUID); - } - - /** - * The type of the Passkey Block message. - */ - @IntDef( - value = { - Type.SEEKER, - Type.PROVIDER, - }) - public @interface Type { - /** - * Seeker's Passkey. - */ - int SEEKER = (byte) 0x02; - /** - * Provider's Passkey. - */ - int PROVIDER = (byte) 0x03; - } - - /** - * Constructs the encrypted value to write to the characteristic. - */ - public static byte[] encrypt(@Type int type, byte[] secret, int passkey) - throws GeneralSecurityException { - Preconditions.checkArgument( - 0 < passkey && passkey < /*2^24=*/ 16777216, - "Passkey %s must be positive and fit in 3 bytes", - passkey); - byte[] passkeyBytes = - new byte[]{(byte) (passkey >>> 16), (byte) (passkey >>> 8), (byte) passkey}; - byte[] salt = - new byte[AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH - 1 - - passkeyBytes.length]; - new Random().nextBytes(salt); - return AesEcbSingleBlockEncryption.encrypt( - secret, concat(new byte[]{(byte) type}, passkeyBytes, salt)); - } - - /** - * Extracts the passkey from the encrypted characteristic value. - */ - public static int decrypt(@Type int type, byte[] secret, - byte[] passkeyCharacteristicValue) - throws GeneralSecurityException { - byte[] decrypted = AesEcbSingleBlockEncryption - .decrypt(secret, passkeyCharacteristicValue); - if (decrypted[0] != (byte) type) { - throw new GeneralSecurityException( - "Wrong Passkey Block type (expected " + type + ", got " - + decrypted[0] + ")"); - } - return ByteBuffer.allocate(4) - .put((byte) 0) - .put(decrypted, /*offset=*/ 1, /*length=*/ 3) - .getInt(0); - } - - private PasskeyCharacteristic() { - } - } - - /** - * Characteristic to write to during the key exchange. - */ - public static final class AccountKeyCharacteristic { - - private static final short SHORT_UUID = 0x1236; - - /** - * Gets the new 128-bit UUID of this characteristic. - * - *

    Note: For GATT server only. GATT client should use {@link - * AccountKeyCharacteristic#getId(BluetoothGattConnection)}. - */ - public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID); - - /** - * Gets the {@link UUID} of this characteristic. - * - *

    This method is designed for being backward compatible with old version of UUID - * therefore - * needs the {@link BluetoothGattConnection} parameter to check the supported status of - * the Fast Pair provider. - */ - public static UUID getId(BluetoothGattConnection gattConnection) { - return getSupportedUuid(gattConnection, SHORT_UUID); - } - - /** - * The type for this message, account key request. - */ - public static final byte TYPE = 0x04; - - private AccountKeyCharacteristic() { - } - } - - /** - * Characteristic to write to and notify on for handling personalized name, see {@link - * NamingEncoder}. - */ - public static final class NameCharacteristic { - - private static final short SHORT_UUID = 0x1237; - - /** - * Gets the new 128-bit UUID of this characteristic. - * - *

    Note: For GATT server only. GATT client should use {@link - * NameCharacteristic#getId(BluetoothGattConnection)}. - */ - public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID); - - /** - * Gets the {@link UUID} of this characteristic. - * - *

    This method is designed for being backward compatible with old version of UUID - * therefore - * needs the {@link BluetoothGattConnection} parameter to check the supported status of - * the Fast Pair provider. - */ - public static UUID getId(BluetoothGattConnection gattConnection) { - return getSupportedUuid(gattConnection, SHORT_UUID); - } - - private NameCharacteristic() { - } - } - - /** - * Characteristic to write to and notify on for handling additional data, see - * https://developers.google.com/nearby/fast-pair/early-access/spec#AdditionalData - */ - public static final class AdditionalDataCharacteristic { - - private static final short SHORT_UUID = 0x1237; - - public static final int DATA_ID_INDEX = 0; - public static final int DATA_LENGTH_INDEX = 1; - public static final int DATA_START_INDEX = 2; - - /** - * Gets the new 128-bit UUID of this characteristic. - * - *

    Note: For GATT server only. GATT client should use {@link - * AdditionalDataCharacteristic#getId(BluetoothGattConnection)}. - */ - public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID); - - /** - * Gets the {@link UUID} of this characteristic. - * - *

    This method is designed for being backward compatible with old version of UUID - * therefore - * needs the {@link BluetoothGattConnection} parameter to check the supported status of - * the Fast Pair provider. - */ - public static UUID getId(BluetoothGattConnection gattConnection) { - return getSupportedUuid(gattConnection, SHORT_UUID); - } - - /** - * Enumerates all types of additional data. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - AdditionalDataType.PERSONALIZED_NAME, - AdditionalDataType.UNKNOWN, - }) - public @interface AdditionalDataType { - /** - * The value indicating that the type is for personalized name. - */ - int PERSONALIZED_NAME = (byte) 0x01; - int UNKNOWN = (byte) 0x00; // and all others. - } - } - - /** - * Characteristic to control the beaconing feature (FastPair+Eddystone). - */ - public static final class BeaconActionsCharacteristic { - - private static final short SHORT_UUID = 0x1238; - - /** - * Gets the new 128-bit UUID of this characteristic. - * - *

    Note: For GATT server only. GATT client should use {@link - * BeaconActionsCharacteristic#getId(BluetoothGattConnection)}. - */ - public static final UUID CUSTOM_128_BIT_UUID = toFastPair128BitUuid(SHORT_UUID); - - /** - * Gets the {@link UUID} of this characteristic. - * - *

    This method is designed for being backward compatible with old version of UUID - * therefore - * needs the {@link BluetoothGattConnection} parameter to check the supported status of - * the Fast Pair provider. - */ - public static UUID getId(BluetoothGattConnection gattConnection) { - return getSupportedUuid(gattConnection, SHORT_UUID); - } - - /** - * Enumerates all types of beacon actions. - */ - /** Fast Pair Bond State. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - BeaconActionType.READ_BEACON_PARAMETERS, - BeaconActionType.READ_PROVISIONING_STATE, - BeaconActionType.SET_EPHEMERAL_IDENTITY_KEY, - BeaconActionType.CLEAR_EPHEMERAL_IDENTITY_KEY, - BeaconActionType.READ_EPHEMERAL_IDENTITY_KEY, - BeaconActionType.RING, - BeaconActionType.READ_RINGING_STATE, - BeaconActionType.UNKNOWN, - }) - public @interface BeaconActionType { - int READ_BEACON_PARAMETERS = (byte) 0x00; - int READ_PROVISIONING_STATE = (byte) 0x01; - int SET_EPHEMERAL_IDENTITY_KEY = (byte) 0x02; - int CLEAR_EPHEMERAL_IDENTITY_KEY = (byte) 0x03; - int READ_EPHEMERAL_IDENTITY_KEY = (byte) 0x04; - int RING = (byte) 0x05; - int READ_RINGING_STATE = (byte) 0x06; - int UNKNOWN = (byte) 0xFF; // and all others - } - - /** Converts value to enum. */ - public static @BeaconActionType int valueOf(byte value) { - switch(value) { - case BeaconActionType.READ_BEACON_PARAMETERS: - case BeaconActionType.READ_PROVISIONING_STATE: - case BeaconActionType.SET_EPHEMERAL_IDENTITY_KEY: - case BeaconActionType.CLEAR_EPHEMERAL_IDENTITY_KEY: - case BeaconActionType.READ_EPHEMERAL_IDENTITY_KEY: - case BeaconActionType.RING: - case BeaconActionType.READ_RINGING_STATE: - case BeaconActionType.UNKNOWN: - return value; - default: - return BeaconActionType.UNKNOWN; - } - } - } - - - /** - * Characteristic to read for checking firmware version. 0X2A26 is assigned number from - * bluetooth SIG website. - */ - public static final class FirmwareVersionCharacteristic { - - /** UUID for firmware version. */ - public static final UUID ID = to128BitUuid((short) 0x2A26); - - private FirmwareVersionCharacteristic() { - } - } - - private FastPairService() { - } - } - - /** - * Defined by the BR/EDR Handover Profile. Pre-release version here: - * {https://jfarfel.users.x20web.corp.google.com/Bluetooth%20Handover%20d09.pdf} - */ - public interface TransportDiscoveryService { - - UUID ID = to128BitUuid((short) 0x1824); - - byte BLUETOOTH_SIG_ORGANIZATION_ID = 0x01; - byte SERVICE_UUIDS_16_BIT_LIST_TYPE = 0x01; - byte SERVICE_UUIDS_32_BIT_LIST_TYPE = 0x02; - byte SERVICE_UUIDS_128_BIT_LIST_TYPE = 0x03; - - /** - * Writing to this allows you to activate the BR/EDR transport. - */ - interface ControlPointCharacteristic { - - UUID ID = to128BitUuid((short) 0x2ABC); - byte ACTIVATE_TRANSPORT_OP_CODE = 0x01; - } - - /** - * Info necessary to pair (mostly the Bluetooth Address). - */ - interface BrHandoverDataCharacteristic { - - UUID ID = to128BitUuid((short) 0x2C01); - - /** - * All bits are reserved for future use. - */ - byte BR_EDR_FEATURES = 0x00; - } - - /** - * This characteristic exists only to wrap the descriptor. - */ - interface BluetoothSigDataCharacteristic { - - UUID ID = to128BitUuid((short) 0x2C02); - - /** - * The entire Transport Block data (e.g. supported Bluetooth services). - */ - interface BrTransportBlockDataDescriptor { - - UUID ID = to128BitUuid((short) 0x2C03); - } - } - } - - public static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_UUID = - to128BitUuid((short) 0x2902); - - /** - * Wrapper for Bluetooth profile - */ - public static class Profile { - - public final int type; - public final String name; - public final String connectionStateAction; - - private Profile(int type, String name, String connectionStateAction) { - this.type = type; - this.name = name; - this.connectionStateAction = connectionStateAction; - } - - @Override - public String toString() { - return name; - } - } - - /** - * {@link BluetoothHeadset} is used for both Headset and HandsFree (HFP). - */ - private static final Profile HEADSET_AND_HANDS_FREE_PROFILE = - new Profile( - HEADSET, "HEADSET_AND_HANDS_FREE", - BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - - /** Fast Pair supported profiles. */ - public static final ImmutableMap PROFILES = - ImmutableMap.builder() - .put( - Constants.A2DP_SINK_SERVICE_UUID, - new Profile(A2DP, "A2DP", - BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) - .put(Constants.HEADSET_SERVICE_UUID, HEADSET_AND_HANDS_FREE_PROFILE) - .put(Constants.HANDS_FREE_SERVICE_UUID, HEADSET_AND_HANDS_FREE_PROFILE) - .build(); - - static short[] getSupportedProfiles() { - return Shorts.toArray(PROFILES.keySet()); - } - - /** - * Helper method of getting 128-bit UUID for Fast Pair custom GATT characteristics. - * - *

    This method is designed for being backward compatible with old version of UUID therefore - * needs the {@link BluetoothGattConnection} parameter to check the supported status of the Fast - * Pair provider. - * - *

    Note: For new custom GATT characteristics, don't need to use this helper and please just - * call {@code toFastPair128BitUuid(shortUuid)} to get the UUID. Which also implies that callers - * don't need to provide {@link BluetoothGattConnection} to get the UUID anymore. - */ - private static UUID getSupportedUuid(BluetoothGattConnection gattConnection, short shortUuid) { - // In worst case (new characteristic not found), this method's performance impact is about - // 6ms - // by using Pixel2 + JBL LIVE220. And the impact should be less and less along with more and - // more devices adopt the new characteristics. - try { - // Checks the new UUID first. - if (gattConnection - .getCharacteristic(FastPairService.ID, toFastPair128BitUuid(shortUuid)) - != null) { - Log.d(TAG, "Uses new KeyBasedPairingCharacteristic.ID"); - return toFastPair128BitUuid(shortUuid); - } - } catch (BluetoothException e) { - Log.d(TAG, "Uses old KeyBasedPairingCharacteristic.ID"); - } - // Returns the old UUID for default. - return to128BitUuid(shortUuid); - } - - private Constants() { - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/CreateBondException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/CreateBondException.java deleted file mode 100644 index d6aa3b2baa912b6ad6b3611ddaad7348a5ce198c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/CreateBondException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import com.android.server.nearby.intdefs.FastPairEventIntDefs.CreateBondErrorCode; - -/** Thrown when binding (pairing) with a bluetooth device fails. */ -public class CreateBondException extends PairingException { - final @CreateBondErrorCode int mErrorCode; - int mReason; - - CreateBondException(@CreateBondErrorCode int errorCode, int reason, String format, - Object... objects) { - super(format, objects); - this.mErrorCode = errorCode; - this.mReason = reason; - } - - /** Returns error code. */ - public @CreateBondErrorCode int getErrorCode() { - return mErrorCode; - } - - /** Returns reason. */ - public int getReason() { - return mReason; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiver.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiver.java deleted file mode 100644 index 5bcf10ab2b7278c3111566146ca739b7fd0f6b24..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiver.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; - -import android.bluetooth.BluetoothDevice; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -/** - * Like {@link SimpleBroadcastReceiver}, but for intents about a certain {@link BluetoothDevice}. - */ -abstract class DeviceIntentReceiver extends SimpleBroadcastReceiver { - - private static final String TAG = DeviceIntentReceiver.class.getSimpleName(); - - private final BluetoothDevice mDevice; - - static DeviceIntentReceiver oneShotReceiver( - Context context, Preferences preferences, BluetoothDevice device, String... actions) { - return new DeviceIntentReceiver(context, preferences, device, actions) { - @Override - protected void onReceiveDeviceIntent(Intent intent) throws Exception { - close(); - } - }; - } - - /** - * @param context The context to use to register / unregister the receiver. - * @param device The interesting device. We ignore intents about other devices. - * @param actions The actions to include in our intent filter. - */ - protected DeviceIntentReceiver( - Context context, Preferences preferences, BluetoothDevice device, String... actions) { - super(context, preferences, actions); - this.mDevice = device; - } - - /** - * Called with intents about the interesting device (see {@link #DeviceIntentReceiver}). Any - * exception thrown by this method will be delivered via {@link #await}. - */ - protected abstract void onReceiveDeviceIntent(Intent intent) throws Exception; - - // incompatible types in argument. - @Override - protected void onReceive(Intent intent) throws Exception { - BluetoothDevice intentDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (mDevice == null || mDevice.equals(intentDevice)) { - onReceiveDeviceIntent(intent); - } else { - Log.v(TAG, - "Ignoring intent for device=" + maskBluetoothAddress(intentDevice) - + "(expected " - + maskBluetoothAddress(mDevice) + ")"); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EllipticCurveDiffieHellmanExchange.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EllipticCurveDiffieHellmanExchange.java deleted file mode 100644 index dbcdf077ea16a08167ddff513858161b33d85e88..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EllipticCurveDiffieHellmanExchange.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.google.common.primitives.Bytes.concat; - -import androidx.annotation.Nullable; - -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import java.util.Arrays; - -import javax.crypto.KeyAgreement; - -/** - * Helper for generating keys based off of the Elliptic-Curve Diffie-Hellman algorithm (ECDH). - */ -public final class EllipticCurveDiffieHellmanExchange { - - public static final int PUBLIC_KEY_LENGTH = 64; - static final int PRIVATE_KEY_LENGTH = 32; - - private static final String[] PROVIDERS = {"GmsCore_OpenSSL", "AndroidOpenSSL", "SC", "BC"}; - - private static final String EC_ALGORITHM = "EC"; - - /** - * Also known as prime256v1 or NIST P-256. - */ - private static final ECGenParameterSpec EC_GEN_PARAMS = new ECGenParameterSpec("secp256r1"); - - @Nullable - private final ECPublicKey mPublicKey; - private final ECPrivateKey mPrivateKey; - - /** - * Creates a new EllipticCurveDiffieHellmanExchange object. - */ - public static EllipticCurveDiffieHellmanExchange create() throws GeneralSecurityException { - KeyPair keyPair = generateKeyPair(); - return new EllipticCurveDiffieHellmanExchange( - (ECPublicKey) keyPair.getPublic(), (ECPrivateKey) keyPair.getPrivate()); - } - - /** - * Creates a new EllipticCurveDiffieHellmanExchange object. - */ - public static EllipticCurveDiffieHellmanExchange create(byte[] privateKey) - throws GeneralSecurityException { - ECPrivateKey ecPrivateKey = (ECPrivateKey) generatePrivateKey(privateKey); - return new EllipticCurveDiffieHellmanExchange(/*publicKey=*/ null, ecPrivateKey); - } - - private EllipticCurveDiffieHellmanExchange( - @Nullable ECPublicKey publicKey, ECPrivateKey privateKey) { - this.mPublicKey = publicKey; - this.mPrivateKey = privateKey; - } - - /** - * @param otherPublicKey Another party's public key. See {@link #getPublicKey()} for format. - * @return The shared secret. Given our public key (and its private key), the other party can - * generate the same secret. This is a key meant for symmetric encryption. - */ - public byte[] generateSecret(byte[] otherPublicKey) throws GeneralSecurityException { - KeyAgreement agreement = keyAgreement(); - agreement.init(mPrivateKey); - agreement.doPhase(generatePublicKey(otherPublicKey), /*lastPhase=*/ true); - byte[] secret = agreement.generateSecret(); - // Headsets only support AES with 128-bit keys. So, hash the secret so that the entropy is - // high and then take only the first 128-bits. - secret = MessageDigest.getInstance("SHA-256").digest(secret); - return Arrays.copyOf(secret, 16); - } - - /** - * Returns a public point W on the NIST P-256 elliptic curve. First 32 bytes are the X - * coordinate, next 32 bytes are the Y coordinate. Each coordinate is an unsigned big-endian - * integer. - */ - public @Nullable byte[] getPublicKey() { - if (mPublicKey == null) { - return null; - } - ECPoint w = mPublicKey.getW(); - // See getPrivateKey for why we're resizing. - byte[] x = resizeWithLeadingZeros(w.getAffineX().toByteArray(), 32); - byte[] y = resizeWithLeadingZeros(w.getAffineY().toByteArray(), 32); - return concat(x, y); - } - - /** - * Returns a private value S, an unsigned big-endian integer. - */ - public byte[] getPrivateKey() { - // Note that BigInteger.toByteArray() returns a signed representation, so it will add an - // extra zero byte to the front if the first bit is 1. - // We must remove that leading zero (we know the number is unsigned). We must also add - // leading zeros if the number is too small. - return resizeWithLeadingZeros(mPrivateKey.getS().toByteArray(), 32); - } - - /** - * Removes or adds leading zeros until we have an array of size {@code n}. - */ - private static byte[] resizeWithLeadingZeros(byte[] x, int n) { - if (n < x.length) { - int start = x.length - n; - for (int i = 0; i < start; i++) { - if (x[i] != 0) { - throw new IllegalArgumentException( - "More than " + n + " non-zero bytes in " + Arrays.toString(x)); - } - } - return Arrays.copyOfRange(x, start, x.length); - } - return concat(new byte[n - x.length], x); - } - - /** - * @param publicKey See {@link #getPublicKey()} for format. - */ - private static PublicKey generatePublicKey(byte[] publicKey) throws GeneralSecurityException { - if (publicKey.length != PUBLIC_KEY_LENGTH) { - throw new GeneralSecurityException("Public key length incorrect: " + publicKey.length); - } - byte[] x = Arrays.copyOf(publicKey, publicKey.length / 2); - byte[] y = Arrays.copyOfRange(publicKey, publicKey.length / 2, publicKey.length); - return keyFactory() - .generatePublic( - new ECPublicKeySpec( - new ECPoint(new BigInteger(/*signum=*/ 1, x), - new BigInteger(/*signum=*/ 1, y)), - ecParameterSpec())); - } - - /** - * @param privateKey See {@link #getPrivateKey()} for format. - */ - private static PrivateKey generatePrivateKey(byte[] privateKey) - throws GeneralSecurityException { - if (privateKey.length != PRIVATE_KEY_LENGTH) { - throw new GeneralSecurityException("Private key length incorrect: " - + privateKey.length); - } - return keyFactory() - .generatePrivate( - new ECPrivateKeySpec(new BigInteger(/*signum=*/ 1, privateKey), - ecParameterSpec())); - } - - private static ECParameterSpec ecParameterSpec() throws GeneralSecurityException { - // This seems to be the simplest way to get the curve's ECParameterSpec. Verified that it's - // the same whether you get it from the public or private key, and that it's the same as the - // raw params in SecAggEcUtil.getNistP256Params(). - return ((ECPublicKey) generateKeyPair().getPublic()).getParams(); - } - - private static KeyPair generateKeyPair() throws GeneralSecurityException { - KeyPairGenerator generator = findProvider(p -> KeyPairGenerator.getInstance(EC_ALGORITHM, - p)); - generator.initialize(EC_GEN_PARAMS); - return generator.generateKeyPair(); - } - - private static KeyAgreement keyAgreement() throws NoSuchProviderException { - return findProvider(p -> KeyAgreement.getInstance("ECDH", p)); - } - - private static KeyFactory keyFactory() throws NoSuchProviderException { - return findProvider(p -> KeyFactory.getInstance(EC_ALGORITHM, p)); - } - - private interface ProviderConsumer { - - T tryProvider(String provider) throws NoSuchAlgorithmException, NoSuchProviderException; - } - - private static T findProvider(ProviderConsumer providerConsumer) - throws NoSuchProviderException { - for (String provider : PROVIDERS) { - try { - return providerConsumer.tryProvider(provider); - } catch (NoSuchAlgorithmException | NoSuchProviderException e) { - // No-op - } - } - throw new NoSuchProviderException(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java deleted file mode 100644 index 0b50dfd0092b5034ca40dc5c1d908781ae376402..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.bluetooth.BluetoothDevice; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.server.nearby.intdefs.NearbyEventIntDefs.EventCode; - -import java.util.Objects; - -import javax.annotation.Nullable; - -/** - * Describes events that are happening during fast pairing. EventCode is required, everything else - * is optional. - */ -public class Event implements Parcelable { - - private final @EventCode int mEventCode; - private final long mTimestamp; - private final Short mProfile; - private final BluetoothDevice mBluetoothDevice; - private final Exception mException; - - private Event(@EventCode int eventCode, long timestamp, @Nullable Short profile, - @Nullable BluetoothDevice bluetoothDevice, @Nullable Exception exception) { - mEventCode = eventCode; - mTimestamp = timestamp; - mProfile = profile; - mBluetoothDevice = bluetoothDevice; - mException = exception; - } - - /** - * Returns event code. - */ - public @EventCode int getEventCode() { - return mEventCode; - } - - /** - * Returns timestamp. - */ - public long getTimestamp() { - return mTimestamp; - } - - /** - * Returns profile. - */ - @Nullable - public Short getProfile() { - return mProfile; - } - - /** - * Returns Bluetooth device. - */ - @Nullable - public BluetoothDevice getBluetoothDevice() { - return mBluetoothDevice; - } - - /** - * Returns exception. - */ - @Nullable - public Exception getException() { - return mException; - } - - /** - * Returns whether profile is not null. - */ - public boolean hasProfile() { - return getProfile() != null; - } - - /** - * Returns whether Bluetooth device is not null. - */ - public boolean hasBluetoothDevice() { - return getBluetoothDevice() != null; - } - - /** - * Returns a builder. - */ - public static Builder builder() { - return new Event.Builder(); - } - - /** - * Returns whether it fails. - */ - public boolean isFailure() { - return getException() != null; - } - - @Override - public String toString() { - return "Event{" - + "eventCode=" + mEventCode + ", " - + "timestamp=" + mTimestamp + ", " - + "profile=" + mProfile + ", " - + "bluetoothDevice=" + mBluetoothDevice + ", " - + "exception=" + mException - + "}"; - } - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (o instanceof Event) { - Event that = (Event) o; - return this.mEventCode == that.getEventCode() - && this.mTimestamp == that.getTimestamp() - && (this.mProfile == null - ? that.getProfile() == null : this.mProfile.equals(that.getProfile())) - && (this.mBluetoothDevice == null - ? that.getBluetoothDevice() == null : - this.mBluetoothDevice.equals(that.getBluetoothDevice())) - && (this.mException == null - ? that.getException() == null : - this.mException.equals(that.getException())); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(mEventCode, mTimestamp, mProfile, mBluetoothDevice, mException); - } - - - /** - * Builder - */ - public static class Builder { - private @EventCode int mEventCode; - private long mTimestamp; - private Short mProfile; - private BluetoothDevice mBluetoothDevice; - private Exception mException; - - /** - * Set event code. - */ - public Builder setEventCode(@EventCode int eventCode) { - this.mEventCode = eventCode; - return this; - } - - /** - * Set timestamp. - */ - public Builder setTimestamp(long timestamp) { - this.mTimestamp = timestamp; - return this; - } - - /** - * Set profile. - */ - public Builder setProfile(@Nullable Short profile) { - this.mProfile = profile; - return this; - } - - /** - * Set Bluetooth device. - */ - public Builder setBluetoothDevice(@Nullable BluetoothDevice device) { - this.mBluetoothDevice = device; - return this; - } - - /** - * Set exception. - */ - public Builder setException(@Nullable Exception exception) { - this.mException = exception; - return this; - } - - /** - * Builds event. - */ - public Event build() { - return new Event(mEventCode, mTimestamp, mProfile, mBluetoothDevice, mException); - } - } - - @Override - public final void writeToParcel(Parcel dest, int flags) { - dest.writeInt(getEventCode()); - dest.writeLong(getTimestamp()); - dest.writeValue(getProfile()); - dest.writeParcelable(getBluetoothDevice(), 0); - dest.writeSerializable(getException()); - } - - @Override - public final int describeContents() { - return 0; - } - - /** - * Event Creator instance. - */ - public static final Creator CREATOR = - new Creator() { - @Override - /** Creates Event from Parcel. */ - public Event createFromParcel(Parcel in) { - return Event.builder() - .setEventCode(in.readInt()) - .setTimestamp(in.readLong()) - .setProfile((Short) in.readValue(Short.class.getClassLoader())) - .setBluetoothDevice( - in.readParcelable(BluetoothDevice.class.getClassLoader())) - .setException((Exception) in.readSerializable()) - .build(); - } - - @Override - /** Returns Event array. */ - public Event[] newArray(int size) { - return new Event[size]; - } - }; -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EventLogger.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EventLogger.java deleted file mode 100644 index 4fc1917a396fbd70ec925b9084b521d945680848..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EventLogger.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -/** Logs events triggered during Fast Pairing. */ -public interface EventLogger { - - /** Log successful event. */ - void logEventSucceeded(Event event); - - /** Log failed event. */ - void logEventFailed(Event event, Exception e); -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EventLoggerWrapper.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EventLoggerWrapper.java deleted file mode 100644 index 024bfdee8a94e6dc73f7dee6c099a5e4d74d8095..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/EventLoggerWrapper.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.bluetooth.BluetoothDevice; -import android.content.Context; - -import com.android.server.nearby.common.bluetooth.fastpair.Preferences.ExtraLoggingInformation; -import com.android.server.nearby.intdefs.NearbyEventIntDefs.EventCode; - -import javax.annotation.Nullable; - -/** - * Convenience wrapper around EventLogger. - */ -// TODO(b/202559985): cleanup EventLoggerWrapper. -class EventLoggerWrapper { - - EventLoggerWrapper(@Nullable EventLogger eventLogger) { - } - - /** - * Binds to the logging service. This operation blocks until binding has completed or timed - * out. - */ - void bind( - Context context, String address, - @Nullable ExtraLoggingInformation extraLoggingInformation) { - } - - boolean isBound() { - return false; - } - - void unbind(Context context) { - } - - void setCurrentEvent(@EventCode int code) { - } - - void setCurrentProfile(short profile) { - } - - void logCurrentEventFailed(Exception e) { - } - - void logCurrentEventSucceeded() { - } - - void setDevice(@Nullable BluetoothDevice device) { - } - - boolean isCurrentEvent() { - return false; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairConnection.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairConnection.java deleted file mode 100644 index c963aa6076879afccddab03adeee4bf3c4947e27..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairConnection.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.annotation.WorkerThread; -import android.bluetooth.BluetoothDevice; - -import androidx.annotation.Nullable; -import androidx.core.util.Consumer; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.nearby.common.bluetooth.BluetoothException; - -import java.security.GeneralSecurityException; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -/** Abstract class for pairing or connecting via FastPair. */ -public abstract class FastPairConnection { - @Nullable protected OnPairedCallback mPairedCallback; - @Nullable protected OnGetBluetoothAddressCallback mOnGetBluetoothAddressCallback; - @Nullable protected PasskeyConfirmationHandler mPasskeyConfirmationHandler; - @Nullable protected FastPairSignalChecker mFastPairSignalChecker; - @Nullable protected Consumer mRescueFromError; - @Nullable protected Runnable mPrepareCreateBondCallback; - protected boolean mPasskeyIsGotten; - - /** Sets a callback to be invoked once the device is paired. */ - public void setOnPairedCallback(OnPairedCallback callback) { - this.mPairedCallback = callback; - } - - /** Sets a callback to be invoked while the target bluetooth address is decided. */ - public void setOnGetBluetoothAddressCallback(OnGetBluetoothAddressCallback callback) { - this.mOnGetBluetoothAddressCallback = callback; - } - - /** Sets a callback to be invoked while handling the passkey confirmation. */ - public void setPasskeyConfirmationHandler( - PasskeyConfirmationHandler passkeyConfirmationHandler) { - this.mPasskeyConfirmationHandler = passkeyConfirmationHandler; - } - - public void setFastPairSignalChecker(FastPairSignalChecker fastPairSignalChecker) { - this.mFastPairSignalChecker = fastPairSignalChecker; - } - - public void setRescueFromError(Consumer rescueFromError) { - this.mRescueFromError = rescueFromError; - } - - public void setPrepareCreateBondCallback(Runnable runnable) { - this.mPrepareCreateBondCallback = runnable; - } - - @VisibleForTesting - @Nullable - public Runnable getPrepareCreateBondCallback() { - return mPrepareCreateBondCallback; - } - - /** - * Sets the fast pair history for identifying whether or not the provider has paired with the - * primary account on other phones before. - */ - @WorkerThread - public abstract void setFastPairHistory(List fastPairHistoryItem); - - /** Sets the device name to the Provider. */ - public abstract void setProviderDeviceName(String deviceName); - - /** Gets the device name from the Provider. */ - @Nullable - public abstract String getProviderDeviceName(); - - /** - * Gets the existing account key of the Provider. - * - * @return the existing account key if the Provider has paired with the account, null otherwise - */ - @WorkerThread - @Nullable - public abstract byte[] getExistingAccountKey(); - - /** - * Pairs with Provider. Synchronous: Blocks until paired and connected. Throws on any error. - * - * @return the secret key for the user's account, if written - */ - @WorkerThread - @Nullable - public abstract SharedSecret pair() - throws BluetoothException, InterruptedException, TimeoutException, ExecutionException, - PairingException, ReflectionException; - - /** - * Pairs with Provider. Synchronous: Blocks until paired and connected. Throws on any error. - * - * @param key can be in two different formats. If it is 16 bytes long, then it is an AES account - * key. Otherwise, it's a public key generated by {@link EllipticCurveDiffieHellmanExchange}. - * See go/fast-pair-2-spec for how each of these keys are used. - * @return the secret key for the user's account, if written - */ - @WorkerThread - @Nullable - public abstract SharedSecret pair(@Nullable byte[] key) - throws BluetoothException, InterruptedException, TimeoutException, ExecutionException, - PairingException, GeneralSecurityException, ReflectionException; - - /** Unpairs with Provider. Synchronous: Blocks until unpaired. Throws on any error. */ - @WorkerThread - public abstract void unpair(BluetoothDevice device) - throws InterruptedException, TimeoutException, ExecutionException, PairingException, - ReflectionException; - - /** Gets the public address of the Provider. */ - @Nullable - public abstract String getPublicAddress(); - - - /** Callback for getting notifications when pairing has completed. */ - public interface OnPairedCallback { - /** Called when the device at address has finished pairing. */ - void onPaired(String address); - } - - /** Callback for getting bluetooth address Bisto oobe need this information */ - public interface OnGetBluetoothAddressCallback { - /** Called when the device has received bluetooth address. */ - void onGetBluetoothAddress(String address); - } - - /** Holds the exchanged secret key and the public mac address of the device. */ - public static class SharedSecret { - private final byte[] mKey; - private final String mAddress; - private SharedSecret(byte[] key, String address) { - mKey = key; - mAddress = address; - } - - /** Creates Shared Secret. */ - public static SharedSecret create(byte[] key, String address) { - return new SharedSecret(key, address); - } - - /** Gets Shared Secret Key. */ - public byte[] getKey() { - return mKey; - } - - /** Gets Shared Secret Address. */ - public String getAddress() { - return mAddress; - } - - @Override - public String toString() { - return "SharedSecret{" - + "key=" + Arrays.toString(mKey) + ", " - + "address=" + mAddress - + "}"; - } - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (o instanceof SharedSecret) { - SharedSecret that = (SharedSecret) o; - return Arrays.equals(this.mKey, that.getKey()) - && this.mAddress.equals(that.getAddress()); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(Arrays.hashCode(mKey), mAddress); - } - } - - /** Invokes if gotten the passkey. */ - public void setPasskeyIsGotten() { - mPasskeyIsGotten = true; - } - - /** Returns the value of passkeyIsGotten. */ - public boolean getPasskeyIsGotten() { - return mPasskeyIsGotten; - } - - /** Interface to get latest address of ModelId. */ - public interface FastPairSignalChecker { - /** Gets address of ModelId. */ - String getValidAddressForModelId(String currentDevice); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairConstants.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairConstants.java deleted file mode 100644 index 0ff1bf2fc4780ff379ace95d20c1100f6b066711..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairConstants.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.bluetooth.BluetoothDevice; - -/** Constants to share with other team. */ -public class FastPairConstants { - private static final String PACKAGE_NAME = "com.android.server.nearby"; - private static final String PREFIX = PACKAGE_NAME + ".common.bluetooth.fastpair."; - - /** MODEL_ID item name for extended intent field. */ - public static final String EXTRA_MODEL_ID = PREFIX + "MODEL_ID"; - /** CONNECTION_ID item name for extended intent field. */ - public static final String EXTRA_CONNECTION_ID = PREFIX + "CONNECTION_ID"; - /** BLUETOOTH_MAC_ADDRESS item name for extended intent field. */ - public static final String EXTRA_BLUETOOTH_MAC_ADDRESS = PREFIX + "BLUETOOTH_MAC_ADDRESS"; - /** COMPANION_SCAN_ITEM item name for extended intent field. */ - public static final String EXTRA_SCAN_ITEM = PREFIX + "COMPANION_SCAN_ITEM"; - /** BOND_RESULT item name for extended intent field. */ - public static final String EXTRA_BOND_RESULT = PREFIX + "EXTRA_BOND_RESULT"; - - /** - * The bond result of the {@link BluetoothDevice} when FastPair launches the companion app, it - * means device is BONDED but the pairing process is not triggered by FastPair. - */ - public static final int BOND_RESULT_SUCCESS_WITHOUT_FP = 0; - - /** - * The bond result of the {@link BluetoothDevice} when FastPair launches the companion app, it - * means device is BONDED and the pairing process is triggered by FastPair. - */ - public static final int BOND_RESULT_SUCCESS_WITH_FP = 1; - - /** - * The bond result of the {@link BluetoothDevice} when FastPair launches the companion app, it - * means the pairing process triggered by FastPair is failed due to the lack of PIN code. - */ - public static final int BOND_RESULT_FAIL_WITH_FP_WITHOUT_PIN = 2; - - /** - * The bond result of the {@link BluetoothDevice} when FastPair launches the companion app, it - * means the pairing process triggered by FastPair is failed due to the PIN code is not - * confirmed by the user. - */ - public static final int BOND_RESULT_FAIL_WITH_FP_WITH_PIN_NOT_CONFIRMED = 3; - - /** - * The bond result of the {@link BluetoothDevice} when FastPair launches the companion app, it - * means the pairing process triggered by FastPair is failed due to the user thinks the PIN is - * wrong. - */ - public static final int BOND_RESULT_FAIL_WITH_FP_WITH_PIN_WRONG = 4; - - /** - * The bond result of the {@link BluetoothDevice} when FastPair launches the companion app, it - * means the pairing process triggered by FastPair is failed even after the user confirmed the - * PIN code is correct. - */ - public static final int BOND_RESULT_FAIL_WITH_FP_WITH_PIN_CORRECT = 5; - - private FastPairConstants() {} -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java deleted file mode 100644 index 789ef5989fa02d5770779d8357dd7a59f4c3d181..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java +++ /dev/null @@ -1,2127 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static android.bluetooth.BluetoothDevice.BOND_BONDED; -import static android.bluetooth.BluetoothDevice.BOND_BONDING; -import static android.bluetooth.BluetoothDevice.BOND_NONE; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.get16BitUuid; -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.to128BitUuid; -import static com.android.server.nearby.common.bluetooth.fastpair.Bytes.toBytes; -import static com.android.server.nearby.common.bluetooth.fastpair.Bytes.toShorts; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Verify.verifyNotNull; -import static com.google.common.io.BaseEncoding.base16; -import static com.google.common.primitives.Bytes.concat; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattCharacteristic; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.ParcelUuid; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.GuardedBy; -import androidx.annotation.IntDef; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.BluetoothGattException; -import com.android.server.nearby.common.bluetooth.BluetoothTimeoutException; -import com.android.server.nearby.common.bluetooth.fastpair.BluetoothAudioPairer.KeyBasedPairingInfo; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AccountKeyCharacteristic; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AdditionalDataCharacteristic.AdditionalDataType; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.FirmwareVersionCharacteristic; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.NameCharacteristic; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.TransportDiscoveryService; -import com.android.server.nearby.common.bluetooth.fastpair.HandshakeHandler.ActionOverBle; -import com.android.server.nearby.common.bluetooth.fastpair.HandshakeHandler.HandshakeException; -import com.android.server.nearby.common.bluetooth.fastpair.HandshakeHandler.HandshakeMessage; -import com.android.server.nearby.common.bluetooth.fastpair.HandshakeHandler.KeyBasedPairingRequest; -import com.android.server.nearby.common.bluetooth.fastpair.Ltv.ParseException; -import com.android.server.nearby.common.bluetooth.fastpair.TimingLogger.ScopedTiming; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection.ChangeObserver; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.BluetoothOperationTimeoutException; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.fastpair.FastPairController; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.BrEdrHandoverErrorCode; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.ConnectErrorCode; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.CreateBondErrorCode; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.ErrorCode; -import com.android.server.nearby.intdefs.NearbyEventIntDefs.EventCode; - -import com.google.common.base.Ascii; -import com.google.common.base.Preconditions; -import com.google.common.primitives.Shorts; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.ByteOrder; -import java.security.GeneralSecurityException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Supports Fast Pair pairing with certain Bluetooth headphones, Auto, etc. - * - *

    Based on https://developers.google.com/nearby/fast-pair/spec, the pairing is constructed by - * both BLE and BREDR connections. Example state transitions for Fast Pair 2, ie a pairing key is - * included in the request (note: timeouts and retries are governed by flags, may change): - * - *

    - * {@code
    - *   Connect GATT
    - *     A) Success -> Handshake
    - *     B) Failure (3s timeout) -> Retry 2x -> end
    - *
    - *   Handshake
    - *     A) Generate a shared secret with the headset (either using anti-spoofing key or account key)
    - *       1) Account key is used directly as the key
    - *       2) Anti-spoofing key is used by combining out private key with the headset's public and
    - *          sending our public to the headset to combine with their private to generate a shared
    - *          key. Sending our public key to headset takes ~3s.
    - *     B) Write an encrypted packet to the headset containing their BLE address for verification
    - *        that both sides have the same key (headset decodes this packet and checks it against their
    - *        own address) (~250ms).
    - *     C) Receive a response from the headset containing their public address (~250ms).
    - *
    - *   Discovery (for devices < Oreo)
    - *     A) Success -> Create Bond
    - *     B) Failure (10s timeout) -> Sleep 1s, Retry 3x -> end
    - *
    - *   Connect to device
    - *     A) If already bonded
    - *       1) Attempt directly connecting to supported profiles (A2DP, etc)
    - *         a) Success -> Write Account Key
    - *         b) Failure (15s timeout, usually fails within a ~2s) -> Remove bond (~1s) -> Create bond
    - *     B) If not already bonded
    - *       1) Create bond
    - *         a) Success -> Connect profile
    - *         b) Failure (15s timeout) -> Retry 2x -> end
    - *       2) Connect profile
    - *         a) Success -> Write account key
    - *         b) Failure -> Retry -> end
    - *
    - *   Write account key
    - *     A) Callback that pairing succeeded
    - *     B) Disconnect GATT
    - *     C) Reconnect GATT for secure connection
    - *     D) Write account key (~3s)
    - * }
    - * 
    - * - * The performance profiling result by {@link TimingLogger}: - * - *
    - *   FastPairDualConnection [Exclusive time] / [Total time] ([Timestamp])
    - *     Connect GATT #1 3054ms (0)
    - *     Handshake 32ms / 740ms (3054)
    - *       Generate key via ECDH 10ms (3054)
    - *       Add salt 1ms (3067)
    - *       Encrypt request 3ms (3068)
    - *       Write data to GATT 692ms (3097)
    - *       Wait response from GATT 0ms (3789)
    - *       Decrypt response 2ms (3789)
    - *     Get BR/EDR handover information via SDP 1ms (3795)
    - *     Pair device #1 6ms / 4887ms (3805)
    - *       Create bond 3965ms / 4881ms (3809)
    - *         Exchange passkey 587ms / 915ms (7124)
    - *           Encrypt passkey 6ms (7694)
    - *           Send passkey to remote 290ms (7700)
    - *           Wait for remote passkey 0ms (7993)
    - *           Decrypt passkey 18ms (7994)
    - *           Confirm the pairing: true 14ms (8025)
    - *         Close BondedReceiver 1ms (8688)
    - *     Connect: A2DP 19ms / 370ms (8701)
    - *       Wait connection 348ms / 349ms (8720)
    - *         Close ConnectedReceiver 1ms (9068)
    - *       Close profile: A2DP 2ms (9069)
    - *     Write account key 2ms / 789ms (9163)
    - *       Encrypt key 0ms (9164)
    - *       Write key via GATT #1 777ms / 783ms (9164)
    - *         Close GATT 6ms (9941)
    - *       Start CloudSyncing 2ms (9947)
    - *       Broadcast Validator 2ms (9949)
    - *   FastPairDualConnection end, 9952ms
    - * 
    - */ -// TODO(b/203441105): break down FastPairDualConnection into smaller classes. -public class FastPairDualConnection extends FastPairConnection { - - private static final String TAG = FastPairDualConnection.class.getSimpleName(); - - @VisibleForTesting - static final int GATT_ERROR_CODE_FAST_PAIR_SIGNAL_LOST = 10000; - @VisibleForTesting - static final int GATT_ERROR_CODE_FAST_PAIR_ADDRESS_ROTATED = 20000; - @VisibleForTesting - static final int GATT_ERROR_CODE_USER_RETRY = 30000; - @VisibleForTesting - static final int GATT_ERROR_CODE_PAIR_WITH_SAME_MODEL_ID_COUNT = 40000; - @VisibleForTesting - static final int GATT_ERROR_CODE_TIMEOUT = 1000; - - @Nullable - private static String sInitialConnectionFirmwareVersion; - private static final byte[] REQUESTED_SERVICES_LTV = - new Ltv( - TransportDiscoveryService.SERVICE_UUIDS_16_BIT_LIST_TYPE, - toBytes( - ByteOrder.LITTLE_ENDIAN, - Constants.A2DP_SINK_SERVICE_UUID, - Constants.HANDS_FREE_SERVICE_UUID, - Constants.HEADSET_SERVICE_UUID)) - .getBytes(); - private static final byte[] TDS_CONTROL_POINT_REQUEST = - concat( - new byte[]{ - TransportDiscoveryService.ControlPointCharacteristic - .ACTIVATE_TRANSPORT_OP_CODE, - TransportDiscoveryService.BLUETOOTH_SIG_ORGANIZATION_ID - }, - REQUESTED_SERVICES_LTV); - - private static boolean sTestMode = false; - - static void enableTestMode() { - sTestMode = true; - } - - /** - * Operation Result Code. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - ResultCode.UNKNOWN, - ResultCode.SUCCESS, - ResultCode.OP_CODE_NOT_SUPPORTED, - ResultCode.INVALID_PARAMETER, - ResultCode.UNSUPPORTED_ORGANIZATION_ID, - ResultCode.OPERATION_FAILED, - }) - - public @interface ResultCode { - - int UNKNOWN = (byte) 0xFF; - int SUCCESS = (byte) 0x00; - int OP_CODE_NOT_SUPPORTED = (byte) 0x01; - int INVALID_PARAMETER = (byte) 0x02; - int UNSUPPORTED_ORGANIZATION_ID = (byte) 0x03; - int OPERATION_FAILED = (byte) 0x04; - } - - - private static @ResultCode int fromTdsControlPointIndication(byte[] response) { - return response == null || response.length < 2 ? ResultCode.UNKNOWN : from(response[1]); - } - - private static @ResultCode int from(byte byteValue) { - switch (byteValue) { - case ResultCode.UNKNOWN: - case ResultCode.SUCCESS: - case ResultCode.OP_CODE_NOT_SUPPORTED: - case ResultCode.INVALID_PARAMETER: - case ResultCode.UNSUPPORTED_ORGANIZATION_ID: - case ResultCode.OPERATION_FAILED: - return byteValue; - default: - return ResultCode.UNKNOWN; - } - } - - private static class BrEdrHandoverInformation { - - private final byte[] mBluetoothAddress; - private final short[] mProfiles; - - private BrEdrHandoverInformation(byte[] bluetoothAddress, short[] profiles) { - this.mBluetoothAddress = bluetoothAddress; - - // For now, since we only connect to one profile, prefer A2DP Sink over headset/HFP. - // TODO(b/37167120): Connect to more than one profile. - Set profileSet = new HashSet<>(Shorts.asList(profiles)); - if (profileSet.contains(Constants.A2DP_SINK_SERVICE_UUID)) { - profileSet.remove(Constants.HEADSET_SERVICE_UUID); - profileSet.remove(Constants.HANDS_FREE_SERVICE_UUID); - } - this.mProfiles = Shorts.toArray(profileSet); - } - - @Override - public String toString() { - return "BrEdrHandoverInformation{" - + maskBluetoothAddress(BluetoothAddress.encode(mBluetoothAddress)) - + ", profiles=" - + (mProfiles.length > 0 ? Shorts.join(",", mProfiles) : "(none)") - + "}"; - } - } - - private final Context mContext; - private final Preferences mPreferences; - private final EventLoggerWrapper mEventLogger; - private final BluetoothAdapter mBluetoothAdapter = - checkNotNull(BluetoothAdapter.getDefaultAdapter()); - private String mBleAddress; - - private final TimingLogger mTimingLogger; - private GattConnectionManager mGattConnectionManager; - private boolean mProviderInitiatesBonding; - private @Nullable - byte[] mPairingSecret; - private @Nullable - byte[] mPairingKey; - @Nullable - private String mPublicAddress; - @VisibleForTesting - @Nullable - FastPairHistoryFinder mPairedHistoryFinder; - @Nullable - private String mProviderDeviceName = null; - private boolean mNeedUpdateProviderName = false; - @Nullable - DeviceNameReceiver mDeviceNameReceiver; - @Nullable - private HandshakeHandler mHandshakeHandlerForTest; - @Nullable - private Runnable mBeforeDirectlyConnectProfileFromCacheForTest; - - public FastPairDualConnection( - Context context, - String bleAddress, - Preferences preferences, - @Nullable EventLogger eventLogger) { - this(context, bleAddress, preferences, eventLogger, - new TimingLogger("FastPairDualConnection", preferences)); - } - - @VisibleForTesting - FastPairDualConnection( - Context context, - String bleAddress, - Preferences preferences, - @Nullable EventLogger eventLogger, - TimingLogger timingLogger) { - this.mContext = context; - this.mPreferences = preferences; - this.mEventLogger = new EventLoggerWrapper(eventLogger); - this.mBleAddress = bleAddress; - this.mTimingLogger = timingLogger; - } - - /** - * Unpairs with headphones. Synchronous: Blocks until unpaired. Throws on any error. - */ - @WorkerThread - public void unpair(BluetoothDevice device) - throws ReflectionException, InterruptedException, ExecutionException, TimeoutException, - PairingException { - if (mPreferences.getExtraLoggingInformation() != null) { - mEventLogger - .bind(mContext, device.getAddress(), mPreferences.getExtraLoggingInformation()); - } - new BluetoothAudioPairer( - mContext, - device, - mPreferences, - mEventLogger, - /* keyBasedPairingInfo= */ null, - /* passkeyConfirmationHandler= */ null, - mTimingLogger) - .unpair(); - if (mEventLogger.isBound()) { - mEventLogger.unbind(mContext); - } - } - - /** - * Sets the fast pair history for identifying the provider which has paired (without being - * forgotten) with the primary account on the device, i.e. the history is not limited on this - * phone, can be on other phones with the same account. If they have already paired, Fast Pair - * should not generate new account key and default personalized name for it after initial pair. - */ - @WorkerThread - public void setFastPairHistory(List fastPairHistoryItem) { - Log.i(TAG, "Paired history has been set."); - this.mPairedHistoryFinder = new FastPairHistoryFinder(fastPairHistoryItem); - } - - /** - * Update the provider device name when we take provider default name and account based name - * into consideration. - */ - public void setProviderDeviceName(String deviceName) { - Log.i(TAG, "Update provider device name = " + deviceName); - mProviderDeviceName = deviceName; - mNeedUpdateProviderName = true; - } - - /** - * Gets the device name from the Provider (via GATT notify). - */ - @Nullable - public String getProviderDeviceName() { - if (mDeviceNameReceiver == null) { - Log.i(TAG, "getProviderDeviceName failed, deviceNameReceiver == null."); - return null; - } - if (mPairingSecret == null) { - Log.i(TAG, "getProviderDeviceName failed, pairingSecret == null."); - return null; - } - String deviceName = mDeviceNameReceiver.getParsedResult(mPairingSecret); - Log.i(TAG, "getProviderDeviceName = " + deviceName); - - return deviceName; - } - - /** - * Get the existing account key of the provider, this API can be called after handshake. - * - * @return the existing account key if the provider has paired with the account before. - * Otherwise, return null, i.e. it is a real initial pairing. - */ - @WorkerThread - @Nullable - public byte[] getExistingAccountKey() { - return mPairedHistoryFinder == null ? null : mPairedHistoryFinder.getExistingAccountKey(); - } - - /** - * Pairs with headphones. Synchronous: Blocks until paired and connected. Throws on any error. - * - * @return the secret key for the user's account, if written. - */ - @WorkerThread - @Nullable - public SharedSecret pair() - throws BluetoothException, InterruptedException, ReflectionException, TimeoutException, - ExecutionException, PairingException { - try { - return pair(/*key=*/ null); - } catch (GeneralSecurityException e) { - throw new RuntimeException("Should never happen, no security key!", e); - } - } - - /** - * Pairs with headphones. Synchronous: Blocks until paired and connected. Throws on any error. - * - * @param key can be in two different formats. If it is 16 bytes long, then it is an AES account - * key. Otherwise, it's a public key generated by {@link EllipticCurveDiffieHellmanExchange}. - * See go/fast-pair-2-spec for how each of these keys are used. - * @return the secret key for the user's account, if written - */ - @WorkerThread - @Nullable - public SharedSecret pair(@Nullable byte[] key) - throws BluetoothException, InterruptedException, ReflectionException, TimeoutException, - ExecutionException, PairingException, GeneralSecurityException { - mPairingKey = key; - if (key != null) { - Log.i(TAG, "Starting to pair " + maskBluetoothAddress(mBleAddress) + ": key[" - + key.length + "], " + mPreferences); - } else { - Log.i(TAG, "Pairing " + maskBluetoothAddress(mBleAddress) + ": " + mPreferences); - } - if (mPreferences.getExtraLoggingInformation() != null) { - this.mEventLogger.bind( - mContext, mBleAddress, mPreferences.getExtraLoggingInformation()); - } - // Provider never initiates if key is null (Fast Pair 1.0). - if (key != null && mPreferences.getProviderInitiatesBondingIfSupported()) { - // Provider can't initiate if we can't get our own public address, so check. - this.mEventLogger.setCurrentEvent(EventCode.GET_LOCAL_PUBLIC_ADDRESS); - if (BluetoothAddress.getPublicAddress(mContext) != null) { - this.mEventLogger.logCurrentEventSucceeded(); - mProviderInitiatesBonding = true; - } else { - this.mEventLogger - .logCurrentEventFailed(new IllegalStateException("null bluetooth_address")); - Log.e(TAG, - "Want provider to initiate bonding, but cannot access Bluetooth public " - + "address. Falling back to initiating bonding ourselves."); - } - } - - // User might be pairing with a bonded device. In this case, we just connect profile - // directly and finish pairing. - if (directConnectProfileWithCachedAddress()) { - callbackOnPaired(); - mTimingLogger.dump(); - if (mEventLogger.isBound()) { - mEventLogger.unbind(mContext); - } - return null; - } - - // Lazily initialize a new connection manager for each pairing request. - initGattConnectionManager(); - boolean isSecretHandshakeCompleted = true; - - try { - if (key != null && key.length > 0) { - // GATT_CONNECTION_AND_SECRET_HANDSHAKE start. - mEventLogger.setCurrentEvent(EventCode.GATT_CONNECTION_AND_SECRET_HANDSHAKE); - isSecretHandshakeCompleted = false; - Exception lastException = null; - boolean lastExceptionFromHandshake = false; - long startTime = SystemClock.elapsedRealtime(); - // We communicate over this connection twice for Key-based Pairing: once before - // bonding begins, and once during (to transfer the passkey). Empirically, keeping - // it alive throughout is far more reliable than disconnecting and reconnecting for - // each step. The while loop is for retry of GATT connection and handshake only. - do { - boolean isHandshaking = false; - try (BluetoothGattConnection connection = - mGattConnectionManager - .getConnectionWithSignalLostCheck(mRescueFromError)) { - mEventLogger.setCurrentEvent(EventCode.SECRET_HANDSHAKE); - if (lastException != null && !lastExceptionFromHandshake) { - logRetrySuccessEvent(EventCode.RECOVER_BY_RETRY_GATT, lastException, - mEventLogger); - lastException = null; - } - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Handshake")) { - isHandshaking = true; - handshakeForKeyBasedPairing(key); - // After handshake, Fast Pair has the public address of the provider, so - // we can check if it has paired with the account. - if (mPublicAddress != null && mPairedHistoryFinder != null) { - if (mPairedHistoryFinder.isInPairedHistory(mPublicAddress)) { - Log.i(TAG, "The provider is found in paired history."); - } else { - Log.i(TAG, "The provider is not found in paired history."); - } - } - } - isHandshaking = false; - // SECRET_HANDSHAKE end. - mEventLogger.logCurrentEventSucceeded(); - isSecretHandshakeCompleted = true; - if (mPrepareCreateBondCallback != null) { - mPrepareCreateBondCallback.run(); - } - if (lastException != null && lastExceptionFromHandshake) { - logRetrySuccessEvent(EventCode.RECOVER_BY_RETRY_HANDSHAKE_RECONNECT, - lastException, mEventLogger); - } - logManualRetryCounts(/* success= */ true); - // GATT_CONNECTION_AND_SECRET_HANDSHAKE end. - mEventLogger.logCurrentEventSucceeded(); - return pair(mPreferences.getEnableBrEdrHandover()); - } catch (SignalLostException e) { - long spentTime = SystemClock.elapsedRealtime() - startTime; - if (spentTime > mPreferences.getAddressRotateRetryMaxSpentTimeMs()) { - Log.w(TAG, "Signal lost but already spend too much time " + spentTime - + "ms"); - throw e; - } - - logCurrentEventFailedBySignalLost(e); - lastException = (Exception) e.getCause(); - lastExceptionFromHandshake = isHandshaking; - if (mRescueFromError != null && isHandshaking) { - mRescueFromError.accept(ErrorCode.SUCCESS_SECRET_HANDSHAKE_RECONNECT); - } - Log.i(TAG, "Signal lost, retry"); - // In case we meet some GATT error which is not recoverable and fail very - // quick. - SystemClock.sleep(mPreferences.getPairingRetryDelayMs()); - } catch (SignalRotatedException e) { - long spentTime = SystemClock.elapsedRealtime() - startTime; - if (spentTime > mPreferences.getAddressRotateRetryMaxSpentTimeMs()) { - Log.w(TAG, "Address rotated but already spend too much time " - + spentTime + "ms"); - throw e; - } - - logCurrentEventFailedBySignalRotated(e); - setBleAddress(e.getNewAddress()); - lastException = (Exception) e.getCause(); - lastExceptionFromHandshake = isHandshaking; - if (mRescueFromError != null) { - mRescueFromError.accept(ErrorCode.SUCCESS_ADDRESS_ROTATE); - } - Log.i(TAG, "Address rotated, retry"); - } catch (HandshakeException e) { - long spentTime = SystemClock.elapsedRealtime() - startTime; - if (spentTime > mPreferences - .getSecretHandshakeRetryGattConnectionMaxSpentTimeMs()) { - Log.w(TAG, "Secret handshake failed but already spend too much time " - + spentTime + "ms"); - throw e.getOriginalException(); - } - if (mEventLogger.isCurrentEvent()) { - mEventLogger.logCurrentEventFailed(e.getOriginalException()); - } - initGattConnectionManager(); - lastException = e.getOriginalException(); - lastExceptionFromHandshake = true; - if (mRescueFromError != null) { - mRescueFromError.accept(ErrorCode.SUCCESS_SECRET_HANDSHAKE_RECONNECT); - } - Log.i(TAG, "Handshake failed, retry GATT connection"); - } - } while (mPreferences.getRetryGattConnectionAndSecretHandshake()); - } - if (mPrepareCreateBondCallback != null) { - mPrepareCreateBondCallback.run(); - } - return pair(mPreferences.getEnableBrEdrHandover()); - } catch (SignalLostException e) { - logCurrentEventFailedBySignalLost(e); - // GATT_CONNECTION_AND_SECRET_HANDSHAKE end. - if (!isSecretHandshakeCompleted) { - logManualRetryCounts(/* success= */ false); - logCurrentEventFailedBySignalLost(e); - } - throw e; - } catch (SignalRotatedException e) { - logCurrentEventFailedBySignalRotated(e); - // GATT_CONNECTION_AND_SECRET_HANDSHAKE end. - if (!isSecretHandshakeCompleted) { - logManualRetryCounts(/* success= */ false); - logCurrentEventFailedBySignalRotated(e); - } - throw e; - } catch (BluetoothException - | InterruptedException - | ReflectionException - | TimeoutException - | ExecutionException - | PairingException - | GeneralSecurityException e) { - if (mEventLogger.isCurrentEvent()) { - mEventLogger.logCurrentEventFailed(e); - } - // GATT_CONNECTION_AND_SECRET_HANDSHAKE end. - if (!isSecretHandshakeCompleted) { - logManualRetryCounts(/* success= */ false); - if (mEventLogger.isCurrentEvent()) { - mEventLogger.logCurrentEventFailed(e); - } - } - throw e; - } finally { - mTimingLogger.dump(); - if (mEventLogger.isBound()) { - mEventLogger.unbind(mContext); - } - } - } - - private boolean directConnectProfileWithCachedAddress() throws ReflectionException { - if (TextUtils.isEmpty(mPreferences.getCachedDeviceAddress()) - || !mPreferences.getDirectConnectProfileIfModelIdInCache() - || mPreferences.getSkipConnectingProfiles()) { - return false; - } - Log.i(TAG, "Try to direct connect profile with cached address " - + maskBluetoothAddress(mPreferences.getCachedDeviceAddress())); - mEventLogger.setCurrentEvent(EventCode.DIRECTLY_CONNECT_PROFILE_WITH_CACHED_ADDRESS); - BluetoothDevice device = - mBluetoothAdapter.getRemoteDevice(mPreferences.getCachedDeviceAddress()).unwrap(); - AtomicBoolean interruptConnection = new AtomicBoolean(false); - BroadcastReceiver receiver = - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent == null - || !BluetoothDevice.ACTION_PAIRING_REQUEST - .equals(intent.getAction())) { - return; - } - BluetoothDevice pairingDevice = intent - .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (pairingDevice == null || !device.getAddress() - .equals(pairingDevice.getAddress())) { - return; - } - abortBroadcast(); - // Should be the clear link key case, make it fail directly to go back to - // initial pairing process. - pairingDevice.setPairingConfirmation(/* confirm= */ false); - Log.w(TAG, "Get pairing request broadcast for device " - + maskBluetoothAddress(device.getAddress()) - + " while try to direct connect profile with cached address, reject" - + " and to go back to initial pairing process"); - interruptConnection.set(true); - } - }; - mContext.registerReceiver(receiver, - new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)); - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, - "Connect to profile with cached address directly")) { - if (mBeforeDirectlyConnectProfileFromCacheForTest != null) { - mBeforeDirectlyConnectProfileFromCacheForTest.run(); - } - attemptConnectProfiles( - new BluetoothAudioPairer( - mContext, - device, - mPreferences, - mEventLogger, - /* keyBasedPairingInfo= */ null, - /* passkeyConfirmationHandler= */ null, - mTimingLogger), - maskBluetoothAddress(device), - getSupportedProfiles(device), - /* numConnectionAttempts= */ 1, - /* enablePairingBehavior= */ false, - interruptConnection); - Log.i(TAG, - "Directly connected to " + maskBluetoothAddress(device) - + "with cached address."); - mEventLogger.logCurrentEventSucceeded(); - mEventLogger.setDevice(device); - logPairWithPossibleCachedAddress(device.getAddress()); - return true; - } catch (PairingException e) { - if (interruptConnection.get()) { - Log.w(TAG, "Fail to connected to " + maskBluetoothAddress(device) - + " with cached address due to link key is cleared.", e); - mEventLogger.logCurrentEventFailed( - new ConnectException(ConnectErrorCode.LINK_KEY_CLEARED, - "Link key is cleared")); - } else { - Log.w(TAG, "Fail to connected to " + maskBluetoothAddress(device) - + " with cached address.", e); - mEventLogger.logCurrentEventFailed(e); - } - return false; - } finally { - mContext.unregisterReceiver(receiver); - } - } - - /** - * Logs for user retry, check go/fastpairquality21q3 for more details. - */ - private void logManualRetryCounts(boolean success) { - if (!mPreferences.getLogUserManualRetry()) { - return; - } - - // We don't want to be the final event on analytics. - if (!mEventLogger.isCurrentEvent()) { - return; - } - - mEventLogger.setCurrentEvent(EventCode.GATT_HANDSHAKE_MANUAL_RETRY_ATTEMPTS); - if (mPreferences.getPairFailureCounts() <= 0 && success) { - mEventLogger.logCurrentEventSucceeded(); - } else { - int errorCode = mPreferences.getPairFailureCounts(); - if (errorCode > 99) { - errorCode = 99; - } - errorCode += success ? 0 : 100; - // To not conflict with current error codes. - errorCode += GATT_ERROR_CODE_USER_RETRY; - mEventLogger.logCurrentEventFailed( - new BluetoothGattException("Error for manual retry", errorCode)); - } - } - - static void logRetrySuccessEvent( - @EventCode int eventCode, - @Nullable Exception recoverFromException, - EventLoggerWrapper eventLogger) { - if (recoverFromException == null) { - return; - } - eventLogger.setCurrentEvent(eventCode); - eventLogger.logCurrentEventFailed(recoverFromException); - } - - private void initGattConnectionManager() { - mGattConnectionManager = - new GattConnectionManager( - mContext, - mPreferences, - mEventLogger, - mBluetoothAdapter, - this::toggleBluetooth, - mBleAddress, - mTimingLogger, - mFastPairSignalChecker, - isPairingWithAntiSpoofingPublicKey()); - } - - private void logCurrentEventFailedBySignalRotated(SignalRotatedException e) { - if (!mEventLogger.isCurrentEvent()) { - return; - } - - Log.w(TAG, "BLE Address for pairing device might rotated!"); - mEventLogger.logCurrentEventFailed( - new BluetoothGattException( - "BLE Address for pairing device might rotated", - appendMoreErrorCode(GATT_ERROR_CODE_FAST_PAIR_ADDRESS_ROTATED, - e.getCause()), - e)); - } - - private void logCurrentEventFailedBySignalLost(SignalLostException e) { - if (!mEventLogger.isCurrentEvent()) { - return; - } - - Log.w(TAG, "BLE signal for pairing device might lost!"); - mEventLogger.logCurrentEventFailed( - new BluetoothGattException( - "BLE signal for pairing device might lost", - appendMoreErrorCode(GATT_ERROR_CODE_FAST_PAIR_SIGNAL_LOST, e.getCause()), - e)); - } - - @VisibleForTesting - static int appendMoreErrorCode(int masterErrorCode, @Nullable Throwable cause) { - if (cause instanceof BluetoothGattException) { - return masterErrorCode + ((BluetoothGattException) cause).getGattErrorCode(); - } else if (cause instanceof TimeoutException - || cause instanceof BluetoothTimeoutException - || cause instanceof BluetoothOperationTimeoutException) { - return masterErrorCode + GATT_ERROR_CODE_TIMEOUT; - } else { - return masterErrorCode; - } - } - - private void setBleAddress(String newAddress) { - if (TextUtils.isEmpty(newAddress) || Ascii.equalsIgnoreCase(newAddress, mBleAddress)) { - return; - } - - mBleAddress = newAddress; - - // Recreates a GattConnectionManager with the new address for establishing a new GATT - // connection later. - initGattConnectionManager(); - - mEventLogger.setDevice(mBluetoothAdapter.getRemoteDevice(mBleAddress).unwrap()); - } - - /** - * Gets the public address of the headset used in the connection. Before the handshake, this - * could be null. - */ - @Nullable - public String getPublicAddress() { - return mPublicAddress; - } - - /** - * Pairs with a Bluetooth device. In general, this process goes through the following steps: - * - *
      - *
    1. Get BrEdr handover information if requested - *
    2. Discover the device (on Android N and lower to work around a bug) - *
    3. Connect to the device - *
        - *
      • Attempt a direct connection to a supported profile if we're already bonded - *
      • Create a new bond with the not bonded device and then connect to a supported - * profile - *
      - *
    4. Write the account secret - *
    - * - *

    Blocks until paired. May take 10+ seconds, so run on a background thread. - */ - @Nullable - private SharedSecret pair(boolean enableBrEdrHandover) - throws BluetoothException, InterruptedException, ReflectionException, TimeoutException, - ExecutionException, PairingException, GeneralSecurityException { - BrEdrHandoverInformation brEdrHandoverInformation = null; - if (enableBrEdrHandover) { - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Get BR/EDR handover information via GATT")) { - brEdrHandoverInformation = - getBrEdrHandoverInformation(mGattConnectionManager.getConnection()); - } catch (BluetoothException | TdsException e) { - Log.w(TAG, - "Couldn't get BR/EDR Handover info via TDS. Trying direct connect.", e); - mEventLogger.logCurrentEventFailed(e); - } - } - - if (brEdrHandoverInformation == null) { - // Pair directly to the BLE address. Works if the BLE and Bluetooth Classic addresses - // are the same, or if we can do BLE cross-key transport. - brEdrHandoverInformation = - new BrEdrHandoverInformation( - BluetoothAddress - .decode(mPublicAddress != null ? mPublicAddress : mBleAddress), - attemptGetBluetoothClassicProfiles( - mBluetoothAdapter.getRemoteDevice(mBleAddress).unwrap(), - mPreferences.getNumSdpAttempts())); - } - - BluetoothDevice device = - mBluetoothAdapter.getRemoteDevice(brEdrHandoverInformation.mBluetoothAddress) - .unwrap(); - callbackOnGetAddress(device.getAddress()); - mEventLogger.setDevice(device); - - Log.i(TAG, "Pairing with " + brEdrHandoverInformation); - KeyBasedPairingInfo keyBasedPairingInfo = - mPairingSecret == null - ? null - : new KeyBasedPairingInfo( - mPairingSecret, mGattConnectionManager, mProviderInitiatesBonding); - - BluetoothAudioPairer pairer = - new BluetoothAudioPairer( - mContext, - device, - mPreferences, - mEventLogger, - keyBasedPairingInfo, - mPasskeyConfirmationHandler, - mTimingLogger); - - logPairWithPossibleCachedAddress(device.getAddress()); - logPairWithModelIdInCacheAndDiscoveryFailForCachedAddress(device); - - // In the case where we are already bonded, we should first just try connecting to supported - // profiles. If successful, then this will be much faster than recreating the bond like we - // normally do and we can finish early. It is also more reliable than tearing down the bond - // and recreating it. - try { - if (!sTestMode) { - attemptDirectConnectionIfBonded(device, pairer); - } - callbackOnPaired(); - return maybeWriteAccountKey(device); - } catch (PairingException e) { - Log.i(TAG, "Failed to directly connect to supported profiles: " + e.getMessage()); - // Catches exception when we fail to connect support profile. And makes the flow to go - // through step to write account key when device is bonded. - if (mPreferences.getEnablePairFlowShowUiWithoutProfileConnection() - && device.getBondState() == BluetoothDevice.BOND_BONDED) { - if (mPreferences.getSkipConnectingProfiles() - && !mPreferences.getCheckBondStateWhenSkipConnectingProfiles()) { - Log.i(TAG, "For notCheckBondStateWhenSkipConnectingProfiles case should do " - + "re-bond"); - } else { - Log.i(TAG, "Fail to connect profile when device is bonded, still call back on" - + "pair callback to show ui"); - callbackOnPaired(); - return maybeWriteAccountKey(device); - } - } - } - - if (mPreferences.getMoreEventLogForQuality()) { - switch (device.getBondState()) { - case BOND_BONDED: - mEventLogger.setCurrentEvent(EventCode.BEFORE_CREATE_BOND_BONDED); - break; - case BOND_BONDING: - mEventLogger.setCurrentEvent(EventCode.BEFORE_CREATE_BOND_BONDING); - break; - case BOND_NONE: - default: - mEventLogger.setCurrentEvent(EventCode.BEFORE_CREATE_BOND); - } - } - - for (int i = 1; i <= mPreferences.getNumCreateBondAttempts(); i++) { - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, "Pair device #" + i)) { - pairer.pair(); - if (mPreferences.getMoreEventLogForQuality()) { - // For EventCode.BEFORE_CREATE_BOND - mEventLogger.logCurrentEventSucceeded(); - } - break; - } catch (Exception e) { - mEventLogger.logCurrentEventFailed(e); - if (mPasskeyIsGotten) { - Log.w(TAG, - "createBond() failed because of " + e.getMessage() - + " after getting the passkey. Skip retry."); - if (mPreferences.getMoreEventLogForQuality()) { - // For EventCode.BEFORE_CREATE_BOND - mEventLogger.logCurrentEventFailed( - new CreateBondException( - CreateBondErrorCode.FAILED_BUT_ALREADY_RECEIVE_PASS_KEY, - 0, - "Already get the passkey")); - } - break; - } - Log.e(TAG, - "removeBond() or createBond() failed, attempt " + i + " of " + mPreferences - .getNumCreateBondAttempts() + ". Bond state " - + device.getBondState(), e); - if (i < mPreferences.getNumCreateBondAttempts()) { - toggleBluetooth(); - - // We've seen 3 createBond() failures within 100ms (!). And then success again - // later (even without turning on/off bluetooth). So create some minimum break - // time. - Log.i(TAG, "Sleeping 1 sec after createBond() failure."); - SystemClock.sleep(1000); - } else if (mPreferences.getMoreEventLogForQuality()) { - // For EventCode.BEFORE_CREATE_BOND - mEventLogger.logCurrentEventFailed(e); - } - } - } - boolean deviceCreateBondFailWithNullSecret = false; - if (!pairer.isPaired()) { - if (mPairingSecret != null) { - // Bonding could fail for a few different reasons here. It could be an error, an - // attacker may have tried to bond, or the device may not be up to spec. - throw new PairingException("createBond() failed, exiting connection process."); - } else if (mPreferences.getSkipConnectingProfiles()) { - throw new PairingException( - "createBond() failed and skipping connecting to a profile."); - } else { - // When bond creation has failed, connecting a profile will still work most of the - // time for Fast Pair 1.0 devices (ie, pairing secret is null), so continue on with - // the spec anyways and attempt to connect supported profiles. - Log.w(TAG, "createBond() failed, will try connecting profiles anyway."); - deviceCreateBondFailWithNullSecret = true; - } - } else if (mPreferences.getEnablePairFlowShowUiWithoutProfileConnection()) { - Log.i(TAG, "new flow to call on paired callback for ui when pairing step is finished"); - callbackOnPaired(); - } - - if (!mPreferences.getSkipConnectingProfiles()) { - if (mPreferences.getWaitForUuidsAfterBonding() - && brEdrHandoverInformation.mProfiles.length == 0) { - short[] supportedProfiles = getCachedUuids(device); - if (supportedProfiles.length == 0 - && mPreferences.getNumSdpAttemptsAfterBonded() > 0) { - Log.i(TAG, "Found no supported profiles in UUID cache, manually trigger SDP."); - attemptGetBluetoothClassicProfiles(device, - mPreferences.getNumSdpAttemptsAfterBonded()); - } - brEdrHandoverInformation = - new BrEdrHandoverInformation( - brEdrHandoverInformation.mBluetoothAddress, supportedProfiles); - } - short[] profiles = brEdrHandoverInformation.mProfiles; - if (profiles.length == 0) { - profiles = Constants.getSupportedProfiles(); - Log.w(TAG, - "Attempting to connect constants profiles, " + Arrays.toString(profiles)); - } else { - Log.i(TAG, "Attempting to connect device profiles, " + Arrays.toString(profiles)); - } - - try { - attemptConnectProfiles( - pairer, - maskBluetoothAddress(device), - profiles, - mPreferences.getNumConnectAttempts(), - /* enablePairingBehavior= */ false); - } catch (PairingException e) { - // For new pair flow to show ui, we already show success ui when finishing the - // createBond step. So we should catch the exception from connecting profile to - // avoid showing fail ui for user. - if (mPreferences.getEnablePairFlowShowUiWithoutProfileConnection() - && !deviceCreateBondFailWithNullSecret) { - Log.i(TAG, "Fail to connect profile when device is bonded"); - } else { - throw e; - } - } - } - if (!mPreferences.getEnablePairFlowShowUiWithoutProfileConnection()) { - Log.i(TAG, "original flow to call on paired callback for ui"); - callbackOnPaired(); - } else if (deviceCreateBondFailWithNullSecret) { - // This paired callback is called for device which create bond fail with null secret - // such as FastPair 1.0 device when directly connecting to any supported profile. - Log.i(TAG, "call on paired callback for ui for device with null secret without bonded " - + "state"); - callbackOnPaired(); - } - if (mPreferences.getEnableFirmwareVersionCharacteristic() - && validateBluetoothGattCharacteristic( - mGattConnectionManager.getConnection(), FirmwareVersionCharacteristic.ID)) { - try { - sInitialConnectionFirmwareVersion = readFirmwareVersion(); - } catch (BluetoothException e) { - Log.i(TAG, "Fast Pair: head phone does not support firmware read", e); - } - } - - // Catch exception when writing account key or name fail to avoid showing pairing failure - // notice for user. Because device is already paired successfully based on paring step. - SharedSecret secret = null; - try { - secret = maybeWriteAccountKey(device); - } catch (InterruptedException - | ExecutionException - | TimeoutException - | NoSuchAlgorithmException - | BluetoothException e) { - Log.w(TAG, "Fast Pair: Got exception when writing account key or name to provider", e); - } - - return secret; - } - - private void logPairWithPossibleCachedAddress(String brEdrAddressForBonding) { - if (TextUtils.isEmpty(mPreferences.getPossibleCachedDeviceAddress()) - || !mPreferences.getLogPairWithCachedModelId()) { - return; - } - mEventLogger.setCurrentEvent(EventCode.PAIR_WITH_CACHED_MODEL_ID); - if (Ascii.equalsIgnoreCase( - mPreferences.getPossibleCachedDeviceAddress(), brEdrAddressForBonding)) { - mEventLogger.logCurrentEventSucceeded(); - Log.i(TAG, "Repair with possible cached device " - + maskBluetoothAddress(mPreferences.getPossibleCachedDeviceAddress())); - } else { - mEventLogger.logCurrentEventFailed( - new PairingException("Pairing with 2nd device with same model ID")); - Log.i(TAG, "Pair with a new device " + maskBluetoothAddress(brEdrAddressForBonding) - + " with model ID in cache " - + maskBluetoothAddress(mPreferences.getPossibleCachedDeviceAddress())); - } - } - - /** - * Logs two type of events. First, why cachedAddress mechanism doesn't work if it's repair with - * bonded device case. Second, if it's not the case, log how many devices with the same model Id - * is already paired. - */ - private void logPairWithModelIdInCacheAndDiscoveryFailForCachedAddress(BluetoothDevice device) { - if (!mPreferences.getLogPairWithCachedModelId()) { - return; - } - - if (device.getBondState() == BOND_BONDED) { - if (mPreferences.getSameModelIdPairedDeviceCount() <= 0) { - Log.i(TAG, "Device is bonded but we don't have this model Id in cache."); - } else if (TextUtils.isEmpty(mPreferences.getCachedDeviceAddress()) - && mPreferences.getDirectConnectProfileIfModelIdInCache() - && !mPreferences.getSkipConnectingProfiles()) { - // Pair with bonded device case. Log why the cached address is not found. - mEventLogger.setCurrentEvent( - EventCode.DIRECTLY_CONNECT_PROFILE_WITH_CACHED_ADDRESS); - mEventLogger.logCurrentEventFailed( - mPreferences.getIsDeviceFinishCheckAddressFromCache() - ? new ConnectException(ConnectErrorCode.FAIL_TO_DISCOVERY, - "Failed to discovery") - : new ConnectException( - ConnectErrorCode.DISCOVERY_NOT_FINISHED, - "Discovery not finished")); - Log.i(TAG, "Failed to get cached address due to " - + (mPreferences.getIsDeviceFinishCheckAddressFromCache() - ? "Failed to discovery" - : "Discovery not finished")); - } - } else if (device.getBondState() == BOND_NONE) { - // Pair with new device case, log how many devices with the same model id is in FastPair - // cache already. - mEventLogger.setCurrentEvent(EventCode.PAIR_WITH_NEW_MODEL); - if (mPreferences.getSameModelIdPairedDeviceCount() <= 0) { - mEventLogger.logCurrentEventSucceeded(); - } else { - mEventLogger.logCurrentEventFailed( - new BluetoothGattException( - "Already have this model ID in cache", - GATT_ERROR_CODE_PAIR_WITH_SAME_MODEL_ID_COUNT - + mPreferences.getSameModelIdPairedDeviceCount())); - } - Log.i(TAG, "This device already has " + mPreferences.getSameModelIdPairedDeviceCount() - + " peripheral with the same model Id"); - } - } - - /** - * Attempts to directly connect to any supported profile if we're already bonded, this will save - * time over tearing down the bond and recreating it. - */ - private void attemptDirectConnectionIfBonded(BluetoothDevice device, - BluetoothAudioPairer pairer) - throws PairingException { - if (mPreferences.getSkipConnectingProfiles()) { - if (mPreferences.getCheckBondStateWhenSkipConnectingProfiles() - && device.getBondState() == BluetoothDevice.BOND_BONDED) { - Log.i(TAG, "Skipping connecting to profiles by preferences."); - return; - } - throw new PairingException( - "Skipping connecting to profiles, no direct connection possible."); - } else if (!mPreferences.getAttemptDirectConnectionWhenPreviouslyBonded() - || device.getBondState() != BluetoothDevice.BOND_BONDED) { - throw new PairingException( - "Not previously bonded skipping direct connection, %s", device.getBondState()); - } - short[] supportedProfiles = getSupportedProfiles(device); - mEventLogger.setCurrentEvent(EventCode.DIRECTLY_CONNECTED_TO_PROFILE); - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Connect to profile directly")) { - attemptConnectProfiles( - pairer, - maskBluetoothAddress(device), - supportedProfiles, - mPreferences.getEnablePairFlowShowUiWithoutProfileConnection() - ? mPreferences.getNumConnectAttempts() - : 1, - mPreferences.getEnablePairingWhileDirectlyConnecting()); - Log.i(TAG, "Directly connected to " + maskBluetoothAddress(device)); - mEventLogger.logCurrentEventSucceeded(); - } catch (PairingException e) { - mEventLogger.logCurrentEventFailed(e); - // Rethrow e so that the exception bubbles up and we continue the normal pairing - // process. - throw e; - } - } - - @VisibleForTesting - void attemptConnectProfiles( - BluetoothAudioPairer pairer, - String deviceMaskedBluetoothAddress, - short[] profiles, - int numConnectionAttempts, - boolean enablePairingBehavior) - throws PairingException { - attemptConnectProfiles( - pairer, - deviceMaskedBluetoothAddress, - profiles, - numConnectionAttempts, - enablePairingBehavior, - new AtomicBoolean(false)); - } - - private void attemptConnectProfiles( - BluetoothAudioPairer pairer, - String deviceMaskedBluetoothAddress, - short[] profiles, - int numConnectionAttempts, - boolean enablePairingBehavior, - AtomicBoolean interruptConnection) - throws PairingException { - if (mPreferences.getMoreEventLogForQuality()) { - mEventLogger.setCurrentEvent(EventCode.BEFORE_CONNECT_PROFILE); - } - Exception lastException = null; - for (short profile : profiles) { - if (interruptConnection.get()) { - Log.w(TAG, "attemptConnectProfiles interrupted"); - break; - } - if (!mPreferences.isSupportedProfile(profile)) { - Log.w(TAG, "Ignoring unsupported profile=" + profile); - continue; - } - for (int i = 1; i <= numConnectionAttempts; i++) { - if (interruptConnection.get()) { - Log.w(TAG, "attemptConnectProfiles interrupted"); - break; - } - mEventLogger.setCurrentEvent(EventCode.CONNECT_PROFILE); - mEventLogger.setCurrentProfile(profile); - try { - pairer.connect(profile, enablePairingBehavior); - mEventLogger.logCurrentEventSucceeded(); - if (mPreferences.getMoreEventLogForQuality()) { - // For EventCode.BEFORE_CONNECT_PROFILE - mEventLogger.logCurrentEventSucceeded(); - } - // If successful, we're done. - // TODO(b/37167120): Connect to more than one profile. - return; - } catch (InterruptedException - | ReflectionException - | TimeoutException - | ExecutionException - | ConnectException e) { - Log.w(TAG, - "Error connecting to profile=" + profile - + " for device=" + deviceMaskedBluetoothAddress - + " (attempt " + i + " of " + mPreferences - .getNumConnectAttempts(), e); - mEventLogger.logCurrentEventFailed(e); - lastException = e; - } - } - } - if (mPreferences.getMoreEventLogForQuality()) { - // For EventCode.BEFORE_CONNECT_PROFILE - if (lastException != null) { - mEventLogger.logCurrentEventFailed(lastException); - } else { - mEventLogger.logCurrentEventSucceeded(); - } - } - throw new PairingException( - "Unable to connect to any profiles in: %s", Arrays.toString(profiles)); - } - - /** - * Checks whether or not an account key should be written to the device and writes it if so. - * This is called after handle notifying the pairedCallback that we've finished pairing, because - * at this point the headset is ready to use. - */ - @Nullable - private SharedSecret maybeWriteAccountKey(BluetoothDevice device) - throws InterruptedException, ExecutionException, TimeoutException, - NoSuchAlgorithmException, - BluetoothException { - if (!sTestMode) { - Locator.get(mContext, FastPairController.class).setShouldUpload(false); - } - if (!shouldWriteAccountKey()) { - // For FastPair 2.0, here should be a subsequent pairing case. - return null; - } - - // Check if it should be a subsequent pairing but go through initial pairing. If there is an - // existed paired history found, use the same account key instead of creating a new one. - byte[] accountKey = - mPairedHistoryFinder == null ? null : mPairedHistoryFinder.getExistingAccountKey(); - if (accountKey == null) { - // It is a real initial pairing, generate a new account key for the headset. - try (ScopedTiming scopedTiming1 = - new ScopedTiming(mTimingLogger, "Write account key")) { - accountKey = doWriteAccountKey(createAccountKey(), device.getAddress()); - if (accountKey == null) { - // Without writing account key back to provider, close the connection. - mGattConnectionManager.closeConnection(); - return null; - } - if (!mPreferences.getIsRetroactivePairing()) { - try (ScopedTiming scopedTiming2 = new ScopedTiming(mTimingLogger, - "Start CloudSyncing")) { - // Start to sync to the footprint - Locator.get(mContext, FastPairController.class).setShouldUpload(true); - //mContext.startService(createCloudSyncingIntent(accountKey)); - } catch (SecurityException e) { - Log.w(TAG, "Error adding device.", e); - } - } - } - } else if (shouldWriteAccountKeyForExistingCase(accountKey)) { - // There is an existing account key, but go through initial pairing, and still write the - // existing account key. - doWriteAccountKey(accountKey, device.getAddress()); - } - - // When finish writing account key in initial pairing, write new device name back to - // provider. - UUID characteristicUuid = NameCharacteristic.getId(mGattConnectionManager.getConnection()); - if (mPreferences.getEnableNamingCharacteristic() - && mNeedUpdateProviderName - && validateBluetoothGattCharacteristic( - mGattConnectionManager.getConnection(), characteristicUuid)) { - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "WriteNameToProvider")) { - writeNameToProvider(this.mProviderDeviceName, device.getAddress()); - } - } - - // When finish writing account key and name back to provider, close the connection. - mGattConnectionManager.closeConnection(); - return SharedSecret.create(accountKey, device.getAddress()); - } - - private boolean shouldWriteAccountKey() { - return isWritingAccountKeyEnabled() && isPairingWithAntiSpoofingPublicKey(); - } - - private boolean isWritingAccountKeyEnabled() { - return mPreferences.getNumWriteAccountKeyAttempts() > 0; - } - - private boolean isPairingWithAntiSpoofingPublicKey() { - return isPairingWithAntiSpoofingPublicKey(mPairingKey); - } - - private boolean isPairingWithAntiSpoofingPublicKey(@Nullable byte[] key) { - return key != null && key.length == EllipticCurveDiffieHellmanExchange.PUBLIC_KEY_LENGTH; - } - - /** - * Creates and writes an account key to the provided mac address. - */ - @Nullable - private byte[] doWriteAccountKey(byte[] accountKey, String macAddress) - throws InterruptedException, ExecutionException, TimeoutException, BluetoothException { - byte[] localPairingSecret = mPairingSecret; - if (localPairingSecret == null) { - Log.w(TAG, "Pairing secret was null, account key couldn't be encrypted or written."); - return null; - } - if (!mPreferences.getSkipDisconnectingGattBeforeWritingAccountKey()) { - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Close GATT and sleep")) { - // Make a new connection instead of reusing gattConnection, because this is - // post-pairing and we need an encrypted connection. - mGattConnectionManager.closeConnection(); - // Sleep before re-connecting to gatt, for writing account key, could increase - // stability. - Thread.sleep(mPreferences.getWriteAccountKeySleepMillis()); - } - } - - byte[] encryptedKey; - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, "Encrypt key")) { - encryptedKey = AesEcbSingleBlockEncryption.encrypt(localPairingSecret, accountKey); - } catch (GeneralSecurityException e) { - Log.w("Failed to encrypt key.", e); - return null; - } - - for (int i = 1; i <= mPreferences.getNumWriteAccountKeyAttempts(); i++) { - mEventLogger.setCurrentEvent(EventCode.WRITE_ACCOUNT_KEY); - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, - "Write key via GATT #" + i)) { - writeAccountKey(encryptedKey, macAddress); - mEventLogger.logCurrentEventSucceeded(); - return accountKey; - } catch (BluetoothException e) { - Log.w("Error writing account key attempt " + i + " of " + mPreferences - .getNumWriteAccountKeyAttempts(), e); - mEventLogger.logCurrentEventFailed(e); - // Retry with a while for stability. - Thread.sleep(mPreferences.getWriteAccountKeySleepMillis()); - } - } - return null; - } - - private byte[] createAccountKey() throws NoSuchAlgorithmException { - return AccountKeyGenerator.createAccountKey(); - } - - @VisibleForTesting - boolean shouldWriteAccountKeyForExistingCase(byte[] existingAccountKey) { - if (!mPreferences.getKeepSameAccountKeyWrite()) { - Log.i(TAG, - "The provider has already paired with the account, skip writing account key."); - return false; - } - if (existingAccountKey[0] != AccountKeyCharacteristic.TYPE) { - Log.i(TAG, - "The provider has already paired with the account, but accountKey[0] != 0x04." - + " Forget the device from the account and re-try"); - - return false; - } - Log.i(TAG, "The provider has already paired with the account, still write the same account " - + "key."); - return true; - } - - /** - * Performs a key-based pairing request handshake to authenticate and get the remote device's - * public address. - * - * @param key is described in {@link #pair(byte[])} - */ - @VisibleForTesting - SharedSecret handshakeForKeyBasedPairing(byte[] key) - throws InterruptedException, ExecutionException, TimeoutException, BluetoothException, - GeneralSecurityException, PairingException { - // We may also initialize gattConnectionManager of prepareForHandshake() that will be used - // in registerNotificationForNamePacket(), so we need to call it here. - HandshakeHandler handshakeHandler = prepareForHandshake(); - KeyBasedPairingRequest.Builder keyBasedPairingRequestBuilder = - new KeyBasedPairingRequest.Builder() - .setVerificationData(BluetoothAddress.decode(mBleAddress)); - if (mProviderInitiatesBonding) { - keyBasedPairingRequestBuilder - .addFlag(KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING); - } - // Seeker only request provider device name in initial pairing. - if (mPreferences.getEnableNamingCharacteristic() && isPairingWithAntiSpoofingPublicKey( - key)) { - keyBasedPairingRequestBuilder.addFlag(KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME); - // Register listener to receive name characteristic response from provider. - registerNotificationForNamePacket(); - } - if (mPreferences.getIsRetroactivePairing()) { - keyBasedPairingRequestBuilder - .addFlag(KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR); - keyBasedPairingRequestBuilder.setSeekerPublicAddress( - Preconditions.checkNotNull(BluetoothAddress.getPublicAddress(mContext))); - } - - return performHandshakeWithRetryAndSignalLostCheck( - handshakeHandler, key, keyBasedPairingRequestBuilder.build(), /* withRetry= */ - true); - } - - /** - * Performs an action-over-BLE request handshake for authentication, i.e. to identify the shared - * secret. The given key should be the account key. - */ - private SharedSecret handshakeForActionOverBle(byte[] key, - @AdditionalDataType int additionalDataType) - throws InterruptedException, ExecutionException, TimeoutException, BluetoothException, - GeneralSecurityException, PairingException { - HandshakeHandler handshakeHandler = prepareForHandshake(); - return performHandshakeWithRetryAndSignalLostCheck( - handshakeHandler, - key, - new ActionOverBle.Builder() - .setVerificationData(BluetoothAddress.decode(mBleAddress)) - .setAdditionalDataType(additionalDataType) - .build(), - /* withRetry= */ false); - } - - private HandshakeHandler prepareForHandshake() { - if (mGattConnectionManager == null) { - mGattConnectionManager = - new GattConnectionManager( - mContext, - mPreferences, - mEventLogger, - mBluetoothAdapter, - this::toggleBluetooth, - mBleAddress, - mTimingLogger, - mFastPairSignalChecker, - isPairingWithAntiSpoofingPublicKey()); - } - if (mHandshakeHandlerForTest != null) { - Log.w(TAG, "Use handshakeHandlerForTest!"); - return verifyNotNull(mHandshakeHandlerForTest); - } - return new HandshakeHandler( - mGattConnectionManager, mBleAddress, mPreferences, mEventLogger, - mFastPairSignalChecker); - } - - @VisibleForTesting - void setHandshakeHandlerForTest(@Nullable HandshakeHandler handshakeHandlerForTest) { - this.mHandshakeHandlerForTest = handshakeHandlerForTest; - } - - private SharedSecret performHandshakeWithRetryAndSignalLostCheck( - HandshakeHandler handshakeHandler, - byte[] key, - HandshakeMessage handshakeMessage, - boolean withRetry) - throws GeneralSecurityException, ExecutionException, BluetoothException, - InterruptedException, TimeoutException, PairingException { - SharedSecret handshakeResult = - withRetry - ? handshakeHandler.doHandshakeWithRetryAndSignalLostCheck( - key, handshakeMessage, mRescueFromError) - : handshakeHandler.doHandshake(key, handshakeMessage); - // TODO: Try to remove these two global variables, publicAddress and pairingSecret. - mPublicAddress = handshakeResult.getAddress(); - mPairingSecret = handshakeResult.getKey(); - return handshakeResult; - } - - private void toggleBluetooth() - throws InterruptedException, ExecutionException, TimeoutException { - if (!mPreferences.getToggleBluetoothOnFailure()) { - return; - } - - Log.i(TAG, "Turning Bluetooth off."); - mEventLogger.setCurrentEvent(EventCode.DISABLE_BLUETOOTH); - mBluetoothAdapter.unwrap().disable(); - disableBle(mBluetoothAdapter.unwrap()); - try { - waitForBluetoothState(android.bluetooth.BluetoothAdapter.STATE_OFF); - mEventLogger.logCurrentEventSucceeded(); - } catch (TimeoutException e) { - mEventLogger.logCurrentEventFailed(e); - // Soldier on despite failing to turn off Bluetooth. We can't control whether other - // clients (even inside GCore) kept it enabled in BLE-only mode. - Log.w(TAG, "Bluetooth still on. BluetoothAdapter state=" - + getBleState(mBluetoothAdapter.unwrap()), e); - } - - // Note: Intentionally don't re-enable BLE-only mode, because we don't know which app - // enabled it. The client app should listen to Bluetooth events and enable as necessary - // (because the user can toggle at any time; e.g. via Airplane mode). - Log.i(TAG, "Turning Bluetooth on."); - mEventLogger.setCurrentEvent(EventCode.ENABLE_BLUETOOTH); - mBluetoothAdapter.unwrap().enable(); - waitForBluetoothState(android.bluetooth.BluetoothAdapter.STATE_ON); - mEventLogger.logCurrentEventSucceeded(); - } - - private void waitForBluetoothState(int state) - throws TimeoutException, ExecutionException, InterruptedException { - waitForBluetoothStateUsingPolling(state); - } - - private void waitForBluetoothStateUsingPolling(int state) throws TimeoutException { - // There's a bug where we (pretty often!) never get the broadcast for STATE_ON or STATE_OFF. - // So poll instead. - long start = SystemClock.elapsedRealtime(); - long timeoutMillis = mPreferences.getBluetoothToggleTimeoutSeconds() * 1000L; - while (SystemClock.elapsedRealtime() - start < timeoutMillis) { - if (state == getBleState(mBluetoothAdapter.unwrap())) { - break; - } - SystemClock.sleep(mPreferences.getBluetoothStatePollingMillis()); - } - - if (state != getBleState(mBluetoothAdapter.unwrap())) { - throw new TimeoutException( - String.format( - Locale.getDefault(), - "Timed out waiting for state %d, current state is %d", - state, - getBleState(mBluetoothAdapter.unwrap()))); - } - } - - private BrEdrHandoverInformation getBrEdrHandoverInformation(BluetoothGattConnection connection) - throws BluetoothException, TdsException, InterruptedException, ExecutionException, - TimeoutException { - Log.i(TAG, "Connecting GATT server to BLE address=" + maskBluetoothAddress(mBleAddress)); - Log.i(TAG, "Telling device to become discoverable"); - mEventLogger.setCurrentEvent(EventCode.BR_EDR_HANDOVER_WRITE_CONTROL_POINT_REQUEST); - ChangeObserver changeObserver = - connection.enableNotification( - TransportDiscoveryService.ID, - TransportDiscoveryService.ControlPointCharacteristic.ID); - connection.writeCharacteristic( - TransportDiscoveryService.ID, - TransportDiscoveryService.ControlPointCharacteristic.ID, - TDS_CONTROL_POINT_REQUEST); - - byte[] response = - changeObserver.waitForUpdate( - TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - @ResultCode int resultCode = fromTdsControlPointIndication(response); - if (resultCode != ResultCode.SUCCESS) { - throw new TdsException( - BrEdrHandoverErrorCode.CONTROL_POINT_RESULT_CODE_NOT_SUCCESS, - "TDS Control Point result code (%s) was not success in response %s", - resultCode, - base16().lowerCase().encode(response)); - } - mEventLogger.logCurrentEventSucceeded(); - return new BrEdrHandoverInformation( - getAddressFromBrEdrConnection(connection), - getProfilesFromBrEdrConnection(connection)); - } - - private byte[] getAddressFromBrEdrConnection(BluetoothGattConnection connection) - throws BluetoothException, TdsException { - Log.i(TAG, "Getting Bluetooth MAC"); - mEventLogger.setCurrentEvent(EventCode.BR_EDR_HANDOVER_READ_BLUETOOTH_MAC); - byte[] brHandoverData = - connection.readCharacteristic( - TransportDiscoveryService.ID, - to128BitUuid(mPreferences.getBrHandoverDataCharacteristicId())); - if (brHandoverData == null || brHandoverData.length < 7) { - throw new TdsException( - BrEdrHandoverErrorCode.BLUETOOTH_MAC_INVALID, - "Bluetooth MAC not contained in BR handover data: %s", - brHandoverData != null ? base16().lowerCase().encode(brHandoverData) - : "(none)"); - } - byte[] bluetoothAddress = - new Bytes.Value(Arrays.copyOfRange(brHandoverData, 1, 7), ByteOrder.LITTLE_ENDIAN) - .getBytes(ByteOrder.BIG_ENDIAN); - mEventLogger.logCurrentEventSucceeded(); - return bluetoothAddress; - } - - private short[] getProfilesFromBrEdrConnection(BluetoothGattConnection connection) { - mEventLogger.setCurrentEvent(EventCode.BR_EDR_HANDOVER_READ_TRANSPORT_BLOCK); - try { - byte[] transportBlock = - connection.readDescriptor( - TransportDiscoveryService.ID, - to128BitUuid(mPreferences.getBluetoothSigDataCharacteristicId()), - to128BitUuid(mPreferences.getBrTransportBlockDataDescriptorId())); - Log.i(TAG, "Got transport block: " + base16().lowerCase().encode(transportBlock)); - short[] profiles = getSupportedProfiles(transportBlock); - mEventLogger.logCurrentEventSucceeded(); - return profiles; - } catch (BluetoothException | TdsException | ParseException e) { - Log.w(TAG, "Failed to get supported profiles from transport block.", e); - mEventLogger.logCurrentEventFailed(e); - } - return new short[0]; - } - - @VisibleForTesting - boolean writeNameToProvider(@Nullable String deviceName, @Nullable String address) - throws InterruptedException, TimeoutException, ExecutionException { - if (deviceName == null || address == null) { - Log.i(TAG, "writeNameToProvider fail because provider name or address is null."); - return false; - } - if (mPairingSecret == null) { - Log.i(TAG, "writeNameToProvider fail because no pairingSecret."); - return false; - } - byte[] encryptedDeviceNamePacket; - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, "Encode device name")) { - encryptedDeviceNamePacket = - NamingEncoder.encodeNamingPacket(mPairingSecret, deviceName); - } catch (GeneralSecurityException e) { - Log.w(TAG, "Failed to encrypt device name.", e); - return false; - } - - for (int i = 1; i <= mPreferences.getNumWriteAccountKeyAttempts(); i++) { - mEventLogger.setCurrentEvent(EventCode.WRITE_DEVICE_NAME); - try { - writeDeviceName(encryptedDeviceNamePacket, address); - mEventLogger.logCurrentEventSucceeded(); - return true; - } catch (BluetoothException e) { - Log.w(TAG, "Error writing name attempt " + i + " of " - + mPreferences.getNumWriteAccountKeyAttempts()); - mEventLogger.logCurrentEventFailed(e); - // Reuses the existing preference because the same usage. - Thread.sleep(mPreferences.getWriteAccountKeySleepMillis()); - } - } - return false; - } - - private void writeAccountKey(byte[] encryptedAccountKey, String address) - throws BluetoothException, InterruptedException, ExecutionException, TimeoutException { - Log.i(TAG, "Writing account key to address=" + maskBluetoothAddress(address)); - BluetoothGattConnection connection = mGattConnectionManager.getConnection(); - connection.setOperationTimeout( - TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - UUID characteristicUuid = AccountKeyCharacteristic.getId(connection); - connection.writeCharacteristic(FastPairService.ID, characteristicUuid, encryptedAccountKey); - Log.i(TAG, - "Finished writing encrypted account key=" + base16().encode(encryptedAccountKey)); - } - - private void writeDeviceName(byte[] naming, String address) - throws BluetoothException, InterruptedException, ExecutionException, TimeoutException { - Log.i(TAG, "Writing new device name to address=" + maskBluetoothAddress(address)); - BluetoothGattConnection connection = mGattConnectionManager.getConnection(); - connection.setOperationTimeout( - TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - UUID characteristicUuid = NameCharacteristic.getId(connection); - connection.writeCharacteristic(FastPairService.ID, characteristicUuid, naming); - Log.i(TAG, "Finished writing new device name=" + base16().encode(naming)); - } - - /** - * Reads firmware version after write account key to provider since simulator is more stable to - * read firmware version in initial gatt connection. This function will also read firmware when - * detect bloomfilter. Need to verify this after real device come out. TODO(b/130592473) - */ - @Nullable - public String readFirmwareVersion() - throws BluetoothException, InterruptedException, ExecutionException, TimeoutException { - if (!TextUtils.isEmpty(sInitialConnectionFirmwareVersion)) { - String result = sInitialConnectionFirmwareVersion; - sInitialConnectionFirmwareVersion = null; - return result; - } - if (mGattConnectionManager == null) { - mGattConnectionManager = - new GattConnectionManager( - mContext, - mPreferences, - mEventLogger, - mBluetoothAdapter, - this::toggleBluetooth, - mBleAddress, - mTimingLogger, - mFastPairSignalChecker, - /* setMtu= */ true); - mGattConnectionManager.closeConnection(); - } - if (sTestMode) { - return null; - } - BluetoothGattConnection connection = mGattConnectionManager.getConnection(); - connection.setOperationTimeout( - TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - - try { - String firmwareVersion = - new String( - connection.readCharacteristic( - FastPairService.ID, - to128BitUuid( - mPreferences.getFirmwareVersionCharacteristicId()))); - Log.i(TAG, "FastPair: Got the firmware info version number = " + firmwareVersion); - mGattConnectionManager.closeConnection(); - return firmwareVersion; - } catch (BluetoothException e) { - Log.i(TAG, "FastPair: can't read firmware characteristic.", e); - mGattConnectionManager.closeConnection(); - return null; - } - } - - @VisibleForTesting - @Nullable - String getInitialConnectionFirmware() { - return sInitialConnectionFirmwareVersion; - } - - private void registerNotificationForNamePacket() - throws BluetoothException, InterruptedException, ExecutionException, TimeoutException { - Log.i(TAG, - "register for the device name response from address=" + maskBluetoothAddress( - mBleAddress)); - - BluetoothGattConnection gattConnection = mGattConnectionManager.getConnection(); - gattConnection.setOperationTimeout( - TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - try { - mDeviceNameReceiver = new DeviceNameReceiver(gattConnection); - } catch (BluetoothException e) { - Log.i(TAG, "Can't register for device name response, no naming characteristic."); - return; - } - } - - private short[] getSupportedProfiles(BluetoothDevice device) { - short[] supportedProfiles = getCachedUuids(device); - if (supportedProfiles.length == 0 && mPreferences.getNumSdpAttemptsAfterBonded() > 0) { - supportedProfiles = - attemptGetBluetoothClassicProfiles(device, - mPreferences.getNumSdpAttemptsAfterBonded()); - } - if (supportedProfiles.length == 0) { - supportedProfiles = Constants.getSupportedProfiles(); - Log.w(TAG, "Attempting to connect constants profiles, " - + Arrays.toString(supportedProfiles)); - } else { - Log.i(TAG, - "Attempting to connect device profiles, " + Arrays.toString(supportedProfiles)); - } - return supportedProfiles; - } - - private static short[] getSupportedProfiles(byte[] transportBlock) - throws TdsException, ParseException { - if (transportBlock == null || transportBlock.length < 4) { - throw new TdsException( - BrEdrHandoverErrorCode.TRANSPORT_BLOCK_INVALID, - "Transport Block null or too short: %s", - base16().lowerCase().encode(transportBlock)); - } - int transportDataLength = transportBlock[2]; - if (transportBlock.length < 3 + transportDataLength) { - throw new TdsException( - BrEdrHandoverErrorCode.TRANSPORT_BLOCK_INVALID, - "Transport Block has wrong length byte: %s", - base16().lowerCase().encode(transportBlock)); - } - byte[] transportData = Arrays.copyOfRange(transportBlock, 3, 3 + transportDataLength); - for (Ltv ltv : Ltv.parse(transportData)) { - int uuidLength = uuidLength(ltv.mType); - // We currently only support a single list of 2-byte UUIDs. - // TODO(b/37539535): Support multiple lists, and longer (32-bit, 128-bit) IDs? - if (uuidLength == 2) { - return toShorts(ByteOrder.LITTLE_ENDIAN, ltv.mValue); - } - } - return new short[0]; - } - - /** - * Returns 0 if the type is not one of the UUID list types; otherwise returns length in bytes. - */ - private static int uuidLength(byte dataType) { - switch (dataType) { - case TransportDiscoveryService.SERVICE_UUIDS_16_BIT_LIST_TYPE: - return 2; - case TransportDiscoveryService.SERVICE_UUIDS_32_BIT_LIST_TYPE: - return 4; - case TransportDiscoveryService.SERVICE_UUIDS_128_BIT_LIST_TYPE: - return 16; - default: - return 0; - } - } - - private short[] attemptGetBluetoothClassicProfiles(BluetoothDevice device, int numSdpAttempts) { - // The docs say that if fetchUuidsWithSdp() has an error or "takes a long time", we get an - // intent containing only the stuff in the cache (i.e. nothing). Retry a few times. - short[] supportedProfiles = null; - for (int i = 1; i <= numSdpAttempts; i++) { - mEventLogger.setCurrentEvent(EventCode.GET_PROFILES_VIA_SDP); - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, - "Get BR/EDR handover information via SDP #" + i)) { - supportedProfiles = getSupportedProfilesViaBluetoothClassic(device); - } catch (ExecutionException | InterruptedException | TimeoutException e) { - // Ignores and retries if needed. - } - if (supportedProfiles != null && supportedProfiles.length != 0) { - mEventLogger.logCurrentEventSucceeded(); - break; - } else { - mEventLogger.logCurrentEventFailed(new TimeoutException()); - Log.w(TAG, "SDP returned no UUIDs from " + maskBluetoothAddress(device.getAddress()) - + ", assuming timeout (attempt " + i + " of " + numSdpAttempts + ")."); - } - } - return (supportedProfiles == null) ? new short[0] : supportedProfiles; - } - - private short[] getSupportedProfilesViaBluetoothClassic(BluetoothDevice device) - throws ExecutionException, InterruptedException, TimeoutException { - Log.i(TAG, "Getting supported profiles via SDP (Bluetooth Classic) for " - + maskBluetoothAddress(device.getAddress())); - try (DeviceIntentReceiver supportedProfilesReceiver = - DeviceIntentReceiver.oneShotReceiver( - mContext, mPreferences, device, BluetoothDevice.ACTION_UUID)) { - device.fetchUuidsWithSdp(); - supportedProfilesReceiver.await(mPreferences.getSdpTimeoutSeconds(), TimeUnit.SECONDS); - } - return getCachedUuids(device); - } - - private static short[] getCachedUuids(BluetoothDevice device) { - ParcelUuid[] parcelUuids = device.getUuids(); - Log.i(TAG, "Got supported UUIDs: " + Arrays.toString(parcelUuids)); - if (parcelUuids == null) { - // The OS can return null. - parcelUuids = new ParcelUuid[0]; - } - - List shortUuids = new ArrayList<>(parcelUuids.length); - for (ParcelUuid parcelUuid : parcelUuids) { - UUID uuid = parcelUuid.getUuid(); - if (BluetoothUuids.is16BitUuid(uuid)) { - shortUuids.add(get16BitUuid(uuid)); - } - } - return Shorts.toArray(shortUuids); - } - - private void callbackOnPaired() { - if (mPairedCallback != null) { - mPairedCallback.onPaired(mPublicAddress != null ? mPublicAddress : mBleAddress); - } - } - - private void callbackOnGetAddress(String address) { - if (mOnGetBluetoothAddressCallback != null) { - mOnGetBluetoothAddressCallback.onGetBluetoothAddress(address); - } - } - - private boolean validateBluetoothGattCharacteristic( - BluetoothGattConnection connection, UUID characteristicUUID) { - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Get service characteristic list")) { - List serviceCharacteristicList = - connection.getService(FastPairService.ID).getCharacteristics(); - for (BluetoothGattCharacteristic characteristic : serviceCharacteristicList) { - if (characteristicUUID.equals(characteristic.getUuid())) { - Log.i(TAG, "characteristic is exists, uuid = " + characteristicUUID); - return true; - } - } - } catch (BluetoothException e) { - Log.w(TAG, "Can't get service characteristic list.", e); - } - Log.i(TAG, "can't find characteristic, uuid = " + characteristicUUID); - return false; - } - - // This method is only for testing to make test method block until get name response or time - // out. - /** - * Set name response countdown latch. - */ - public void setNameResponseCountDownLatch(CountDownLatch countDownLatch) { - if (mDeviceNameReceiver != null) { - mDeviceNameReceiver.setCountDown(countDownLatch); - Log.v(TAG, "set up nameResponseCountDown"); - } - } - - private static int getBleState(android.bluetooth.BluetoothAdapter bluetoothAdapter) { - // Can't use the public isLeEnabled() API, because it returns false for - // STATE_BLE_TURNING_(ON|OFF). So if we assume false == STATE_OFF, that can be - // very wrong. - return getLeState(bluetoothAdapter); - } - - private static int getLeState(android.bluetooth.BluetoothAdapter adapter) { - try { - return (Integer) Reflect.on(adapter).withMethod("getLeState").get(); - } catch (ReflectionException e) { - Log.i(TAG, "Can't call getLeState", e); - } - return adapter.getState(); - } - - private static void disableBle(android.bluetooth.BluetoothAdapter adapter) { - adapter.disableBLE(); - } - - /** - * Handle the searching of Fast Pair history. Since there is only one public address using - * during Fast Pair connection, {@link #isInPairedHistory(String)} only needs to be called once, - * then the result is kept, and call {@link #getExistingAccountKey()} to get the result. - */ - @VisibleForTesting - static final class FastPairHistoryFinder { - - private @Nullable - byte[] mExistingAccountKey; - @Nullable - private final List mHistoryItems; - - FastPairHistoryFinder(List historyItems) { - this.mHistoryItems = historyItems; - } - - @WorkerThread - @VisibleForTesting - boolean isInPairedHistory(String publicAddress) { - if (mHistoryItems == null || mHistoryItems.isEmpty()) { - return false; - } - for (FastPairHistoryItem item : mHistoryItems) { - if (item.isMatched(BluetoothAddress.decode(publicAddress))) { - mExistingAccountKey = item.accountKey().toByteArray(); - return true; - } - } - return false; - } - - // This function should be called after isInPairedHistory(). Or it will just return null. - @WorkerThread - @VisibleForTesting - @Nullable - byte[] getExistingAccountKey() { - return mExistingAccountKey; - } - } - - private static final class DeviceNameReceiver { - - @GuardedBy("this") - private @Nullable - byte[] mEncryptedResponse; - - @GuardedBy("this") - @Nullable - private String mDecryptedDeviceName; - - @Nullable - private CountDownLatch mResponseCountDown; - - DeviceNameReceiver(BluetoothGattConnection gattConnection) throws BluetoothException { - UUID characteristicUuid = NameCharacteristic.getId(gattConnection); - ChangeObserver observer = - gattConnection.enableNotification(FastPairService.ID, characteristicUuid); - observer.setListener( - (byte[] value) -> { - synchronized (DeviceNameReceiver.this) { - Log.i(TAG, "DeviceNameReceiver: device name response size = " - + value.length); - // We don't decrypt it here because we may not finish handshaking and - // the pairing - // secret is not available. - mEncryptedResponse = value; - } - // For testing to know we get the device name from provider. - if (mResponseCountDown != null) { - mResponseCountDown.countDown(); - Log.v(TAG, "Finish nameResponseCountDown."); - } - }); - } - - void setCountDown(CountDownLatch countDownLatch) { - this.mResponseCountDown = countDownLatch; - } - - synchronized @Nullable String getParsedResult(byte[] secret) { - if (mDecryptedDeviceName != null) { - return mDecryptedDeviceName; - } - if (mEncryptedResponse == null) { - Log.i(TAG, "DeviceNameReceiver: no device name sent from the Provider."); - return null; - } - try { - mDecryptedDeviceName = NamingEncoder.decodeNamingPacket(secret, mEncryptedResponse); - Log.i(TAG, "DeviceNameReceiver: decrypted provider's name from naming response, " - + "name = " + mDecryptedDeviceName); - } catch (GeneralSecurityException e) { - Log.w(TAG, "DeviceNameReceiver: fail to parse the NameCharacteristic from provider" - + ".", e); - return null; - } - return mDecryptedDeviceName; - } - } - - static void checkFastPairSignal( - FastPairSignalChecker fastPairSignalChecker, - String currentAddress, - Exception originalException) - throws SignalLostException, SignalRotatedException { - String newAddress = fastPairSignalChecker.getValidAddressForModelId(currentAddress); - if (TextUtils.isEmpty(newAddress)) { - throw new SignalLostException("Signal lost", originalException); - } else if (!Ascii.equalsIgnoreCase(currentAddress, newAddress)) { - throw new SignalRotatedException("Address rotated", newAddress, originalException); - } - } - - @VisibleForTesting - public Preferences getPreferences() { - return mPreferences; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairHistoryItem.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairHistoryItem.java deleted file mode 100644 index e7748860e62e4d07073b4803f6d10eeecdef799e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairHistoryItem.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.google.common.primitives.Bytes.concat; - -import com.google.common.hash.Hashing; -import com.google.protobuf.ByteString; - -import java.util.Arrays; - -/** - * It contains the sha256 of "account key + headset's public address" to identify the headset which - * has paired with the account. Previously, account key is the only information for Fast Pair to - * identify the headset, but Fast Pair can't identify the headset in initial pairing, there is no - * account key data advertising from headset. - */ -public class FastPairHistoryItem { - - private final ByteString mAccountKey; - private final ByteString mSha256AccountKeyPublicAddress; - - FastPairHistoryItem(ByteString accountkey, ByteString sha256AccountKeyPublicAddress) { - mAccountKey = accountkey; - mSha256AccountKeyPublicAddress = sha256AccountKeyPublicAddress; - } - - /** - * Creates an instance of {@link FastPairHistoryItem}. - * - * @param accountKey key of an account that has paired with the headset. - * @param sha256AccountKeyPublicAddress hash value of account key and headset's public address. - */ - public static FastPairHistoryItem create( - ByteString accountKey, ByteString sha256AccountKeyPublicAddress) { - return new FastPairHistoryItem(accountKey, sha256AccountKeyPublicAddress); - } - - ByteString accountKey() { - return mAccountKey; - } - - ByteString sha256AccountKeyPublicAddress() { - return mSha256AccountKeyPublicAddress; - } - - // Return true if the input public address is considered the same as this history item. Because - // of privacy concern, Fast Pair does not really store the public address, it is identified by - // the SHA256 of the account key and the public key. - final boolean isMatched(byte[] publicAddress) { - return Arrays.equals( - sha256AccountKeyPublicAddress().toByteArray(), - Hashing.sha256().hashBytes(concat(accountKey().toByteArray(), publicAddress)) - .asBytes()); - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/GattConnectionManager.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/GattConnectionManager.java deleted file mode 100644 index e7ce4bfde675a4e45db4777aaf42542ec24a32b0..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/GattConnectionManager.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH; -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; -import static com.android.server.nearby.common.bluetooth.fastpair.FastPairDualConnection.logRetrySuccessEvent; - -import static com.google.common.base.Preconditions.checkNotNull; - -import android.content.Context; -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.core.util.Consumer; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.BluetoothGattException; -import com.android.server.nearby.common.bluetooth.BluetoothTimeoutException; -import com.android.server.nearby.common.bluetooth.fastpair.TimingLogger.ScopedTiming; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattHelper; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattHelper.ConnectionOptions; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.BluetoothOperationTimeoutException; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.ErrorCode; -import com.android.server.nearby.intdefs.NearbyEventIntDefs.EventCode; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Manager for working with Gatt connections. - * - *

    This helper class allows for opening and closing GATT connections to a provided address. - * Optionally, it can also support automatically reopening a connection in the case that it has been - * closed when it's next needed through {@link Preferences#getAutomaticallyReconnectGattWhenNeeded}. - */ -// TODO(b/202524672): Add class unit test. -final class GattConnectionManager { - - private static final String TAG = GattConnectionManager.class.getSimpleName(); - - private final Context mContext; - private final Preferences mPreferences; - private final EventLoggerWrapper mEventLogger; - private final BluetoothAdapter mBluetoothAdapter; - private final ToggleBluetoothTask mToggleBluetooth; - private final String mAddress; - private final TimingLogger mTimingLogger; - private final boolean mSetMtu; - @Nullable - private final FastPairConnection.FastPairSignalChecker mFastPairSignalChecker; - @Nullable - private BluetoothGattConnection mGattConnection; - private static boolean sTestMode = false; - - static void enableTestMode() { - sTestMode = true; - } - - GattConnectionManager( - Context context, - Preferences preferences, - EventLoggerWrapper eventLogger, - BluetoothAdapter bluetoothAdapter, - ToggleBluetoothTask toggleBluetooth, - String address, - TimingLogger timingLogger, - @Nullable FastPairConnection.FastPairSignalChecker fastPairSignalChecker, - boolean setMtu) { - this.mContext = context; - this.mPreferences = preferences; - this.mEventLogger = eventLogger; - this.mBluetoothAdapter = bluetoothAdapter; - this.mToggleBluetooth = toggleBluetooth; - this.mAddress = address; - this.mTimingLogger = timingLogger; - this.mFastPairSignalChecker = fastPairSignalChecker; - this.mSetMtu = setMtu; - } - - /** - * Gets a gatt connection to address. If this connection does not exist, it creates one. - */ - BluetoothGattConnection getConnection() - throws InterruptedException, ExecutionException, TimeoutException, BluetoothException { - if (mGattConnection == null) { - try { - mGattConnection = - connect(mAddress, /* checkSignalWhenFail= */ false, - /* rescueFromError= */ null); - } catch (SignalLostException | SignalRotatedException e) { - // Impossible to happen here because we didn't do signal check. - throw new ExecutionException("getConnection throws SignalLostException", e); - } - } - return mGattConnection; - } - - BluetoothGattConnection getConnectionWithSignalLostCheck( - @Nullable Consumer rescueFromError) - throws InterruptedException, ExecutionException, TimeoutException, BluetoothException, - SignalLostException, SignalRotatedException { - if (mGattConnection == null) { - mGattConnection = connect(mAddress, /* checkSignalWhenFail= */ true, - rescueFromError); - } - return mGattConnection; - } - - /** - * Closes the gatt connection when it is open. - */ - void closeConnection() throws BluetoothException { - if (mGattConnection != null) { - try (ScopedTiming scopedTiming = new ScopedTiming(mTimingLogger, "Close GATT")) { - mGattConnection.close(); - mGattConnection = null; - } - } - } - - private BluetoothGattConnection connect( - String address, boolean checkSignalWhenFail, - @Nullable Consumer rescueFromError) - throws InterruptedException, ExecutionException, TimeoutException, BluetoothException, - SignalLostException, SignalRotatedException { - int i = 1; - boolean isRecoverable = true; - long startElapsedRealtime = SystemClock.elapsedRealtime(); - BluetoothException lastException = null; - mEventLogger.setCurrentEvent(EventCode.GATT_CONNECT); - while (isRecoverable) { - try (ScopedTiming scopedTiming = - new ScopedTiming(mTimingLogger, "Connect GATT #" + i)) { - Log.i(TAG, "Connecting to GATT server at " + maskBluetoothAddress(address)); - if (sTestMode) { - return null; - } - BluetoothGattConnection connection = - new BluetoothGattHelper(mContext, mBluetoothAdapter) - .connect( - mBluetoothAdapter.getRemoteDevice(address), - getConnectionOptions(startElapsedRealtime)); - connection.setOperationTimeout( - TimeUnit.SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - if (mPreferences.getAutomaticallyReconnectGattWhenNeeded()) { - connection.addCloseListener( - () -> { - Log.i(TAG, "Gatt connection with " + maskBluetoothAddress(address) - + " closed."); - mGattConnection = null; - }); - } - mEventLogger.logCurrentEventSucceeded(); - if (lastException != null) { - logRetrySuccessEvent(EventCode.RECOVER_BY_RETRY_GATT, lastException, - mEventLogger); - } - return connection; - } catch (BluetoothException e) { - lastException = e; - - boolean ableToRetry; - if (mPreferences.getGattConnectRetryTimeoutMillis() > 0) { - ableToRetry = - (SystemClock.elapsedRealtime() - startElapsedRealtime) - < mPreferences.getGattConnectRetryTimeoutMillis(); - Log.i(TAG, "Retry connecting GATT by timeout: " + ableToRetry); - } else { - ableToRetry = i < mPreferences.getNumAttempts(); - } - - if (mPreferences.getRetryGattConnectionAndSecretHandshake()) { - if (isNoRetryError(mPreferences, e)) { - ableToRetry = false; - } - - if (ableToRetry) { - if (rescueFromError != null) { - rescueFromError.accept( - e instanceof BluetoothOperationTimeoutException - ? ErrorCode.SUCCESS_RETRY_GATT_TIMEOUT - : ErrorCode.SUCCESS_RETRY_GATT_ERROR); - } - if (mFastPairSignalChecker != null && checkSignalWhenFail) { - FastPairDualConnection - .checkFastPairSignal(mFastPairSignalChecker, address, e); - } - } - isRecoverable = ableToRetry; - if (ableToRetry && mPreferences.getPairingRetryDelayMs() > 0) { - SystemClock.sleep(mPreferences.getPairingRetryDelayMs()); - } - } else { - isRecoverable = - ableToRetry - && (e instanceof BluetoothOperationTimeoutException - || e instanceof BluetoothTimeoutException - || (e instanceof BluetoothGattException - && ((BluetoothGattException) e).getGattErrorCode() == 133)); - } - Log.w(TAG, "GATT connect attempt " + i + "of " + mPreferences.getNumAttempts() - + " failed, " + (isRecoverable ? "recovering" : "permanently"), e); - if (isRecoverable) { - // If we're going to retry, log failure here. If we throw, an upper level will - // log it. - mToggleBluetooth.toggleBluetooth(); - i++; - mEventLogger.logCurrentEventFailed(e); - mEventLogger.setCurrentEvent(EventCode.GATT_CONNECT); - } - } - } - throw checkNotNull(lastException); - } - - static boolean isNoRetryError(Preferences preferences, BluetoothException e) { - return e instanceof BluetoothGattException - && preferences - .getGattConnectionAndSecretHandshakeNoRetryGattError() - .contains(((BluetoothGattException) e).getGattErrorCode()); - } - - @VisibleForTesting - long getTimeoutMs(long spentTime) { - long timeoutInMs; - if (mPreferences.getRetryGattConnectionAndSecretHandshake()) { - timeoutInMs = - spentTime < mPreferences.getGattConnectShortTimeoutRetryMaxSpentTimeMs() - ? mPreferences.getGattConnectShortTimeoutMs() - : mPreferences.getGattConnectLongTimeoutMs(); - } else { - timeoutInMs = TimeUnit.SECONDS.toMillis(mPreferences.getGattConnectionTimeoutSeconds()); - } - return timeoutInMs; - } - - private ConnectionOptions getConnectionOptions(long startElapsedRealtime) { - return createConnectionOptions( - mSetMtu, - getTimeoutMs(SystemClock.elapsedRealtime() - startElapsedRealtime)); - } - - public static ConnectionOptions createConnectionOptions(boolean setMtu, long timeoutInMs) { - ConnectionOptions.Builder builder = ConnectionOptions.builder(); - if (setMtu) { - // There are 3 overhead bytes added to BLE packets. - builder.setMtu( - AES_BLOCK_LENGTH + EllipticCurveDiffieHellmanExchange.PUBLIC_KEY_LENGTH + 3); - } - builder.setConnectionTimeoutMillis(timeoutInMs); - return builder.build(); - } - - @VisibleForTesting - void setGattConnection(BluetoothGattConnection gattConnection) { - this.mGattConnection = gattConnection; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java deleted file mode 100644 index 984133b153e430141678ef0692fc01097a909eda..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandler.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH; -import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.decrypt; -import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.encrypt; -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.BLUETOOTH_ADDRESS_LENGTH; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.DEVICE_ACTION; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.ADDITIONAL_DATA_TYPE_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.EVENT_ADDITIONAL_DATA_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.EVENT_ADDITIONAL_DATA_LENGTH_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.EVENT_CODE_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.EVENT_GROUP_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.FLAGS_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.SEEKER_PUBLIC_ADDRESS_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.TYPE_ACTION_OVER_BLE; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.TYPE_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.TYPE_KEY_BASED_PAIRING_REQUEST; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.VERIFICATION_DATA_INDEX; -import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.VERIFICATION_DATA_LENGTH; -import static com.android.server.nearby.common.bluetooth.fastpair.FastPairDualConnection.logRetrySuccessEvent; -import static com.android.server.nearby.common.bluetooth.fastpair.GattConnectionManager.isNoRetryError; - -import static com.google.common.base.Verify.verifyNotNull; -import static com.google.common.io.BaseEncoding.base16; -import static com.google.common.primitives.Bytes.concat; - -import static java.util.concurrent.TimeUnit.SECONDS; - -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.core.util.Consumer; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.BluetoothTimeoutException; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AdditionalDataCharacteristic.AdditionalDataType; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag; -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request; -import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection.SharedSecret; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection.ChangeObserver; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.BluetoothOperationTimeoutException; -import com.android.server.nearby.intdefs.FastPairEventIntDefs.ErrorCode; -import com.android.server.nearby.intdefs.NearbyEventIntDefs.EventCode; - -import java.security.GeneralSecurityException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -/** - * Handles the handshake step of Fast Pair, the Provider's public address and the shared secret will - * be disclosed during this step. It is the first step of all key-based operations, e.g. key-based - * pairing and action over BLE. - * - * @see - * Fastpair Spec Procedure - */ -public class HandshakeHandler { - - private static final String TAG = HandshakeHandler.class.getSimpleName(); - private final GattConnectionManager mGattConnectionManager; - private final String mProviderBleAddress; - private final Preferences mPreferences; - private final EventLoggerWrapper mEventLogger; - @Nullable - private final FastPairConnection.FastPairSignalChecker mFastPairSignalChecker; - - /** - * Keeps the keys used during handshaking, generated by {@link #createKey(byte[])}. - */ - private static final class Keys { - - private final byte[] mSharedSecret; - private final byte[] mPublicKey; - - private Keys(byte[] sharedSecret, byte[] publicKey) { - this.mSharedSecret = sharedSecret; - this.mPublicKey = publicKey; - } - } - - public HandshakeHandler( - GattConnectionManager gattConnectionManager, - String bleAddress, - Preferences preferences, - EventLoggerWrapper eventLogger, - @Nullable FastPairConnection.FastPairSignalChecker fastPairSignalChecker) { - this.mGattConnectionManager = gattConnectionManager; - this.mProviderBleAddress = bleAddress; - this.mPreferences = preferences; - this.mEventLogger = eventLogger; - this.mFastPairSignalChecker = fastPairSignalChecker; - } - - /** - * Performs a handshake to authenticate and get the remote device's public address. Returns the - * AES-128 key as the shared secret for this pairing session. - */ - public SharedSecret doHandshake(byte[] key, HandshakeMessage message) - throws GeneralSecurityException, InterruptedException, ExecutionException, - TimeoutException, BluetoothException, PairingException { - Keys keys = createKey(key); - Log.i(TAG, - "Handshake " + maskBluetoothAddress(mProviderBleAddress) + ", flags " - + message.mFlags); - byte[] handshakeResponse = - processGattCommunication( - createPacket(keys, message), - SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds())); - String providerPublicAddress = decodeResponse(keys.mSharedSecret, handshakeResponse); - - return SharedSecret.create(keys.mSharedSecret, providerPublicAddress); - } - - /** - * Performs a handshake to authenticate and get the remote device's public address. Returns the - * AES-128 key as the shared secret for this pairing session. Will retry and also performs - * FastPair signal check if fails. - */ - public SharedSecret doHandshakeWithRetryAndSignalLostCheck( - byte[] key, HandshakeMessage message, @Nullable Consumer rescueFromError) - throws GeneralSecurityException, InterruptedException, ExecutionException, - TimeoutException, BluetoothException, PairingException { - Keys keys = createKey(key); - Log.i(TAG, - "Handshake " + maskBluetoothAddress(mProviderBleAddress) + ", flags " - + message.mFlags); - int retryCount = 0; - byte[] handshakeResponse = null; - long startTime = SystemClock.elapsedRealtime(); - BluetoothException lastException = null; - do { - try { - mEventLogger.setCurrentEvent(EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); - handshakeResponse = - processGattCommunication( - createPacket(keys, message), - getTimeoutMs(SystemClock.elapsedRealtime() - startTime)); - mEventLogger.logCurrentEventSucceeded(); - if (lastException != null) { - logRetrySuccessEvent(EventCode.RECOVER_BY_RETRY_HANDSHAKE, lastException, - mEventLogger); - } - } catch (BluetoothException e) { - lastException = e; - long spentTime = SystemClock.elapsedRealtime() - startTime; - Log.w(TAG, "Secret handshake failed, address=" - + maskBluetoothAddress(mProviderBleAddress) - + ", spent time=" + spentTime + "ms, retryCount=" + retryCount); - mEventLogger.logCurrentEventFailed(e); - - if (!mPreferences.getRetryGattConnectionAndSecretHandshake()) { - throw e; - } - - if (spentTime > mPreferences.getSecretHandshakeLongTimeoutRetryMaxSpentTimeMs()) { - Log.w(TAG, "Spent too long time for handshake, timeInMs=" + spentTime); - throw e; - } - if (isNoRetryError(mPreferences, e)) { - throw e; - } - - if (mFastPairSignalChecker != null) { - FastPairDualConnection - .checkFastPairSignal(mFastPairSignalChecker, mProviderBleAddress, e); - } - retryCount++; - if (retryCount > mPreferences.getSecretHandshakeRetryAttempts() - || ((e instanceof BluetoothOperationTimeoutException) - && !mPreferences.getRetrySecretHandshakeTimeout())) { - throw new HandshakeException("Fail on handshake!", e); - } - if (rescueFromError != null) { - rescueFromError.accept( - (e instanceof BluetoothTimeoutException - || e instanceof BluetoothOperationTimeoutException) - ? ErrorCode.SUCCESS_RETRY_SECRET_HANDSHAKE_TIMEOUT - : ErrorCode.SUCCESS_RETRY_SECRET_HANDSHAKE_ERROR); - } - } - } while (mPreferences.getRetryGattConnectionAndSecretHandshake() - && handshakeResponse == null); - if (retryCount > 0) { - Log.i(TAG, "Secret handshake failed but restored by retry, retry count=" + retryCount); - } - String providerPublicAddress = - decodeResponse(keys.mSharedSecret, verifyNotNull(handshakeResponse)); - - return SharedSecret.create(keys.mSharedSecret, providerPublicAddress); - } - - @VisibleForTesting - long getTimeoutMs(long spentTime) { - if (!mPreferences.getRetryGattConnectionAndSecretHandshake()) { - return SECONDS.toMillis(mPreferences.getGattOperationTimeoutSeconds()); - } else { - return spentTime < mPreferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs() - ? mPreferences.getSecretHandshakeShortTimeoutMs() - : mPreferences.getSecretHandshakeLongTimeoutMs(); - } - } - - /** - * If the given key is an ecc-256 public key (currently, we are using secp256r1), the shared - * secret is generated by ECDH; if the input key is AES-128 key (should be the account key), - * then it is the shared secret. - */ - private Keys createKey(byte[] key) throws GeneralSecurityException { - if (key.length == EllipticCurveDiffieHellmanExchange.PUBLIC_KEY_LENGTH) { - EllipticCurveDiffieHellmanExchange exchange = EllipticCurveDiffieHellmanExchange - .create(); - byte[] publicKey = exchange.getPublicKey(); - if (publicKey != null) { - Log.i(TAG, "Handshake " + maskBluetoothAddress(mProviderBleAddress) - + ", generates key by ECDH."); - } else { - throw new GeneralSecurityException("Failed to do ECDH."); - } - return new Keys(exchange.generateSecret(key), publicKey); - } else if (key.length == AesEcbSingleBlockEncryption.KEY_LENGTH) { - Log.i(TAG, "Handshake " + maskBluetoothAddress(mProviderBleAddress) - + ", using the given secret."); - return new Keys(key, new byte[0]); - } else { - throw new GeneralSecurityException("Key length is not correct: " + key.length); - } - } - - private static byte[] createPacket(Keys keys, HandshakeMessage message) - throws GeneralSecurityException { - byte[] encryptedMessage = encrypt(keys.mSharedSecret, message.getBytes()); - return concat(encryptedMessage, keys.mPublicKey); - } - - private byte[] processGattCommunication(byte[] packet, long gattOperationTimeoutMS) - throws BluetoothException, InterruptedException, ExecutionException, TimeoutException { - BluetoothGattConnection gattConnection = mGattConnectionManager.getConnection(); - gattConnection.setOperationTimeout(gattOperationTimeoutMS); - UUID characteristicUuid = KeyBasedPairingCharacteristic.getId(gattConnection); - ChangeObserver changeObserver = - gattConnection.enableNotification(FastPairService.ID, characteristicUuid); - - Log.i(TAG, - "Writing handshake packet to address=" + maskBluetoothAddress(mProviderBleAddress)); - gattConnection.writeCharacteristic(FastPairService.ID, characteristicUuid, packet); - Log.i(TAG, "Waiting handshake packet from address=" + maskBluetoothAddress( - mProviderBleAddress)); - return changeObserver.waitForUpdate(gattOperationTimeoutMS); - } - - private String decodeResponse(byte[] sharedSecret, byte[] response) - throws PairingException, GeneralSecurityException { - if (response.length != AES_BLOCK_LENGTH) { - throw new PairingException( - "Handshake failed because of incorrect response: " + base16().encode(response)); - } - // 1 byte type, 6 bytes public address, remainder random salt. - byte[] decryptedResponse = decrypt(sharedSecret, response); - if (decryptedResponse[0] != KeyBasedPairingCharacteristic.Response.TYPE) { - throw new PairingException( - "Handshake response type incorrect: " + decryptedResponse[0]); - } - String address = BluetoothAddress.encode(Arrays.copyOfRange(decryptedResponse, 1, 7)); - Log.i(TAG, "Handshake success with public " + maskBluetoothAddress(address) + ", ble " - + maskBluetoothAddress(mProviderBleAddress)); - return address; - } - - /** - * The base class for handshake message that contains the common data: message type, flags and - * verification data. - */ - abstract static class HandshakeMessage { - - final byte mType; - final byte mFlags; - private final byte[] mVerificationData; - - HandshakeMessage(Builder builder) { - this.mType = builder.mType; - this.mVerificationData = builder.mVerificationData; - this.mFlags = builder.mFlags; - } - - abstract static class Builder> { - - byte mType; - byte mFlags; - private byte[] mVerificationData; - - abstract T getThis(); - - T setVerificationData(byte[] verificationData) { - if (verificationData.length != BLUETOOTH_ADDRESS_LENGTH) { - throw new IllegalArgumentException( - "Incorrect verification data length: " + verificationData.length + "."); - } - this.mVerificationData = verificationData; - return getThis(); - } - } - - /** - * Constructs the base handshake message according to the format of Fast Pair spec. - */ - byte[] constructBaseBytes() { - byte[] rawMessage = new byte[Request.SIZE]; - new SecureRandom().nextBytes(rawMessage); - rawMessage[TYPE_INDEX] = mType; - rawMessage[FLAGS_INDEX] = mFlags; - - System.arraycopy( - mVerificationData, - /* srcPos= */ 0, - rawMessage, - VERIFICATION_DATA_INDEX, - VERIFICATION_DATA_LENGTH); - return rawMessage; - } - - /** - * Returns the raw handshake message. - */ - abstract byte[] getBytes(); - } - - /** - * Extends {@link HandshakeMessage} and contains the required data for key-based pairing - * request. - */ - public static class KeyBasedPairingRequest extends HandshakeMessage { - - @Nullable - private final byte[] mSeekerPublicAddress; - - private KeyBasedPairingRequest(Builder builder) { - super(builder); - this.mSeekerPublicAddress = builder.mSeekerPublicAddress; - } - - @Override - byte[] getBytes() { - byte[] rawMessage = constructBaseBytes(); - if (mSeekerPublicAddress != null) { - System.arraycopy( - mSeekerPublicAddress, - /* srcPos= */ 0, - rawMessage, - SEEKER_PUBLIC_ADDRESS_INDEX, - BLUETOOTH_ADDRESS_LENGTH); - } - Log.i(TAG, - "Handshake Message: type (" + rawMessage[TYPE_INDEX] + "), flag (" - + rawMessage[FLAGS_INDEX] + ")."); - return rawMessage; - } - - /** - * Builder class for key-based pairing request. - */ - public static class Builder extends HandshakeMessage.Builder { - - @Nullable - private byte[] mSeekerPublicAddress; - - /** - * Adds flags without changing other flags. - */ - public Builder addFlag(@KeyBasedPairingRequestFlag int flag) { - this.mFlags |= (byte) flag; - return this; - } - - /** - * Set seeker's public address. - */ - public Builder setSeekerPublicAddress(byte[] seekerPublicAddress) { - this.mSeekerPublicAddress = seekerPublicAddress; - return this; - } - - /** - * Buulds KeyBasedPairigRequest. - */ - public KeyBasedPairingRequest build() { - mType = TYPE_KEY_BASED_PAIRING_REQUEST; - return new KeyBasedPairingRequest(this); - } - - @Override - Builder getThis() { - return this; - } - } - } - - /** - * Extends {@link HandshakeMessage} and contains the required data for action over BLE request. - */ - public static class ActionOverBle extends HandshakeMessage { - - private final byte mEventGroup; - private final byte mEventCode; - @Nullable - private final byte[] mEventData; - private final byte mAdditionalDataType; - - private ActionOverBle(Builder builder) { - super(builder); - this.mEventGroup = builder.mEventGroup; - this.mEventCode = builder.mEventCode; - this.mEventData = builder.mEventData; - this.mAdditionalDataType = builder.mAdditionalDataType; - } - - @Override - byte[] getBytes() { - byte[] rawMessage = constructBaseBytes(); - StringBuilder stringBuilder = - new StringBuilder( - String.format( - "type (%02X), flag (%02X)", rawMessage[TYPE_INDEX], - rawMessage[FLAGS_INDEX])); - if ((mFlags & (byte) DEVICE_ACTION) != 0) { - rawMessage[EVENT_GROUP_INDEX] = mEventGroup; - rawMessage[EVENT_CODE_INDEX] = mEventCode; - - if (mEventData != null) { - rawMessage[EVENT_ADDITIONAL_DATA_LENGTH_INDEX] = (byte) mEventData.length; - System.arraycopy( - mEventData, - /* srcPos= */ 0, - rawMessage, - EVENT_ADDITIONAL_DATA_INDEX, - mEventData.length); - } else { - rawMessage[EVENT_ADDITIONAL_DATA_LENGTH_INDEX] = (byte) 0; - } - stringBuilder.append( - String.format( - ", group(%02X), code(%02X), length(%02X)", - rawMessage[EVENT_GROUP_INDEX], - rawMessage[EVENT_CODE_INDEX], - rawMessage[EVENT_ADDITIONAL_DATA_LENGTH_INDEX])); - } - if ((mFlags & (byte) ADDITIONAL_DATA_CHARACTERISTIC) != 0) { - rawMessage[ADDITIONAL_DATA_TYPE_INDEX] = mAdditionalDataType; - stringBuilder.append( - String.format(", data id(%02X)", rawMessage[ADDITIONAL_DATA_TYPE_INDEX])); - } - Log.i(TAG, "Handshake Message: " + stringBuilder); - return rawMessage; - } - - /** - * Builder class for action over BLE request. - */ - public static class Builder extends HandshakeMessage.Builder { - - private byte mEventGroup; - private byte mEventCode; - @Nullable - private byte[] mEventData; - private byte mAdditionalDataType; - - // Adds a flag to this handshake message. This can be called repeatedly for adding - // different preference. - - /** - * Adds flag without changing other flags. - */ - public Builder addFlag(@ActionOverBleFlag int flag) { - this.mFlags |= (byte) flag; - return this; - } - - /** - * Set event group and event code. - */ - public Builder setEvent(int eventGroup, int eventCode) { - this.mFlags |= (byte) DEVICE_ACTION; - this.mEventGroup = (byte) (eventGroup & 0xFF); - this.mEventCode = (byte) (eventCode & 0xFF); - return this; - } - - /** - * Set event additional data. - */ - public Builder setEventAdditionalData(byte[] data) { - this.mEventData = data; - return this; - } - - /** - * Set event additional data type. - */ - public Builder setAdditionalDataType(@AdditionalDataType int additionalDataType) { - this.mFlags |= (byte) ADDITIONAL_DATA_CHARACTERISTIC; - this.mAdditionalDataType = (byte) additionalDataType; - return this; - } - - @Override - Builder getThis() { - return this; - } - - ActionOverBle build() { - mType = TYPE_ACTION_OVER_BLE; - return new ActionOverBle(this); - } - } - } - - /** - * Exception for handshake failure. - */ - public static class HandshakeException extends PairingException { - - private final BluetoothException mOriginalException; - - @VisibleForTesting - HandshakeException(String format, BluetoothException e) { - super(format); - mOriginalException = e; - } - - public BluetoothException getOriginalException() { - return mOriginalException; - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPiece.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPiece.java deleted file mode 100644 index 26ff79fab58a3a0e533026e5314f9a243c373ff2..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPiece.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.Nullable; -import androidx.core.content.FileProvider; - -import java.util.Arrays; -import java.util.Objects; - -/** - * This class is subclass of real headset. It contains image url, battery value and charging - * status. - */ -public class HeadsetPiece implements Parcelable { - private int mLowLevelThreshold; - private int mBatteryLevel; - private String mImageUrl; - private boolean mCharging; - private Uri mImageContentUri; - - private HeadsetPiece( - int lowLevelThreshold, - int batteryLevel, - String imageUrl, - boolean charging, - @Nullable Uri imageContentUri) { - this.mLowLevelThreshold = lowLevelThreshold; - this.mBatteryLevel = batteryLevel; - this.mImageUrl = imageUrl; - this.mCharging = charging; - this.mImageContentUri = imageContentUri; - } - - /** - * Returns a builder of HeadsetPiece. - */ - public static HeadsetPiece.Builder builder() { - return new HeadsetPiece.Builder(); - } - - /** - * The low level threshold. - */ - public int lowLevelThreshold() { - return mLowLevelThreshold; - } - - /** - * The battery level. - */ - public int batteryLevel() { - return mBatteryLevel; - } - - /** - * The web URL of the image. - */ - public String imageUrl() { - return mImageUrl; - } - - /** - * Whether the headset is charging. - */ - public boolean charging() { - return mCharging; - } - - /** - * The content Uri of the image if it could be downloaded from the web URL and generated through - * {@link FileProvider#getUriForFile} successfully, otherwise null. - */ - @Nullable - public Uri imageContentUri() { - return mImageContentUri; - } - - /** - * @return whether battery is low or not. - */ - public boolean isBatteryLow() { - return batteryLevel() <= lowLevelThreshold() && batteryLevel() >= 0 && !charging(); - } - - @Override - public String toString() { - return "HeadsetPiece{" - + "lowLevelThreshold=" + mLowLevelThreshold + ", " - + "batteryLevel=" + mBatteryLevel + ", " - + "imageUrl=" + mImageUrl + ", " - + "charging=" + mCharging + ", " - + "imageContentUri=" + mImageContentUri - + "}"; - } - - /** - * Builder function for headset piece. - */ - public static class Builder { - private int mLowLevelThreshold; - private int mBatteryLevel; - private String mImageUrl; - private boolean mCharging; - private Uri mImageContentUri; - - /** - * Set low level threshold. - */ - public HeadsetPiece.Builder setLowLevelThreshold(int lowLevelThreshold) { - this.mLowLevelThreshold = lowLevelThreshold; - return this; - } - - /** - * Set battery level. - */ - public HeadsetPiece.Builder setBatteryLevel(int level) { - this.mBatteryLevel = level; - return this; - } - - /** - * Set image url. - */ - public HeadsetPiece.Builder setImageUrl(String url) { - this.mImageUrl = url; - return this; - } - - /** - * Set charging. - */ - public HeadsetPiece.Builder setCharging(boolean charging) { - this.mCharging = charging; - return this; - } - - /** - * Set image content Uri. - */ - public HeadsetPiece.Builder setImageContentUri(Uri uri) { - this.mImageContentUri = uri; - return this; - } - - /** - * Builds HeadSetPiece. - */ - public HeadsetPiece build() { - return new HeadsetPiece(mLowLevelThreshold, mBatteryLevel, mImageUrl, mCharging, - mImageContentUri); - } - } - - @Override - public final void writeToParcel(Parcel dest, int flags) { - dest.writeString(imageUrl()); - dest.writeInt(lowLevelThreshold()); - dest.writeInt(batteryLevel()); - // Writes 1 if charging, otherwise 0. - dest.writeByte((byte) (charging() ? 1 : 0)); - dest.writeParcelable(imageContentUri(), flags); - } - - @Override - public final int describeContents() { - return 0; - } - - public static final Creator CREATOR = - new Creator() { - @Override - public HeadsetPiece createFromParcel(Parcel in) { - String imageUrl = in.readString(); - return HeadsetPiece.builder() - .setImageUrl(imageUrl != null ? imageUrl : "") - .setLowLevelThreshold(in.readInt()) - .setBatteryLevel(in.readInt()) - .setCharging(in.readByte() != 0) - .setImageContentUri(in.readParcelable(Uri.class.getClassLoader())) - .build(); - } - - @Override - public HeadsetPiece[] newArray(int size) { - return new HeadsetPiece[size]; - } - }; - - @Override - public final int hashCode() { - return Arrays.hashCode( - new Object[]{ - lowLevelThreshold(), batteryLevel(), imageUrl(), charging(), - imageContentUri() - }); - } - - @Override - public final boolean equals(@Nullable Object other) { - if (other == null) { - return false; - } - - if (this == other) { - return true; - } - - if (!(other instanceof HeadsetPiece)) { - return false; - } - - HeadsetPiece that = (HeadsetPiece) other; - return lowLevelThreshold() == that.lowLevelThreshold() - && batteryLevel() == that.batteryLevel() - && Objects.equals(imageUrl(), that.imageUrl()) - && charging() == that.charging() - && Objects.equals(imageContentUri(), that.imageContentUri()); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HmacSha256.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HmacSha256.java deleted file mode 100644 index cc7a300dbb3692a29adef4378ab71d91c3848878..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/HmacSha256.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesCtrMultipleBlockEncryption.KEY_LENGTH; - -import androidx.annotation.VisibleForTesting; - -import java.security.GeneralSecurityException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * HMAC-SHA256 utility used to generate key-SHA256 based message authentication code. This is - * specific for Fast Pair GATT connection exchanging data to verify both the data integrity and the - * authentication of a message. It is defined as: - * - *

      - *
    1. SHA256(concat((key ^ opad),SHA256(concat((key ^ ipad), data)))), where - *
    2. key is the given secret extended to 64 bytes by concat(secret, ZEROS). - *
    3. opad is 64 bytes outer padding, consisting of repeated bytes valued 0x5c. - *
    4. ipad is 64 bytes inner padding, consisting of repeated bytes valued 0x36. - *
    - * - */ -final class HmacSha256 { - @VisibleForTesting static final int HMAC_SHA256_BLOCK_SIZE = 64; - - private HmacSha256() {} - - /** - * Generates the HMAC for given parameters, this is specific for Fast Pair GATT connection - * exchanging data which is encrypted using AES-CTR. - * - * @param secret 16 bytes shared secret. - * @param data the data encrypted using AES-CTR and the given nonce. - * @return HMAC-SHA256 result. - */ - static byte[] build(byte[] secret, byte[] data) throws GeneralSecurityException { - // Currently we only accept AES-128 key here, the second check is to secure we won't - // modify KEY_LENGTH to > HMAC_SHA256_BLOCK_SIZE by mistake. - if (secret.length != KEY_LENGTH) { - throw new GeneralSecurityException("Incorrect key length, should be the AES-128 key."); - } - if (KEY_LENGTH > HMAC_SHA256_BLOCK_SIZE) { - throw new GeneralSecurityException("KEY_LENGTH > HMAC_SHA256_BLOCK_SIZE!"); - } - - return buildWith64BytesKey(secret, data); - } - - /** - * Generates the HMAC for given parameters, this is specific for Fast Pair GATT connection - * exchanging data which is encrypted using AES-CTR. - * - * @param secret 16 bytes shared secret. - * @param data the data encrypted using AES-CTR and the given nonce. - * @return HMAC-SHA256 result. - */ - static byte[] buildWith64BytesKey(byte[] secret, byte[] data) throws GeneralSecurityException { - if (secret.length > HMAC_SHA256_BLOCK_SIZE) { - throw new GeneralSecurityException("KEY_LENGTH > HMAC_SHA256_BLOCK_SIZE!"); - } - - Mac mac = Mac.getInstance("HmacSHA256"); - SecretKeySpec keySpec = new SecretKeySpec(secret, "HmacSHA256"); - mac.init(keySpec); - - return mac.doFinal(data); - } - - /** - * Constant-time HMAC comparison to prevent a possible timing attack, e.g. time the same MAC - * with all different first byte for a given ciphertext, the right one will take longer as it - * will fail on the second byte's verification. - * - * @param hmac1 HMAC want to be compared with. - * @param hmac2 HMAC want to be compared with. - * @return true if and ony if the give 2 HMACs are identical and non-null. - */ - static boolean compareTwoHMACs(byte[] hmac1, byte[] hmac2) { - if (hmac1 == null || hmac2 == null) { - return false; - } - - if (hmac1.length != hmac2.length) { - return false; - } - // This is for constant-time comparison, don't optimize it. - int res = 0; - for (int i = 0; i < hmac1.length; i++) { - res |= hmac1[i] ^ hmac2[i]; - } - return res == 0; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Ltv.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Ltv.java deleted file mode 100644 index 88c9484d34a1ef1d6c7b17f07ab157a0a2789960..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Ltv.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.google.common.io.BaseEncoding.base16; - -import com.google.common.primitives.Bytes; -import com.google.errorprone.annotations.FormatMethod; -import com.google.errorprone.annotations.FormatString; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A length, type, value (LTV) data block. - */ -public class Ltv { - - private static final int SIZE_OF_LEN_TYPE = 2; - - final byte mType; - final byte[] mValue; - - /** - * Thrown if there's an error during {@link #parse}. - */ - public static class ParseException extends Exception { - - @FormatMethod - private ParseException(@FormatString String format, Object... objects) { - super(String.format(format, objects)); - } - } - - /** - * Constructor. - */ - public Ltv(byte type, byte... value) { - this.mType = type; - this.mValue = value; - } - - /** - * Parses a list of LTV blocks out of the input byte block. - */ - static List parse(byte[] bytes) throws ParseException { - List ltvs = new ArrayList<>(); - // The "+ 2" is for the length and type bytes. - for (int valueLength, i = 0; i < bytes.length; i += SIZE_OF_LEN_TYPE + valueLength) { - // - 1 since the length in the packet includes the type byte. - valueLength = bytes[i] - 1; - if (valueLength < 0 || bytes.length < i + SIZE_OF_LEN_TYPE + valueLength) { - throw new ParseException( - "Wrong length=%d at index=%d in LTVs=%s", bytes[i], i, - base16().encode(bytes)); - } - ltvs.add(new Ltv(bytes[i + 1], Arrays.copyOfRange(bytes, i + SIZE_OF_LEN_TYPE, - i + SIZE_OF_LEN_TYPE + valueLength))); - } - return ltvs; - } - - /** - * Returns an LTV block, where length is mValue.length + 1 (for the type byte). - */ - public byte[] getBytes() { - return Bytes.concat(new byte[]{(byte) (mValue.length + 1), mType}, mValue); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/MessageStreamHmacEncoder.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/MessageStreamHmacEncoder.java deleted file mode 100644 index b04cf7352afb3f45d1e705007bd7b4f01be72f2c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/MessageStreamHmacEncoder.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesCtrMultipleBlockEncryption.generateNonce; - -import static com.google.common.primitives.Bytes.concat; - -import java.security.GeneralSecurityException; -import java.util.Arrays; - -/** - * Message stream utilities for encoding raw packet with HMAC. - * - *

    Encoded packet is: - * - *

      - *
    1. Packet[0 - (data length - 1)]: the raw data. - *
    2. Packet[data length - (data length + 7)]: the 8-byte message nonce. - *
    3. Packet[(data length + 8) - (data length + 15)]: the 8-byte of HMAC. - *
    - */ -public class MessageStreamHmacEncoder { - public static final int EXTRACT_HMAC_SIZE = 8; - public static final int SECTION_NONCE_LENGTH = 8; - - private MessageStreamHmacEncoder() {} - - /** Encodes Message Packet. */ - public static byte[] encodeMessagePacket(byte[] accountKey, byte[] sectionNonce, byte[] data) - throws GeneralSecurityException { - checkAccountKeyAndSectionNonce(accountKey, sectionNonce); - - if (data == null || data.length == 0) { - throw new GeneralSecurityException("No input data for encodeMessagePacket"); - } - - byte[] messageNonce = generateNonce(); - byte[] extractedHmac = - Arrays.copyOf( - HmacSha256.buildWith64BytesKey( - accountKey, concat(sectionNonce, messageNonce, data)), - EXTRACT_HMAC_SIZE); - - return concat(data, messageNonce, extractedHmac); - } - - /** Verifies Hmac. */ - public static boolean verifyHmac(byte[] accountKey, byte[] sectionNonce, byte[] data) - throws GeneralSecurityException { - checkAccountKeyAndSectionNonce(accountKey, sectionNonce); - if (data == null) { - throw new GeneralSecurityException("data is null"); - } - if (data.length <= EXTRACT_HMAC_SIZE + SECTION_NONCE_LENGTH) { - throw new GeneralSecurityException("data.length too short"); - } - - byte[] hmac = Arrays.copyOfRange(data, data.length - EXTRACT_HMAC_SIZE, data.length); - byte[] messageNonce = - Arrays.copyOfRange( - data, - data.length - EXTRACT_HMAC_SIZE - SECTION_NONCE_LENGTH, - data.length - EXTRACT_HMAC_SIZE); - byte[] rawData = Arrays.copyOf( - data, data.length - EXTRACT_HMAC_SIZE - SECTION_NONCE_LENGTH); - return Arrays.equals( - Arrays.copyOf( - HmacSha256.buildWith64BytesKey( - accountKey, concat(sectionNonce, messageNonce, rawData)), - EXTRACT_HMAC_SIZE), - hmac); - } - - private static void checkAccountKeyAndSectionNonce(byte[] accountKey, byte[] sectionNonce) - throws GeneralSecurityException { - if (accountKey == null || accountKey.length == 0) { - throw new GeneralSecurityException( - "Incorrect accountKey for encoding message packet, accountKey.length = " - + (accountKey == null ? "NULL" : accountKey.length)); - } - - if (sectionNonce == null || sectionNonce.length != SECTION_NONCE_LENGTH) { - throw new GeneralSecurityException( - "Incorrect sectionNonce for encoding message packet, sectionNonce.length = " - + (sectionNonce == null ? "NULL" : sectionNonce.length)); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/NamingEncoder.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/NamingEncoder.java deleted file mode 100644 index 1521be6033871485e2ed85c1048bf453a00538bf..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/NamingEncoder.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.AesCtrMultipleBlockEncryption.NONCE_SIZE; - -import static com.google.common.primitives.Bytes.concat; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import android.annotation.TargetApi; -import android.os.Build.VERSION_CODES; - -import com.google.common.base.Utf8; - -import java.security.GeneralSecurityException; -import java.util.Arrays; - -/** - * Naming utilities for encoding naming packet, decoding naming packet and verifying both the data - * integrity and the authentication of a message by checking HMAC. - * - *

    Naming packet is: - * - *

      - *
    1. Naming_Packet[0 - 7]: the first 8-byte of HMAC. - *
    2. Naming_Packet[8 - var]: the encrypted name (with 8-byte nonce appended to the front). - *
    - */ -@TargetApi(VERSION_CODES.M) -public final class NamingEncoder { - - static final int EXTRACT_HMAC_SIZE = 8; - static final int MAX_LENGTH_OF_NAME = 48; - - private NamingEncoder() { - } - - /** - * Encodes the name to naming packet by the given secret. - * - * @param secret AES-128 key for encryption. - * @param name the given name to be encoded. - * @return the encrypted data with the 8-byte extracted HMAC appended to the front. - * @throws GeneralSecurityException if the given key or name is invalid for encoding. - */ - public static byte[] encodeNamingPacket(byte[] secret, String name) - throws GeneralSecurityException { - if (secret == null || secret.length != AesCtrMultipleBlockEncryption.KEY_LENGTH) { - throw new GeneralSecurityException( - "Incorrect secret for encoding name packet, secret.length = " - + (secret == null ? "NULL" : secret.length)); - } - - if ((name == null) || (name.length() == 0) || (Utf8.encodedLength(name) - > MAX_LENGTH_OF_NAME)) { - throw new GeneralSecurityException( - "Invalid name for encoding name packet, Utf8.encodedLength(name) = " - + (name == null ? "NULL" : Utf8.encodedLength(name))); - } - - byte[] encryptedData = AesCtrMultipleBlockEncryption.encrypt(secret, name.getBytes(UTF_8)); - byte[] extractedHmac = - Arrays.copyOf(HmacSha256.build(secret, encryptedData), EXTRACT_HMAC_SIZE); - - return concat(extractedHmac, encryptedData); - } - - /** - * Decodes the name from naming packet by the given secret. - * - * @param secret AES-128 key used in the encryption to decrypt data. - * @param namingPacket naming packet which is encoded by the given secret.. - * @return the name decoded from the given packet. - * @throws GeneralSecurityException if the given key or naming packet is invalid for decoding. - */ - public static String decodeNamingPacket(byte[] secret, byte[] namingPacket) - throws GeneralSecurityException { - if (secret == null || secret.length != AesCtrMultipleBlockEncryption.KEY_LENGTH) { - throw new GeneralSecurityException( - "Incorrect secret for decoding name packet, secret.length = " - + (secret == null ? "NULL" : secret.length)); - } - if (namingPacket == null - || namingPacket.length <= EXTRACT_HMAC_SIZE - || namingPacket.length > (MAX_LENGTH_OF_NAME + EXTRACT_HMAC_SIZE + NONCE_SIZE)) { - throw new GeneralSecurityException( - "Naming packet size is incorrect, namingPacket.length is " - + (namingPacket == null ? "NULL" : namingPacket.length)); - } - - if (!verifyHmac(secret, namingPacket)) { - throw new GeneralSecurityException( - "Verify HMAC failed, could be incorrect key or naming packet."); - } - byte[] encryptedData = Arrays - .copyOfRange(namingPacket, EXTRACT_HMAC_SIZE, namingPacket.length); - return new String(AesCtrMultipleBlockEncryption.decrypt(secret, encryptedData), UTF_8); - } - - // Computes the HMAC of the given key and name, and compares the first 8-byte of the HMAC result - // with the one from name packet. Must call constant-time comparison to prevent a possible - // timing attack, e.g. time the same MAC with all different first byte for a given ciphertext, - // the right one will take longer as it will fail on the second byte's verification. - private static boolean verifyHmac(byte[] key, byte[] namingPacket) - throws GeneralSecurityException { - byte[] packetHmac = Arrays.copyOfRange(namingPacket, /* from= */ 0, EXTRACT_HMAC_SIZE); - byte[] encryptedData = Arrays - .copyOfRange(namingPacket, EXTRACT_HMAC_SIZE, namingPacket.length); - byte[] computedHmac = Arrays - .copyOf(HmacSha256.build(key, encryptedData), EXTRACT_HMAC_SIZE); - - return HmacSha256.compareTwoHMACs(packetHmac, computedHmac); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingException.java deleted file mode 100644 index 722dc85c8320cdc55a0ca8be5244cb11a90de74c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -/** Base class for pairing exceptions. */ -// TODO(b/200594968): convert exceptions into error codes to save memory. -public class PairingException extends Exception { - PairingException(String format, Object... objects) { - super(String.format(format, objects)); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java deleted file mode 100644 index 270cb42fa7f9322d1a8a7b5ebaeba3b8a6163cdf..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListener.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import androidx.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Callback interface for pairing progress. */ -public interface PairingProgressListener { - - /** Fast Pair Bond State. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - PairingEvent.START, - PairingEvent.SUCCESS, - PairingEvent.FAILED, - PairingEvent.UNKNOWN, - }) - public @interface PairingEvent { - int START = 0; - int SUCCESS = 1; - int FAILED = 2; - int UNKNOWN = 3; - } - - /** Returns enum based on the ordinal index. */ - static @PairingEvent int fromOrdinal(int ordinal) { - switch (ordinal) { - case 0: - return PairingEvent.START; - case 1: - return PairingEvent.SUCCESS; - case 2: - return PairingEvent.FAILED; - case 3: - return PairingEvent.UNKNOWN; - default: - return PairingEvent.UNKNOWN; - } - } - - /** Callback function upon pairing progress update. */ - void onPairingProgressUpdating(@PairingEvent int event, String message); -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PasskeyConfirmationHandler.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PasskeyConfirmationHandler.java deleted file mode 100644 index f5807a3254f03ddb3f812e7b1a04358f10e5ec61..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/PasskeyConfirmationHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.bluetooth.BluetoothDevice; - -/** Interface for getting the passkey confirmation request. */ -public interface PasskeyConfirmationHandler { - /** Called when getting the passkey confirmation request while pairing. */ - void onPasskeyConfirmation(BluetoothDevice device, int passkey); -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java deleted file mode 100644 index bb7b71b79626f3dd299e4c16e0a48779607a8b59..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java +++ /dev/null @@ -1,2309 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.get16BitUuid; - -import androidx.annotation.Nullable; - -import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.FirmwareVersionCharacteristic; - -import com.google.common.collect.ImmutableSet; -import com.google.common.primitives.Shorts; - -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.Objects; - -/** - * Preferences that tweak the Fast Pairing process: timeouts, number of retries... All preferences - * have default values which should be reasonable for all clients. - */ -public class Preferences { - - private final int mGattOperationTimeoutSeconds; - private final int mGattConnectionTimeoutSeconds; - private final int mBluetoothToggleTimeoutSeconds; - private final int mBluetoothToggleSleepSeconds; - private final int mClassicDiscoveryTimeoutSeconds; - private final int mNumDiscoverAttempts; - private final int mDiscoveryRetrySleepSeconds; - private final boolean mIgnoreDiscoveryError; - private final int mSdpTimeoutSeconds; - private final int mNumSdpAttempts; - private final int mNumCreateBondAttempts; - private final int mNumConnectAttempts; - private final int mNumWriteAccountKeyAttempts; - private final boolean mToggleBluetoothOnFailure; - private final boolean mBluetoothStateUsesPolling; - private final int mBluetoothStatePollingMillis; - private final int mNumAttempts; - private final boolean mEnableBrEdrHandover; - private final short mBrHandoverDataCharacteristicId; - private final short mBluetoothSigDataCharacteristicId; - private final short mFirmwareVersionCharacteristicId; - private final short mBrTransportBlockDataDescriptorId; - private final boolean mWaitForUuidsAfterBonding; - private final boolean mReceiveUuidsAndBondedEventBeforeClose; - private final int mRemoveBondTimeoutSeconds; - private final int mRemoveBondSleepMillis; - private final int mCreateBondTimeoutSeconds; - private final int mHidCreateBondTimeoutSeconds; - private final int mProxyTimeoutSeconds; - private final boolean mRejectPhonebookAccess; - private final boolean mRejectMessageAccess; - private final boolean mRejectSimAccess; - private final int mWriteAccountKeySleepMillis; - private final boolean mSkipDisconnectingGattBeforeWritingAccountKey; - private final boolean mMoreEventLogForQuality; - private final boolean mRetryGattConnectionAndSecretHandshake; - private final long mGattConnectShortTimeoutMs; - private final long mGattConnectLongTimeoutMs; - private final long mGattConnectShortTimeoutRetryMaxSpentTimeMs; - private final long mAddressRotateRetryMaxSpentTimeMs; - private final long mPairingRetryDelayMs; - private final long mSecretHandshakeShortTimeoutMs; - private final long mSecretHandshakeLongTimeoutMs; - private final long mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs; - private final long mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs; - private final long mSecretHandshakeRetryAttempts; - private final long mSecretHandshakeRetryGattConnectionMaxSpentTimeMs; - private final long mSignalLostRetryMaxSpentTimeMs; - private final ImmutableSet mGattConnectionAndSecretHandshakeNoRetryGattError; - private final boolean mRetrySecretHandshakeTimeout; - private final boolean mLogUserManualRetry; - private final int mPairFailureCounts; - private final String mCachedDeviceAddress; - private final String mPossibleCachedDeviceAddress; - private final int mSameModelIdPairedDeviceCount; - private final boolean mIsDeviceFinishCheckAddressFromCache; - private final boolean mLogPairWithCachedModelId; - private final boolean mDirectConnectProfileIfModelIdInCache; - private final boolean mAcceptPasskey; - private final byte[] mSupportedProfileUuids; - private final boolean mProviderInitiatesBondingIfSupported; - private final boolean mAttemptDirectConnectionWhenPreviouslyBonded; - private final boolean mAutomaticallyReconnectGattWhenNeeded; - private final boolean mSkipConnectingProfiles; - private final boolean mIgnoreUuidTimeoutAfterBonded; - private final boolean mSpecifyCreateBondTransportType; - private final int mCreateBondTransportType; - private final boolean mIncreaseIntentFilterPriority; - private final boolean mEvaluatePerformance; - private final Preferences.ExtraLoggingInformation mExtraLoggingInformation; - private final boolean mEnableNamingCharacteristic; - private final boolean mEnableFirmwareVersionCharacteristic; - private final boolean mKeepSameAccountKeyWrite; - private final boolean mIsRetroactivePairing; - private final int mNumSdpAttemptsAfterBonded; - private final boolean mSupportHidDevice; - private final boolean mEnablePairingWhileDirectlyConnecting; - private final boolean mAcceptConsentForFastPairOne; - private final int mGattConnectRetryTimeoutMillis; - private final boolean mEnable128BitCustomGattCharacteristicsId; - private final boolean mEnableSendExceptionStepToValidator; - private final boolean mEnableAdditionalDataTypeWhenActionOverBle; - private final boolean mCheckBondStateWhenSkipConnectingProfiles; - private final boolean mHandlePasskeyConfirmationByUi; - private final boolean mEnablePairFlowShowUiWithoutProfileConnection; - - private Preferences( - int gattOperationTimeoutSeconds, - int gattConnectionTimeoutSeconds, - int bluetoothToggleTimeoutSeconds, - int bluetoothToggleSleepSeconds, - int classicDiscoveryTimeoutSeconds, - int numDiscoverAttempts, - int discoveryRetrySleepSeconds, - boolean ignoreDiscoveryError, - int sdpTimeoutSeconds, - int numSdpAttempts, - int numCreateBondAttempts, - int numConnectAttempts, - int numWriteAccountKeyAttempts, - boolean toggleBluetoothOnFailure, - boolean bluetoothStateUsesPolling, - int bluetoothStatePollingMillis, - int numAttempts, - boolean enableBrEdrHandover, - short brHandoverDataCharacteristicId, - short bluetoothSigDataCharacteristicId, - short firmwareVersionCharacteristicId, - short brTransportBlockDataDescriptorId, - boolean waitForUuidsAfterBonding, - boolean receiveUuidsAndBondedEventBeforeClose, - int removeBondTimeoutSeconds, - int removeBondSleepMillis, - int createBondTimeoutSeconds, - int hidCreateBondTimeoutSeconds, - int proxyTimeoutSeconds, - boolean rejectPhonebookAccess, - boolean rejectMessageAccess, - boolean rejectSimAccess, - int writeAccountKeySleepMillis, - boolean skipDisconnectingGattBeforeWritingAccountKey, - boolean moreEventLogForQuality, - boolean retryGattConnectionAndSecretHandshake, - long gattConnectShortTimeoutMs, - long gattConnectLongTimeoutMs, - long gattConnectShortTimeoutRetryMaxSpentTimeMs, - long addressRotateRetryMaxSpentTimeMs, - long pairingRetryDelayMs, - long secretHandshakeShortTimeoutMs, - long secretHandshakeLongTimeoutMs, - long secretHandshakeShortTimeoutRetryMaxSpentTimeMs, - long secretHandshakeLongTimeoutRetryMaxSpentTimeMs, - long secretHandshakeRetryAttempts, - long secretHandshakeRetryGattConnectionMaxSpentTimeMs, - long signalLostRetryMaxSpentTimeMs, - ImmutableSet gattConnectionAndSecretHandshakeNoRetryGattError, - boolean retrySecretHandshakeTimeout, - boolean logUserManualRetry, - int pairFailureCounts, - String cachedDeviceAddress, - String possibleCachedDeviceAddress, - int sameModelIdPairedDeviceCount, - boolean isDeviceFinishCheckAddressFromCache, - boolean logPairWithCachedModelId, - boolean directConnectProfileIfModelIdInCache, - boolean acceptPasskey, - byte[] supportedProfileUuids, - boolean providerInitiatesBondingIfSupported, - boolean attemptDirectConnectionWhenPreviouslyBonded, - boolean automaticallyReconnectGattWhenNeeded, - boolean skipConnectingProfiles, - boolean ignoreUuidTimeoutAfterBonded, - boolean specifyCreateBondTransportType, - int createBondTransportType, - boolean increaseIntentFilterPriority, - boolean evaluatePerformance, - @Nullable Preferences.ExtraLoggingInformation extraLoggingInformation, - boolean enableNamingCharacteristic, - boolean enableFirmwareVersionCharacteristic, - boolean keepSameAccountKeyWrite, - boolean isRetroactivePairing, - int numSdpAttemptsAfterBonded, - boolean supportHidDevice, - boolean enablePairingWhileDirectlyConnecting, - boolean acceptConsentForFastPairOne, - int gattConnectRetryTimeoutMillis, - boolean enable128BitCustomGattCharacteristicsId, - boolean enableSendExceptionStepToValidator, - boolean enableAdditionalDataTypeWhenActionOverBle, - boolean checkBondStateWhenSkipConnectingProfiles, - boolean handlePasskeyConfirmationByUi, - boolean enablePairFlowShowUiWithoutProfileConnection) { - this.mGattOperationTimeoutSeconds = gattOperationTimeoutSeconds; - this.mGattConnectionTimeoutSeconds = gattConnectionTimeoutSeconds; - this.mBluetoothToggleTimeoutSeconds = bluetoothToggleTimeoutSeconds; - this.mBluetoothToggleSleepSeconds = bluetoothToggleSleepSeconds; - this.mClassicDiscoveryTimeoutSeconds = classicDiscoveryTimeoutSeconds; - this.mNumDiscoverAttempts = numDiscoverAttempts; - this.mDiscoveryRetrySleepSeconds = discoveryRetrySleepSeconds; - this.mIgnoreDiscoveryError = ignoreDiscoveryError; - this.mSdpTimeoutSeconds = sdpTimeoutSeconds; - this.mNumSdpAttempts = numSdpAttempts; - this.mNumCreateBondAttempts = numCreateBondAttempts; - this.mNumConnectAttempts = numConnectAttempts; - this.mNumWriteAccountKeyAttempts = numWriteAccountKeyAttempts; - this.mToggleBluetoothOnFailure = toggleBluetoothOnFailure; - this.mBluetoothStateUsesPolling = bluetoothStateUsesPolling; - this.mBluetoothStatePollingMillis = bluetoothStatePollingMillis; - this.mNumAttempts = numAttempts; - this.mEnableBrEdrHandover = enableBrEdrHandover; - this.mBrHandoverDataCharacteristicId = brHandoverDataCharacteristicId; - this.mBluetoothSigDataCharacteristicId = bluetoothSigDataCharacteristicId; - this.mFirmwareVersionCharacteristicId = firmwareVersionCharacteristicId; - this.mBrTransportBlockDataDescriptorId = brTransportBlockDataDescriptorId; - this.mWaitForUuidsAfterBonding = waitForUuidsAfterBonding; - this.mReceiveUuidsAndBondedEventBeforeClose = receiveUuidsAndBondedEventBeforeClose; - this.mRemoveBondTimeoutSeconds = removeBondTimeoutSeconds; - this.mRemoveBondSleepMillis = removeBondSleepMillis; - this.mCreateBondTimeoutSeconds = createBondTimeoutSeconds; - this.mHidCreateBondTimeoutSeconds = hidCreateBondTimeoutSeconds; - this.mProxyTimeoutSeconds = proxyTimeoutSeconds; - this.mRejectPhonebookAccess = rejectPhonebookAccess; - this.mRejectMessageAccess = rejectMessageAccess; - this.mRejectSimAccess = rejectSimAccess; - this.mWriteAccountKeySleepMillis = writeAccountKeySleepMillis; - this.mSkipDisconnectingGattBeforeWritingAccountKey = - skipDisconnectingGattBeforeWritingAccountKey; - this.mMoreEventLogForQuality = moreEventLogForQuality; - this.mRetryGattConnectionAndSecretHandshake = retryGattConnectionAndSecretHandshake; - this.mGattConnectShortTimeoutMs = gattConnectShortTimeoutMs; - this.mGattConnectLongTimeoutMs = gattConnectLongTimeoutMs; - this.mGattConnectShortTimeoutRetryMaxSpentTimeMs = - gattConnectShortTimeoutRetryMaxSpentTimeMs; - this.mAddressRotateRetryMaxSpentTimeMs = addressRotateRetryMaxSpentTimeMs; - this.mPairingRetryDelayMs = pairingRetryDelayMs; - this.mSecretHandshakeShortTimeoutMs = secretHandshakeShortTimeoutMs; - this.mSecretHandshakeLongTimeoutMs = secretHandshakeLongTimeoutMs; - this.mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs = - secretHandshakeShortTimeoutRetryMaxSpentTimeMs; - this.mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs = - secretHandshakeLongTimeoutRetryMaxSpentTimeMs; - this.mSecretHandshakeRetryAttempts = secretHandshakeRetryAttempts; - this.mSecretHandshakeRetryGattConnectionMaxSpentTimeMs = - secretHandshakeRetryGattConnectionMaxSpentTimeMs; - this.mSignalLostRetryMaxSpentTimeMs = signalLostRetryMaxSpentTimeMs; - this.mGattConnectionAndSecretHandshakeNoRetryGattError = - gattConnectionAndSecretHandshakeNoRetryGattError; - this.mRetrySecretHandshakeTimeout = retrySecretHandshakeTimeout; - this.mLogUserManualRetry = logUserManualRetry; - this.mPairFailureCounts = pairFailureCounts; - this.mCachedDeviceAddress = cachedDeviceAddress; - this.mPossibleCachedDeviceAddress = possibleCachedDeviceAddress; - this.mSameModelIdPairedDeviceCount = sameModelIdPairedDeviceCount; - this.mIsDeviceFinishCheckAddressFromCache = isDeviceFinishCheckAddressFromCache; - this.mLogPairWithCachedModelId = logPairWithCachedModelId; - this.mDirectConnectProfileIfModelIdInCache = directConnectProfileIfModelIdInCache; - this.mAcceptPasskey = acceptPasskey; - this.mSupportedProfileUuids = supportedProfileUuids; - this.mProviderInitiatesBondingIfSupported = providerInitiatesBondingIfSupported; - this.mAttemptDirectConnectionWhenPreviouslyBonded = - attemptDirectConnectionWhenPreviouslyBonded; - this.mAutomaticallyReconnectGattWhenNeeded = automaticallyReconnectGattWhenNeeded; - this.mSkipConnectingProfiles = skipConnectingProfiles; - this.mIgnoreUuidTimeoutAfterBonded = ignoreUuidTimeoutAfterBonded; - this.mSpecifyCreateBondTransportType = specifyCreateBondTransportType; - this.mCreateBondTransportType = createBondTransportType; - this.mIncreaseIntentFilterPriority = increaseIntentFilterPriority; - this.mEvaluatePerformance = evaluatePerformance; - this.mExtraLoggingInformation = extraLoggingInformation; - this.mEnableNamingCharacteristic = enableNamingCharacteristic; - this.mEnableFirmwareVersionCharacteristic = enableFirmwareVersionCharacteristic; - this.mKeepSameAccountKeyWrite = keepSameAccountKeyWrite; - this.mIsRetroactivePairing = isRetroactivePairing; - this.mNumSdpAttemptsAfterBonded = numSdpAttemptsAfterBonded; - this.mSupportHidDevice = supportHidDevice; - this.mEnablePairingWhileDirectlyConnecting = enablePairingWhileDirectlyConnecting; - this.mAcceptConsentForFastPairOne = acceptConsentForFastPairOne; - this.mGattConnectRetryTimeoutMillis = gattConnectRetryTimeoutMillis; - this.mEnable128BitCustomGattCharacteristicsId = enable128BitCustomGattCharacteristicsId; - this.mEnableSendExceptionStepToValidator = enableSendExceptionStepToValidator; - this.mEnableAdditionalDataTypeWhenActionOverBle = enableAdditionalDataTypeWhenActionOverBle; - this.mCheckBondStateWhenSkipConnectingProfiles = checkBondStateWhenSkipConnectingProfiles; - this.mHandlePasskeyConfirmationByUi = handlePasskeyConfirmationByUi; - this.mEnablePairFlowShowUiWithoutProfileConnection = - enablePairFlowShowUiWithoutProfileConnection; - } - - /** - * Timeout for each GATT operation (not for the whole pairing process). - */ - public int getGattOperationTimeoutSeconds() { - return mGattOperationTimeoutSeconds; - } - - /** - * Timeout for Gatt connection operation. - */ - public int getGattConnectionTimeoutSeconds() { - return mGattConnectionTimeoutSeconds; - } - - /** - * Timeout for Bluetooth toggle. - */ - public int getBluetoothToggleTimeoutSeconds() { - return mBluetoothToggleTimeoutSeconds; - } - - /** - * Sleep time for Bluetooth toggle. - */ - public int getBluetoothToggleSleepSeconds() { - return mBluetoothToggleSleepSeconds; - } - - /** - * Timeout for classic discovery. - */ - public int getClassicDiscoveryTimeoutSeconds() { - return mClassicDiscoveryTimeoutSeconds; - } - - /** - * Number of discovery attempts allowed. - */ - public int getNumDiscoverAttempts() { - return mNumDiscoverAttempts; - } - - /** - * Sleep time between discovery retry. - */ - public int getDiscoveryRetrySleepSeconds() { - return mDiscoveryRetrySleepSeconds; - } - - /** - * Whether to ignore error incurred during discovery. - */ - public boolean getIgnoreDiscoveryError() { - return mIgnoreDiscoveryError; - } - - /** - * Timeout for Sdp. - */ - public int getSdpTimeoutSeconds() { - return mSdpTimeoutSeconds; - } - - /** - * Number of Sdp attempts allowed. - */ - public int getNumSdpAttempts() { - return mNumSdpAttempts; - } - - /** - * Number of create bond attempts allowed. - */ - public int getNumCreateBondAttempts() { - return mNumCreateBondAttempts; - } - - /** - * Number of connect attempts allowed. - */ - public int getNumConnectAttempts() { - return mNumConnectAttempts; - } - - /** - * Number of write account key attempts allowed. - */ - public int getNumWriteAccountKeyAttempts() { - return mNumWriteAccountKeyAttempts; - } - - /** - * Returns whether it is OK toggle bluetooth to retry upon failure. - */ - public boolean getToggleBluetoothOnFailure() { - return mToggleBluetoothOnFailure; - } - - /** - * Whether to get Bluetooth state using polling. - */ - public boolean getBluetoothStateUsesPolling() { - return mBluetoothStateUsesPolling; - } - - /** - * Polling time when retrieving Bluetooth state. - */ - public int getBluetoothStatePollingMillis() { - return mBluetoothStatePollingMillis; - } - - /** - * The number of times to attempt a generic operation, before giving up. - */ - public int getNumAttempts() { - return mNumAttempts; - } - - /** - * Returns whether BrEdr handover is enabled. - */ - public boolean getEnableBrEdrHandover() { - return mEnableBrEdrHandover; - } - - /** - * Returns characteristic Id for Br Handover data. - */ - public short getBrHandoverDataCharacteristicId() { - return mBrHandoverDataCharacteristicId; - } - - /** - * Returns characteristic Id for Bluethoth Sig data. - */ - public short getBluetoothSigDataCharacteristicId() { - return mBluetoothSigDataCharacteristicId; - } - - /** - * Returns characteristic Id for Firmware version. - */ - public short getFirmwareVersionCharacteristicId() { - return mFirmwareVersionCharacteristicId; - } - - /** - * Returns descripter Id for Br transport block data. - */ - public short getBrTransportBlockDataDescriptorId() { - return mBrTransportBlockDataDescriptorId; - } - - /** - * Whether to wait for Uuids after bonding. - */ - public boolean getWaitForUuidsAfterBonding() { - return mWaitForUuidsAfterBonding; - } - - /** - * Whether to get received Uuids and bonded events before close. - */ - public boolean getReceiveUuidsAndBondedEventBeforeClose() { - return mReceiveUuidsAndBondedEventBeforeClose; - } - - /** - * Timeout for remove bond operation. - */ - public int getRemoveBondTimeoutSeconds() { - return mRemoveBondTimeoutSeconds; - } - - /** - * Sleep time for remove bond operation. - */ - public int getRemoveBondSleepMillis() { - return mRemoveBondSleepMillis; - } - - /** - * This almost always succeeds (or fails) in 2-10 seconds (Taimen running O -> Nexus 6P sim). - */ - public int getCreateBondTimeoutSeconds() { - return mCreateBondTimeoutSeconds; - } - - /** - * Timeout for creating bond with Hid devices. - */ - public int getHidCreateBondTimeoutSeconds() { - return mHidCreateBondTimeoutSeconds; - } - - /** - * Timeout for get proxy operation. - */ - public int getProxyTimeoutSeconds() { - return mProxyTimeoutSeconds; - } - - /** - * Whether to reject phone book access. - */ - public boolean getRejectPhonebookAccess() { - return mRejectPhonebookAccess; - } - - /** - * Whether to reject message access. - */ - public boolean getRejectMessageAccess() { - return mRejectMessageAccess; - } - - /** - * Whether to reject sim access. - */ - public boolean getRejectSimAccess() { - return mRejectSimAccess; - } - - /** - * Sleep time for write account key operation. - */ - public int getWriteAccountKeySleepMillis() { - return mWriteAccountKeySleepMillis; - } - - /** - * Whether to skip disconneting gatt before writing account key. - */ - public boolean getSkipDisconnectingGattBeforeWritingAccountKey() { - return mSkipDisconnectingGattBeforeWritingAccountKey; - } - - /** - * Whether to get more event log for quality improvement. - */ - public boolean getMoreEventLogForQuality() { - return mMoreEventLogForQuality; - } - - /** - * Whether to retry gatt connection and secrete handshake. - */ - public boolean getRetryGattConnectionAndSecretHandshake() { - return mRetryGattConnectionAndSecretHandshake; - } - - /** - * Short Gatt connection timeoout. - */ - public long getGattConnectShortTimeoutMs() { - return mGattConnectShortTimeoutMs; - } - - /** - * Long Gatt connection timeout. - */ - public long getGattConnectLongTimeoutMs() { - return mGattConnectLongTimeoutMs; - } - - /** - * Short Timeout for Gatt connection, including retry. - */ - public long getGattConnectShortTimeoutRetryMaxSpentTimeMs() { - return mGattConnectShortTimeoutRetryMaxSpentTimeMs; - } - - /** - * Timeout for address rotation, including retry. - */ - public long getAddressRotateRetryMaxSpentTimeMs() { - return mAddressRotateRetryMaxSpentTimeMs; - } - - /** - * Returns pairing retry delay time. - */ - public long getPairingRetryDelayMs() { - return mPairingRetryDelayMs; - } - - /** - * Short timeout for secrete handshake. - */ - public long getSecretHandshakeShortTimeoutMs() { - return mSecretHandshakeShortTimeoutMs; - } - - /** - * Long timeout for secret handshake. - */ - public long getSecretHandshakeLongTimeoutMs() { - return mSecretHandshakeLongTimeoutMs; - } - - /** - * Short timeout for secret handshake, including retry. - */ - public long getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs() { - return mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs; - } - - /** - * Long timeout for secret handshake, including retry. - */ - public long getSecretHandshakeLongTimeoutRetryMaxSpentTimeMs() { - return mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs; - } - - /** - * Number of secrete handshake retry allowed. - */ - public long getSecretHandshakeRetryAttempts() { - return mSecretHandshakeRetryAttempts; - } - - /** - * Timeout for secrete handshake and gatt connection, including retry. - */ - public long getSecretHandshakeRetryGattConnectionMaxSpentTimeMs() { - return mSecretHandshakeRetryGattConnectionMaxSpentTimeMs; - } - - /** - * Timeout for signal lost handling, including retry. - */ - public long getSignalLostRetryMaxSpentTimeMs() { - return mSignalLostRetryMaxSpentTimeMs; - } - - /** - * Returns error for gatt connection and secrete handshake, without retry. - */ - public ImmutableSet getGattConnectionAndSecretHandshakeNoRetryGattError() { - return mGattConnectionAndSecretHandshakeNoRetryGattError; - } - - /** - * Whether to retry upon secrete handshake timeout. - */ - public boolean getRetrySecretHandshakeTimeout() { - return mRetrySecretHandshakeTimeout; - } - - /** - * Wehther to log user manual retry. - */ - public boolean getLogUserManualRetry() { - return mLogUserManualRetry; - } - - /** - * Returns number of pairing failure counts. - */ - public int getPairFailureCounts() { - return mPairFailureCounts; - } - - /** - * Returns cached device address. - */ - public String getCachedDeviceAddress() { - return mCachedDeviceAddress; - } - - /** - * Returns possible cached device address. - */ - public String getPossibleCachedDeviceAddress() { - return mPossibleCachedDeviceAddress; - } - - /** - * Returns count of paired devices from the same model Id. - */ - public int getSameModelIdPairedDeviceCount() { - return mSameModelIdPairedDeviceCount; - } - - /** - * Whether the bonded device address is in the Cache . - */ - public boolean getIsDeviceFinishCheckAddressFromCache() { - return mIsDeviceFinishCheckAddressFromCache; - } - - /** - * Whether to log pairing info when cached model Id is hit. - */ - public boolean getLogPairWithCachedModelId() { - return mLogPairWithCachedModelId; - } - - /** - * Whether to directly connnect to a profile of a device, whose model Id is in cache. - */ - public boolean getDirectConnectProfileIfModelIdInCache() { - return mDirectConnectProfileIfModelIdInCache; - } - - /** - * Whether to auto-accept - * {@link android.bluetooth.BluetoothDevice#PAIRING_VARIANT_PASSKEY_CONFIRMATION}. - * Only the Fast Pair Simulator (which runs on an Android device) sends this. Since real - * Bluetooth headphones don't have displays, they use secure simple pairing (no pin code - * confirmation; we get no pairing request broadcast at all). So we may want to turn this off in - * prod. - */ - public boolean getAcceptPasskey() { - return mAcceptPasskey; - } - - /** - * Returns Uuids for supported profiles. - */ - @SuppressWarnings("mutable") - public byte[] getSupportedProfileUuids() { - return mSupportedProfileUuids; - } - - /** - * If true, after the Key-based Pairing BLE handshake, we wait for the headphones to send a - * pairing request to us; if false, we send the request to them. - */ - public boolean getProviderInitiatesBondingIfSupported() { - return mProviderInitiatesBondingIfSupported; - } - - /** - * If true, the first step will be attempting to connect directly to our supported profiles when - * a device has previously been bonded. This will help with performance on subsequent bondings - * and help to increase reliability in some cases. - */ - public boolean getAttemptDirectConnectionWhenPreviouslyBonded() { - return mAttemptDirectConnectionWhenPreviouslyBonded; - } - - /** - * If true, closed Gatt connections will be reopened when they are needed again. Otherwise, they - * will remain closed until they are explicitly reopened. - */ - public boolean getAutomaticallyReconnectGattWhenNeeded() { - return mAutomaticallyReconnectGattWhenNeeded; - } - - /** - * If true, we'll finish the pairing process after we've created a bond instead of after - * connecting a profile. - */ - public boolean getSkipConnectingProfiles() { - return mSkipConnectingProfiles; - } - - /** - * If true, continues the pairing process if we've timed out due to not receiving UUIDs from the - * headset. We can still attempt to connect to A2DP afterwards. If false, Fast Pair will fail - * after this step since we're expecting to receive the UUIDs. - */ - public boolean getIgnoreUuidTimeoutAfterBonded() { - return mIgnoreUuidTimeoutAfterBonded; - } - - /** - * If true, a specific transport type will be included in the create bond request, which will be - * used for dual mode devices. Otherwise, we'll use the platform defined default which is - * BluetoothDevice.TRANSPORT_AUTO. See {@link #getCreateBondTransportType()}. - */ - public boolean getSpecifyCreateBondTransportType() { - return mSpecifyCreateBondTransportType; - } - - /** - * The transport type to use when creating a bond when - * {@link #getSpecifyCreateBondTransportType() is true. This should be one of - * BluetoothDevice.TRANSPORT_AUTO, BluetoothDevice.TRANSPORT_BREDR, - * or BluetoothDevice.TRANSPORT_LE. - */ - public int getCreateBondTransportType() { - return mCreateBondTransportType; - } - - /** - * Whether to increase intent filter priority. - */ - public boolean getIncreaseIntentFilterPriority() { - return mIncreaseIntentFilterPriority; - } - - /** - * Whether to evaluate performance. - */ - public boolean getEvaluatePerformance() { - return mEvaluatePerformance; - } - - /** - * Returns extra logging information. - */ - @Nullable - public ExtraLoggingInformation getExtraLoggingInformation() { - return mExtraLoggingInformation; - } - - /** - * Whether to enable naming characteristic. - */ - public boolean getEnableNamingCharacteristic() { - return mEnableNamingCharacteristic; - } - - /** - * Whether to enable firmware version characteristic. - */ - public boolean getEnableFirmwareVersionCharacteristic() { - return mEnableFirmwareVersionCharacteristic; - } - - /** - * If true, even Fast Pair identifies a provider have paired with the account, still writes the - * identified account key to the provider. - */ - public boolean getKeepSameAccountKeyWrite() { - return mKeepSameAccountKeyWrite; - } - - /** - * If true, run retroactive pairing. - */ - public boolean getIsRetroactivePairing() { - return mIsRetroactivePairing; - } - - /** - * If it's larger than 0, {@link android.bluetooth.BluetoothDevice#fetchUuidsWithSdp} would be - * triggered with number of attempts after device is bonded and no profiles were automatically - * discovered". - */ - public int getNumSdpAttemptsAfterBonded() { - return mNumSdpAttemptsAfterBonded; - } - - /** - * If true, supports HID device for fastpair. - */ - public boolean getSupportHidDevice() { - return mSupportHidDevice; - } - - /** - * If true, we'll enable the pairing behavior to handle the state transition from BOND_BONDED to - * BOND_BONDING when directly connecting profiles. - */ - public boolean getEnablePairingWhileDirectlyConnecting() { - return mEnablePairingWhileDirectlyConnecting; - } - - /** - * If true, we will accept the user confirmation when bonding with FastPair 1.0 devices. - */ - public boolean getAcceptConsentForFastPairOne() { - return mAcceptConsentForFastPairOne; - } - - /** - * If it's larger than 0, we will retry connecting GATT within the timeout. - */ - public int getGattConnectRetryTimeoutMillis() { - return mGattConnectRetryTimeoutMillis; - } - - /** - * If true, then uses the new custom GATT characteristics {go/fastpair-128bit-gatt}. - */ - public boolean getEnable128BitCustomGattCharacteristicsId() { - return mEnable128BitCustomGattCharacteristicsId; - } - - /** - * If true, then sends the internal pair step or Exception to Validator by Intent. - */ - public boolean getEnableSendExceptionStepToValidator() { - return mEnableSendExceptionStepToValidator; - } - - /** - * If true, then adds the additional data type in the handshake packet when action over BLE. - */ - public boolean getEnableAdditionalDataTypeWhenActionOverBle() { - return mEnableAdditionalDataTypeWhenActionOverBle; - } - - /** - * If true, then checks the bond state when skips connecting profiles in the pairing shortcut. - */ - public boolean getCheckBondStateWhenSkipConnectingProfiles() { - return mCheckBondStateWhenSkipConnectingProfiles; - } - - /** - * If true, the passkey confirmation will be handled by the half-sheet UI. - */ - public boolean getHandlePasskeyConfirmationByUi() { - return mHandlePasskeyConfirmationByUi; - } - - /** - * If true, then use pair flow to show ui when pairing is finished without connecting profile. - */ - public boolean getEnablePairFlowShowUiWithoutProfileConnection() { - return mEnablePairFlowShowUiWithoutProfileConnection; - } - - @Override - public String toString() { - return "Preferences{" - + "gattOperationTimeoutSeconds=" + mGattOperationTimeoutSeconds + ", " - + "gattConnectionTimeoutSeconds=" + mGattConnectionTimeoutSeconds + ", " - + "bluetoothToggleTimeoutSeconds=" + mBluetoothToggleTimeoutSeconds + ", " - + "bluetoothToggleSleepSeconds=" + mBluetoothToggleSleepSeconds + ", " - + "classicDiscoveryTimeoutSeconds=" + mClassicDiscoveryTimeoutSeconds + ", " - + "numDiscoverAttempts=" + mNumDiscoverAttempts + ", " - + "discoveryRetrySleepSeconds=" + mDiscoveryRetrySleepSeconds + ", " - + "ignoreDiscoveryError=" + mIgnoreDiscoveryError + ", " - + "sdpTimeoutSeconds=" + mSdpTimeoutSeconds + ", " - + "numSdpAttempts=" + mNumSdpAttempts + ", " - + "numCreateBondAttempts=" + mNumCreateBondAttempts + ", " - + "numConnectAttempts=" + mNumConnectAttempts + ", " - + "numWriteAccountKeyAttempts=" + mNumWriteAccountKeyAttempts + ", " - + "toggleBluetoothOnFailure=" + mToggleBluetoothOnFailure + ", " - + "bluetoothStateUsesPolling=" + mBluetoothStateUsesPolling + ", " - + "bluetoothStatePollingMillis=" + mBluetoothStatePollingMillis + ", " - + "numAttempts=" + mNumAttempts + ", " - + "enableBrEdrHandover=" + mEnableBrEdrHandover + ", " - + "brHandoverDataCharacteristicId=" + mBrHandoverDataCharacteristicId + ", " - + "bluetoothSigDataCharacteristicId=" + mBluetoothSigDataCharacteristicId + ", " - + "firmwareVersionCharacteristicId=" + mFirmwareVersionCharacteristicId + ", " - + "brTransportBlockDataDescriptorId=" + mBrTransportBlockDataDescriptorId + ", " - + "waitForUuidsAfterBonding=" + mWaitForUuidsAfterBonding + ", " - + "receiveUuidsAndBondedEventBeforeClose=" + mReceiveUuidsAndBondedEventBeforeClose - + ", " - + "removeBondTimeoutSeconds=" + mRemoveBondTimeoutSeconds + ", " - + "removeBondSleepMillis=" + mRemoveBondSleepMillis + ", " - + "createBondTimeoutSeconds=" + mCreateBondTimeoutSeconds + ", " - + "hidCreateBondTimeoutSeconds=" + mHidCreateBondTimeoutSeconds + ", " - + "proxyTimeoutSeconds=" + mProxyTimeoutSeconds + ", " - + "rejectPhonebookAccess=" + mRejectPhonebookAccess + ", " - + "rejectMessageAccess=" + mRejectMessageAccess + ", " - + "rejectSimAccess=" + mRejectSimAccess + ", " - + "writeAccountKeySleepMillis=" + mWriteAccountKeySleepMillis + ", " - + "skipDisconnectingGattBeforeWritingAccountKey=" - + mSkipDisconnectingGattBeforeWritingAccountKey + ", " - + "moreEventLogForQuality=" + mMoreEventLogForQuality + ", " - + "retryGattConnectionAndSecretHandshake=" + mRetryGattConnectionAndSecretHandshake - + ", " - + "gattConnectShortTimeoutMs=" + mGattConnectShortTimeoutMs + ", " - + "gattConnectLongTimeoutMs=" + mGattConnectLongTimeoutMs + ", " - + "gattConnectShortTimeoutRetryMaxSpentTimeMs=" - + mGattConnectShortTimeoutRetryMaxSpentTimeMs + ", " - + "addressRotateRetryMaxSpentTimeMs=" + mAddressRotateRetryMaxSpentTimeMs + ", " - + "pairingRetryDelayMs=" + mPairingRetryDelayMs + ", " - + "secretHandshakeShortTimeoutMs=" + mSecretHandshakeShortTimeoutMs + ", " - + "secretHandshakeLongTimeoutMs=" + mSecretHandshakeLongTimeoutMs + ", " - + "secretHandshakeShortTimeoutRetryMaxSpentTimeMs=" - + mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs + ", " - + "secretHandshakeLongTimeoutRetryMaxSpentTimeMs=" - + mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs + ", " - + "secretHandshakeRetryAttempts=" + mSecretHandshakeRetryAttempts + ", " - + "secretHandshakeRetryGattConnectionMaxSpentTimeMs=" - + mSecretHandshakeRetryGattConnectionMaxSpentTimeMs + ", " - + "signalLostRetryMaxSpentTimeMs=" + mSignalLostRetryMaxSpentTimeMs + ", " - + "gattConnectionAndSecretHandshakeNoRetryGattError=" - + mGattConnectionAndSecretHandshakeNoRetryGattError + ", " - + "retrySecretHandshakeTimeout=" + mRetrySecretHandshakeTimeout + ", " - + "logUserManualRetry=" + mLogUserManualRetry + ", " - + "pairFailureCounts=" + mPairFailureCounts + ", " - + "cachedDeviceAddress=" + mCachedDeviceAddress + ", " - + "possibleCachedDeviceAddress=" + mPossibleCachedDeviceAddress + ", " - + "sameModelIdPairedDeviceCount=" + mSameModelIdPairedDeviceCount + ", " - + "isDeviceFinishCheckAddressFromCache=" + mIsDeviceFinishCheckAddressFromCache - + ", " - + "logPairWithCachedModelId=" + mLogPairWithCachedModelId + ", " - + "directConnectProfileIfModelIdInCache=" + mDirectConnectProfileIfModelIdInCache - + ", " - + "acceptPasskey=" + mAcceptPasskey + ", " - + "supportedProfileUuids=" + Arrays.toString(mSupportedProfileUuids) + ", " - + "providerInitiatesBondingIfSupported=" + mProviderInitiatesBondingIfSupported - + ", " - + "attemptDirectConnectionWhenPreviouslyBonded=" - + mAttemptDirectConnectionWhenPreviouslyBonded + ", " - + "automaticallyReconnectGattWhenNeeded=" + mAutomaticallyReconnectGattWhenNeeded - + ", " - + "skipConnectingProfiles=" + mSkipConnectingProfiles + ", " - + "ignoreUuidTimeoutAfterBonded=" + mIgnoreUuidTimeoutAfterBonded + ", " - + "specifyCreateBondTransportType=" + mSpecifyCreateBondTransportType + ", " - + "createBondTransportType=" + mCreateBondTransportType + ", " - + "increaseIntentFilterPriority=" + mIncreaseIntentFilterPriority + ", " - + "evaluatePerformance=" + mEvaluatePerformance + ", " - + "extraLoggingInformation=" + mExtraLoggingInformation + ", " - + "enableNamingCharacteristic=" + mEnableNamingCharacteristic + ", " - + "enableFirmwareVersionCharacteristic=" + mEnableFirmwareVersionCharacteristic - + ", " - + "keepSameAccountKeyWrite=" + mKeepSameAccountKeyWrite + ", " - + "isRetroactivePairing=" + mIsRetroactivePairing + ", " - + "numSdpAttemptsAfterBonded=" + mNumSdpAttemptsAfterBonded + ", " - + "supportHidDevice=" + mSupportHidDevice + ", " - + "enablePairingWhileDirectlyConnecting=" + mEnablePairingWhileDirectlyConnecting - + ", " - + "acceptConsentForFastPairOne=" + mAcceptConsentForFastPairOne + ", " - + "gattConnectRetryTimeoutMillis=" + mGattConnectRetryTimeoutMillis + ", " - + "enable128BitCustomGattCharacteristicsId=" - + mEnable128BitCustomGattCharacteristicsId + ", " - + "enableSendExceptionStepToValidator=" + mEnableSendExceptionStepToValidator + ", " - + "enableAdditionalDataTypeWhenActionOverBle=" - + mEnableAdditionalDataTypeWhenActionOverBle + ", " - + "checkBondStateWhenSkipConnectingProfiles=" - + mCheckBondStateWhenSkipConnectingProfiles + ", " - + "handlePasskeyConfirmationByUi=" + mHandlePasskeyConfirmationByUi + ", " - + "enablePairFlowShowUiWithoutProfileConnection=" - + mEnablePairFlowShowUiWithoutProfileConnection - + "}"; - } - - /** - * Converts an instance to a builder. - */ - public Builder toBuilder() { - return new Preferences.Builder(this); - } - - /** - * Constructs a builder. - */ - public static Builder builder() { - return new Preferences.Builder() - .setGattOperationTimeoutSeconds(3) - .setGattConnectionTimeoutSeconds(15) - .setBluetoothToggleTimeoutSeconds(10) - .setBluetoothToggleSleepSeconds(2) - .setClassicDiscoveryTimeoutSeconds(10) - .setNumDiscoverAttempts(3) - .setDiscoveryRetrySleepSeconds(1) - .setIgnoreDiscoveryError(false) - .setSdpTimeoutSeconds(10) - .setNumSdpAttempts(3) - .setNumCreateBondAttempts(3) - .setNumConnectAttempts(1) - .setNumWriteAccountKeyAttempts(3) - .setToggleBluetoothOnFailure(false) - .setBluetoothStateUsesPolling(true) - .setBluetoothStatePollingMillis(1000) - .setNumAttempts(2) - .setEnableBrEdrHandover(false) - .setBrHandoverDataCharacteristicId(get16BitUuid( - Constants.TransportDiscoveryService.BrHandoverDataCharacteristic.ID)) - .setBluetoothSigDataCharacteristicId(get16BitUuid( - Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic.ID)) - .setFirmwareVersionCharacteristicId(get16BitUuid(FirmwareVersionCharacteristic.ID)) - .setBrTransportBlockDataDescriptorId( - get16BitUuid( - Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic - .BrTransportBlockDataDescriptor.ID)) - .setWaitForUuidsAfterBonding(true) - .setReceiveUuidsAndBondedEventBeforeClose(true) - .setRemoveBondTimeoutSeconds(5) - .setRemoveBondSleepMillis(1000) - .setCreateBondTimeoutSeconds(15) - .setHidCreateBondTimeoutSeconds(40) - .setProxyTimeoutSeconds(2) - .setRejectPhonebookAccess(false) - .setRejectMessageAccess(false) - .setRejectSimAccess(false) - .setAcceptPasskey(true) - .setSupportedProfileUuids(Constants.getSupportedProfiles()) - .setWriteAccountKeySleepMillis(2000) - .setProviderInitiatesBondingIfSupported(false) - .setAttemptDirectConnectionWhenPreviouslyBonded(false) - .setAutomaticallyReconnectGattWhenNeeded(false) - .setSkipDisconnectingGattBeforeWritingAccountKey(false) - .setSkipConnectingProfiles(false) - .setIgnoreUuidTimeoutAfterBonded(false) - .setSpecifyCreateBondTransportType(false) - .setCreateBondTransportType(0 /*BluetoothDevice.TRANSPORT_AUTO*/) - .setIncreaseIntentFilterPriority(true) - .setEvaluatePerformance(false) - .setKeepSameAccountKeyWrite(true) - .setEnableNamingCharacteristic(false) - .setEnableFirmwareVersionCharacteristic(false) - .setIsRetroactivePairing(false) - .setNumSdpAttemptsAfterBonded(1) - .setSupportHidDevice(false) - .setEnablePairingWhileDirectlyConnecting(true) - .setAcceptConsentForFastPairOne(true) - .setGattConnectRetryTimeoutMillis(0) - .setEnable128BitCustomGattCharacteristicsId(true) - .setEnableSendExceptionStepToValidator(true) - .setEnableAdditionalDataTypeWhenActionOverBle(true) - .setCheckBondStateWhenSkipConnectingProfiles(true) - .setHandlePasskeyConfirmationByUi(false) - .setMoreEventLogForQuality(true) - .setRetryGattConnectionAndSecretHandshake(true) - .setGattConnectShortTimeoutMs(7000) - .setGattConnectLongTimeoutMs(15000) - .setGattConnectShortTimeoutRetryMaxSpentTimeMs(10000) - .setAddressRotateRetryMaxSpentTimeMs(15000) - .setPairingRetryDelayMs(100) - .setSecretHandshakeShortTimeoutMs(3000) - .setSecretHandshakeLongTimeoutMs(10000) - .setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(5000) - .setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(7000) - .setSecretHandshakeRetryAttempts(3) - .setSecretHandshakeRetryGattConnectionMaxSpentTimeMs(15000) - .setSignalLostRetryMaxSpentTimeMs(15000) - .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of()) - .setRetrySecretHandshakeTimeout(false) - .setLogUserManualRetry(true) - .setPairFailureCounts(0) - .setEnablePairFlowShowUiWithoutProfileConnection(true) - .setPairFailureCounts(0) - .setLogPairWithCachedModelId(true) - .setDirectConnectProfileIfModelIdInCache(false) - .setCachedDeviceAddress("") - .setPossibleCachedDeviceAddress("") - .setSameModelIdPairedDeviceCount(0) - .setIsDeviceFinishCheckAddressFromCache(true); - } - - /** - * Constructs a builder from GmsLog. - */ - // TODO(b/206668142): remove this builder once api is ready. - public static Builder builderFromGmsLog() { - return new Preferences.Builder() - .setGattOperationTimeoutSeconds(10) - .setGattConnectionTimeoutSeconds(15) - .setBluetoothToggleTimeoutSeconds(10) - .setBluetoothToggleSleepSeconds(2) - .setClassicDiscoveryTimeoutSeconds(13) - .setNumDiscoverAttempts(3) - .setDiscoveryRetrySleepSeconds(1) - .setIgnoreDiscoveryError(true) - .setSdpTimeoutSeconds(10) - .setNumSdpAttempts(0) - .setNumCreateBondAttempts(3) - .setNumConnectAttempts(2) - .setNumWriteAccountKeyAttempts(3) - .setToggleBluetoothOnFailure(false) - .setBluetoothStateUsesPolling(true) - .setBluetoothStatePollingMillis(1000) - .setNumAttempts(2) - .setEnableBrEdrHandover(false) - .setBrHandoverDataCharacteristicId((short) 11265) - .setBluetoothSigDataCharacteristicId((short) 11266) - .setFirmwareVersionCharacteristicId((short) 10790) - .setBrTransportBlockDataDescriptorId((short) 11267) - .setWaitForUuidsAfterBonding(true) - .setReceiveUuidsAndBondedEventBeforeClose(true) - .setRemoveBondTimeoutSeconds(5) - .setRemoveBondSleepMillis(1000) - .setCreateBondTimeoutSeconds(15) - .setHidCreateBondTimeoutSeconds(40) - .setProxyTimeoutSeconds(2) - .setRejectPhonebookAccess(false) - .setRejectMessageAccess(false) - .setRejectSimAccess(false) - .setAcceptPasskey(true) - .setSupportedProfileUuids(Constants.getSupportedProfiles()) - .setWriteAccountKeySleepMillis(2000) - .setProviderInitiatesBondingIfSupported(false) - .setAttemptDirectConnectionWhenPreviouslyBonded(true) - .setAutomaticallyReconnectGattWhenNeeded(true) - .setSkipDisconnectingGattBeforeWritingAccountKey(true) - .setSkipConnectingProfiles(false) - .setIgnoreUuidTimeoutAfterBonded(true) - .setSpecifyCreateBondTransportType(false) - .setCreateBondTransportType(0 /*BluetoothDevice.TRANSPORT_AUTO*/) - .setIncreaseIntentFilterPriority(true) - .setEvaluatePerformance(true) - .setKeepSameAccountKeyWrite(true) - .setEnableNamingCharacteristic(true) - .setEnableFirmwareVersionCharacteristic(true) - .setIsRetroactivePairing(false) - .setNumSdpAttemptsAfterBonded(1) - .setSupportHidDevice(false) - .setEnablePairingWhileDirectlyConnecting(true) - .setAcceptConsentForFastPairOne(true) - .setGattConnectRetryTimeoutMillis(18000) - .setEnable128BitCustomGattCharacteristicsId(true) - .setEnableSendExceptionStepToValidator(true) - .setEnableAdditionalDataTypeWhenActionOverBle(true) - .setCheckBondStateWhenSkipConnectingProfiles(true) - .setHandlePasskeyConfirmationByUi(false) - .setMoreEventLogForQuality(true) - .setRetryGattConnectionAndSecretHandshake(true) - .setGattConnectShortTimeoutMs(7000) - .setGattConnectLongTimeoutMs(15000) - .setGattConnectShortTimeoutRetryMaxSpentTimeMs(10000) - .setAddressRotateRetryMaxSpentTimeMs(15000) - .setPairingRetryDelayMs(100) - .setSecretHandshakeShortTimeoutMs(3000) - .setSecretHandshakeLongTimeoutMs(10000) - .setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(5000) - .setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(7000) - .setSecretHandshakeRetryAttempts(3) - .setSecretHandshakeRetryGattConnectionMaxSpentTimeMs(15000) - .setSignalLostRetryMaxSpentTimeMs(15000) - .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of(257)) - .setRetrySecretHandshakeTimeout(false) - .setLogUserManualRetry(true) - .setPairFailureCounts(0) - .setEnablePairFlowShowUiWithoutProfileConnection(true) - .setPairFailureCounts(0) - .setLogPairWithCachedModelId(true) - .setDirectConnectProfileIfModelIdInCache(true) - .setCachedDeviceAddress("") - .setPossibleCachedDeviceAddress("") - .setSameModelIdPairedDeviceCount(0) - .setIsDeviceFinishCheckAddressFromCache(true); - } - - /** - * Preferences builder. - */ - public static class Builder { - - private int mGattOperationTimeoutSeconds; - private int mGattConnectionTimeoutSeconds; - private int mBluetoothToggleTimeoutSeconds; - private int mBluetoothToggleSleepSeconds; - private int mClassicDiscoveryTimeoutSeconds; - private int mNumDiscoverAttempts; - private int mDiscoveryRetrySleepSeconds; - private boolean mIgnoreDiscoveryError; - private int mSdpTimeoutSeconds; - private int mNumSdpAttempts; - private int mNumCreateBondAttempts; - private int mNumConnectAttempts; - private int mNumWriteAccountKeyAttempts; - private boolean mToggleBluetoothOnFailure; - private boolean mBluetoothStateUsesPolling; - private int mBluetoothStatePollingMillis; - private int mNumAttempts; - private boolean mEnableBrEdrHandover; - private short mBrHandoverDataCharacteristicId; - private short mBluetoothSigDataCharacteristicId; - private short mFirmwareVersionCharacteristicId; - private short mBrTransportBlockDataDescriptorId; - private boolean mWaitForUuidsAfterBonding; - private boolean mReceiveUuidsAndBondedEventBeforeClose; - private int mRemoveBondTimeoutSeconds; - private int mRemoveBondSleepMillis; - private int mCreateBondTimeoutSeconds; - private int mHidCreateBondTimeoutSeconds; - private int mProxyTimeoutSeconds; - private boolean mRejectPhonebookAccess; - private boolean mRejectMessageAccess; - private boolean mRejectSimAccess; - private int mWriteAccountKeySleepMillis; - private boolean mSkipDisconnectingGattBeforeWritingAccountKey; - private boolean mMoreEventLogForQuality; - private boolean mRetryGattConnectionAndSecretHandshake; - private long mGattConnectShortTimeoutMs; - private long mGattConnectLongTimeoutMs; - private long mGattConnectShortTimeoutRetryMaxSpentTimeMs; - private long mAddressRotateRetryMaxSpentTimeMs; - private long mPairingRetryDelayMs; - private long mSecretHandshakeShortTimeoutMs; - private long mSecretHandshakeLongTimeoutMs; - private long mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs; - private long mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs; - private long mSecretHandshakeRetryAttempts; - private long mSecretHandshakeRetryGattConnectionMaxSpentTimeMs; - private long mSignalLostRetryMaxSpentTimeMs; - private ImmutableSet mGattConnectionAndSecretHandshakeNoRetryGattError; - private boolean mRetrySecretHandshakeTimeout; - private boolean mLogUserManualRetry; - private int mPairFailureCounts; - private String mCachedDeviceAddress; - private String mPossibleCachedDeviceAddress; - private int mSameModelIdPairedDeviceCount; - private boolean mIsDeviceFinishCheckAddressFromCache; - private boolean mLogPairWithCachedModelId; - private boolean mDirectConnectProfileIfModelIdInCache; - private boolean mAcceptPasskey; - private byte[] mSupportedProfileUuids; - private boolean mProviderInitiatesBondingIfSupported; - private boolean mAttemptDirectConnectionWhenPreviouslyBonded; - private boolean mAutomaticallyReconnectGattWhenNeeded; - private boolean mSkipConnectingProfiles; - private boolean mIgnoreUuidTimeoutAfterBonded; - private boolean mSpecifyCreateBondTransportType; - private int mCreateBondTransportType; - private boolean mIncreaseIntentFilterPriority; - private boolean mEvaluatePerformance; - private Preferences.ExtraLoggingInformation mExtraLoggingInformation; - private boolean mEnableNamingCharacteristic; - private boolean mEnableFirmwareVersionCharacteristic; - private boolean mKeepSameAccountKeyWrite; - private boolean mIsRetroactivePairing; - private int mNumSdpAttemptsAfterBonded; - private boolean mSupportHidDevice; - private boolean mEnablePairingWhileDirectlyConnecting; - private boolean mAcceptConsentForFastPairOne; - private int mGattConnectRetryTimeoutMillis; - private boolean mEnable128BitCustomGattCharacteristicsId; - private boolean mEnableSendExceptionStepToValidator; - private boolean mEnableAdditionalDataTypeWhenActionOverBle; - private boolean mCheckBondStateWhenSkipConnectingProfiles; - private boolean mHandlePasskeyConfirmationByUi; - private boolean mEnablePairFlowShowUiWithoutProfileConnection; - - private Builder() { - } - - private Builder(Preferences source) { - this.mGattOperationTimeoutSeconds = source.getGattOperationTimeoutSeconds(); - this.mGattConnectionTimeoutSeconds = source.getGattConnectionTimeoutSeconds(); - this.mBluetoothToggleTimeoutSeconds = source.getBluetoothToggleTimeoutSeconds(); - this.mBluetoothToggleSleepSeconds = source.getBluetoothToggleSleepSeconds(); - this.mClassicDiscoveryTimeoutSeconds = source.getClassicDiscoveryTimeoutSeconds(); - this.mNumDiscoverAttempts = source.getNumDiscoverAttempts(); - this.mDiscoveryRetrySleepSeconds = source.getDiscoveryRetrySleepSeconds(); - this.mIgnoreDiscoveryError = source.getIgnoreDiscoveryError(); - this.mSdpTimeoutSeconds = source.getSdpTimeoutSeconds(); - this.mNumSdpAttempts = source.getNumSdpAttempts(); - this.mNumCreateBondAttempts = source.getNumCreateBondAttempts(); - this.mNumConnectAttempts = source.getNumConnectAttempts(); - this.mNumWriteAccountKeyAttempts = source.getNumWriteAccountKeyAttempts(); - this.mToggleBluetoothOnFailure = source.getToggleBluetoothOnFailure(); - this.mBluetoothStateUsesPolling = source.getBluetoothStateUsesPolling(); - this.mBluetoothStatePollingMillis = source.getBluetoothStatePollingMillis(); - this.mNumAttempts = source.getNumAttempts(); - this.mEnableBrEdrHandover = source.getEnableBrEdrHandover(); - this.mBrHandoverDataCharacteristicId = source.getBrHandoverDataCharacteristicId(); - this.mBluetoothSigDataCharacteristicId = source.getBluetoothSigDataCharacteristicId(); - this.mFirmwareVersionCharacteristicId = source.getFirmwareVersionCharacteristicId(); - this.mBrTransportBlockDataDescriptorId = source.getBrTransportBlockDataDescriptorId(); - this.mWaitForUuidsAfterBonding = source.getWaitForUuidsAfterBonding(); - this.mReceiveUuidsAndBondedEventBeforeClose = source - .getReceiveUuidsAndBondedEventBeforeClose(); - this.mRemoveBondTimeoutSeconds = source.getRemoveBondTimeoutSeconds(); - this.mRemoveBondSleepMillis = source.getRemoveBondSleepMillis(); - this.mCreateBondTimeoutSeconds = source.getCreateBondTimeoutSeconds(); - this.mHidCreateBondTimeoutSeconds = source.getHidCreateBondTimeoutSeconds(); - this.mProxyTimeoutSeconds = source.getProxyTimeoutSeconds(); - this.mRejectPhonebookAccess = source.getRejectPhonebookAccess(); - this.mRejectMessageAccess = source.getRejectMessageAccess(); - this.mRejectSimAccess = source.getRejectSimAccess(); - this.mWriteAccountKeySleepMillis = source.getWriteAccountKeySleepMillis(); - this.mSkipDisconnectingGattBeforeWritingAccountKey = source - .getSkipDisconnectingGattBeforeWritingAccountKey(); - this.mMoreEventLogForQuality = source.getMoreEventLogForQuality(); - this.mRetryGattConnectionAndSecretHandshake = source - .getRetryGattConnectionAndSecretHandshake(); - this.mGattConnectShortTimeoutMs = source.getGattConnectShortTimeoutMs(); - this.mGattConnectLongTimeoutMs = source.getGattConnectLongTimeoutMs(); - this.mGattConnectShortTimeoutRetryMaxSpentTimeMs = source - .getGattConnectShortTimeoutRetryMaxSpentTimeMs(); - this.mAddressRotateRetryMaxSpentTimeMs = source.getAddressRotateRetryMaxSpentTimeMs(); - this.mPairingRetryDelayMs = source.getPairingRetryDelayMs(); - this.mSecretHandshakeShortTimeoutMs = source.getSecretHandshakeShortTimeoutMs(); - this.mSecretHandshakeLongTimeoutMs = source.getSecretHandshakeLongTimeoutMs(); - this.mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs = source - .getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(); - this.mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs = source - .getSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(); - this.mSecretHandshakeRetryAttempts = source.getSecretHandshakeRetryAttempts(); - this.mSecretHandshakeRetryGattConnectionMaxSpentTimeMs = source - .getSecretHandshakeRetryGattConnectionMaxSpentTimeMs(); - this.mSignalLostRetryMaxSpentTimeMs = source.getSignalLostRetryMaxSpentTimeMs(); - this.mGattConnectionAndSecretHandshakeNoRetryGattError = source - .getGattConnectionAndSecretHandshakeNoRetryGattError(); - this.mRetrySecretHandshakeTimeout = source.getRetrySecretHandshakeTimeout(); - this.mLogUserManualRetry = source.getLogUserManualRetry(); - this.mPairFailureCounts = source.getPairFailureCounts(); - this.mCachedDeviceAddress = source.getCachedDeviceAddress(); - this.mPossibleCachedDeviceAddress = source.getPossibleCachedDeviceAddress(); - this.mSameModelIdPairedDeviceCount = source.getSameModelIdPairedDeviceCount(); - this.mIsDeviceFinishCheckAddressFromCache = source - .getIsDeviceFinishCheckAddressFromCache(); - this.mLogPairWithCachedModelId = source.getLogPairWithCachedModelId(); - this.mDirectConnectProfileIfModelIdInCache = source - .getDirectConnectProfileIfModelIdInCache(); - this.mAcceptPasskey = source.getAcceptPasskey(); - this.mSupportedProfileUuids = source.getSupportedProfileUuids(); - this.mProviderInitiatesBondingIfSupported = source - .getProviderInitiatesBondingIfSupported(); - this.mAttemptDirectConnectionWhenPreviouslyBonded = source - .getAttemptDirectConnectionWhenPreviouslyBonded(); - this.mAutomaticallyReconnectGattWhenNeeded = source - .getAutomaticallyReconnectGattWhenNeeded(); - this.mSkipConnectingProfiles = source.getSkipConnectingProfiles(); - this.mIgnoreUuidTimeoutAfterBonded = source.getIgnoreUuidTimeoutAfterBonded(); - this.mSpecifyCreateBondTransportType = source.getSpecifyCreateBondTransportType(); - this.mCreateBondTransportType = source.getCreateBondTransportType(); - this.mIncreaseIntentFilterPriority = source.getIncreaseIntentFilterPriority(); - this.mEvaluatePerformance = source.getEvaluatePerformance(); - this.mExtraLoggingInformation = source.getExtraLoggingInformation(); - this.mEnableNamingCharacteristic = source.getEnableNamingCharacteristic(); - this.mEnableFirmwareVersionCharacteristic = source - .getEnableFirmwareVersionCharacteristic(); - this.mKeepSameAccountKeyWrite = source.getKeepSameAccountKeyWrite(); - this.mIsRetroactivePairing = source.getIsRetroactivePairing(); - this.mNumSdpAttemptsAfterBonded = source.getNumSdpAttemptsAfterBonded(); - this.mSupportHidDevice = source.getSupportHidDevice(); - this.mEnablePairingWhileDirectlyConnecting = source - .getEnablePairingWhileDirectlyConnecting(); - this.mAcceptConsentForFastPairOne = source.getAcceptConsentForFastPairOne(); - this.mGattConnectRetryTimeoutMillis = source.getGattConnectRetryTimeoutMillis(); - this.mEnable128BitCustomGattCharacteristicsId = source - .getEnable128BitCustomGattCharacteristicsId(); - this.mEnableSendExceptionStepToValidator = source - .getEnableSendExceptionStepToValidator(); - this.mEnableAdditionalDataTypeWhenActionOverBle = source - .getEnableAdditionalDataTypeWhenActionOverBle(); - this.mCheckBondStateWhenSkipConnectingProfiles = source - .getCheckBondStateWhenSkipConnectingProfiles(); - this.mHandlePasskeyConfirmationByUi = source.getHandlePasskeyConfirmationByUi(); - this.mEnablePairFlowShowUiWithoutProfileConnection = source - .getEnablePairFlowShowUiWithoutProfileConnection(); - } - - /** - * Set gatt operation timeout. - */ - public Builder setGattOperationTimeoutSeconds(int value) { - this.mGattOperationTimeoutSeconds = value; - return this; - } - - /** - * Set gatt connection timeout. - */ - public Builder setGattConnectionTimeoutSeconds(int value) { - this.mGattConnectionTimeoutSeconds = value; - return this; - } - - /** - * Set bluetooth toggle timeout. - */ - public Builder setBluetoothToggleTimeoutSeconds(int value) { - this.mBluetoothToggleTimeoutSeconds = value; - return this; - } - - /** - * Set bluetooth toggle sleep time. - */ - public Builder setBluetoothToggleSleepSeconds(int value) { - this.mBluetoothToggleSleepSeconds = value; - return this; - } - - /** - * Set classic discovery timeout. - */ - public Builder setClassicDiscoveryTimeoutSeconds(int value) { - this.mClassicDiscoveryTimeoutSeconds = value; - return this; - } - - /** - * Set number of discover attempts allowed. - */ - public Builder setNumDiscoverAttempts(int value) { - this.mNumDiscoverAttempts = value; - return this; - } - - /** - * Set discovery retry sleep time. - */ - public Builder setDiscoveryRetrySleepSeconds(int value) { - this.mDiscoveryRetrySleepSeconds = value; - return this; - } - - /** - * Set whether to ignore discovery error. - */ - public Builder setIgnoreDiscoveryError(boolean value) { - this.mIgnoreDiscoveryError = value; - return this; - } - - /** - * Set sdp timeout. - */ - public Builder setSdpTimeoutSeconds(int value) { - this.mSdpTimeoutSeconds = value; - return this; - } - - /** - * Set number of sdp attempts allowed. - */ - public Builder setNumSdpAttempts(int value) { - this.mNumSdpAttempts = value; - return this; - } - - /** - * Set number of allowed attempts to create bond. - */ - public Builder setNumCreateBondAttempts(int value) { - this.mNumCreateBondAttempts = value; - return this; - } - - /** - * Set number of connect attempts allowed. - */ - public Builder setNumConnectAttempts(int value) { - this.mNumConnectAttempts = value; - return this; - } - - /** - * Set number of write account key attempts allowed. - */ - public Builder setNumWriteAccountKeyAttempts(int value) { - this.mNumWriteAccountKeyAttempts = value; - return this; - } - - /** - * Set whether to retry by bluetooth toggle on failure. - */ - public Builder setToggleBluetoothOnFailure(boolean value) { - this.mToggleBluetoothOnFailure = value; - return this; - } - - /** - * Set whether to use polling to set bluetooth status. - */ - public Builder setBluetoothStateUsesPolling(boolean value) { - this.mBluetoothStateUsesPolling = value; - return this; - } - - /** - * Set Bluetooth state polling timeout. - */ - public Builder setBluetoothStatePollingMillis(int value) { - this.mBluetoothStatePollingMillis = value; - return this; - } - - /** - * Set number of attempts. - */ - public Builder setNumAttempts(int value) { - this.mNumAttempts = value; - return this; - } - - /** - * Set whether to enable BrEdr handover. - */ - public Builder setEnableBrEdrHandover(boolean value) { - this.mEnableBrEdrHandover = value; - return this; - } - - /** - * Set Br handover data characteristic Id. - */ - public Builder setBrHandoverDataCharacteristicId(short value) { - this.mBrHandoverDataCharacteristicId = value; - return this; - } - - /** - * Set Bluetooth Sig data characteristic Id. - */ - public Builder setBluetoothSigDataCharacteristicId(short value) { - this.mBluetoothSigDataCharacteristicId = value; - return this; - } - - /** - * Set Firmware version characteristic id. - */ - public Builder setFirmwareVersionCharacteristicId(short value) { - this.mFirmwareVersionCharacteristicId = value; - return this; - } - - /** - * Set Br transport block data descriptor id. - */ - public Builder setBrTransportBlockDataDescriptorId(short value) { - this.mBrTransportBlockDataDescriptorId = value; - return this; - } - - /** - * Set whether to wait for Uuids after bonding. - */ - public Builder setWaitForUuidsAfterBonding(boolean value) { - this.mWaitForUuidsAfterBonding = value; - return this; - } - - /** - * Set whether to receive Uuids and bonded event before close. - */ - public Builder setReceiveUuidsAndBondedEventBeforeClose(boolean value) { - this.mReceiveUuidsAndBondedEventBeforeClose = value; - return this; - } - - /** - * Set remove bond timeout. - */ - public Builder setRemoveBondTimeoutSeconds(int value) { - this.mRemoveBondTimeoutSeconds = value; - return this; - } - - /** - * Set remove bound sleep time. - */ - public Builder setRemoveBondSleepMillis(int value) { - this.mRemoveBondSleepMillis = value; - return this; - } - - /** - * Set create bond timeout. - */ - public Builder setCreateBondTimeoutSeconds(int value) { - this.mCreateBondTimeoutSeconds = value; - return this; - } - - /** - * Set Hid create bond timeout. - */ - public Builder setHidCreateBondTimeoutSeconds(int value) { - this.mHidCreateBondTimeoutSeconds = value; - return this; - } - - /** - * Set proxy timeout. - */ - public Builder setProxyTimeoutSeconds(int value) { - this.mProxyTimeoutSeconds = value; - return this; - } - - /** - * Set whether to reject phone book access. - */ - public Builder setRejectPhonebookAccess(boolean value) { - this.mRejectPhonebookAccess = value; - return this; - } - - /** - * Set whether to reject message access. - */ - public Builder setRejectMessageAccess(boolean value) { - this.mRejectMessageAccess = value; - return this; - } - - /** - * Set whether to reject slim access. - */ - public Builder setRejectSimAccess(boolean value) { - this.mRejectSimAccess = value; - return this; - } - - /** - * Set whether to accept passkey. - */ - public Builder setAcceptPasskey(boolean value) { - this.mAcceptPasskey = value; - return this; - } - - /** - * Set supported profile Uuids. - */ - public Builder setSupportedProfileUuids(byte[] value) { - this.mSupportedProfileUuids = value; - return this; - } - - /** - * Set whether to collect more event log for quality. - */ - public Builder setMoreEventLogForQuality(boolean value) { - this.mMoreEventLogForQuality = value; - return this; - } - - /** - * Set supported profile Uuids. - */ - public Builder setSupportedProfileUuids(short... uuids) { - return setSupportedProfileUuids(Bytes.toBytes(ByteOrder.BIG_ENDIAN, uuids)); - } - - /** - * Set write account key sleep time. - */ - public Builder setWriteAccountKeySleepMillis(int value) { - this.mWriteAccountKeySleepMillis = value; - return this; - } - - /** - * Set whether to do provider initialized bonding if supported. - */ - public Builder setProviderInitiatesBondingIfSupported(boolean value) { - this.mProviderInitiatesBondingIfSupported = value; - return this; - } - - /** - * Set whether to try direct connection when the device is previously bonded. - */ - public Builder setAttemptDirectConnectionWhenPreviouslyBonded(boolean value) { - this.mAttemptDirectConnectionWhenPreviouslyBonded = value; - return this; - } - - /** - * Set whether to automatically reconnect gatt when needed. - */ - public Builder setAutomaticallyReconnectGattWhenNeeded(boolean value) { - this.mAutomaticallyReconnectGattWhenNeeded = value; - return this; - } - - /** - * Set whether to skip disconnecting gatt before writing account key. - */ - public Builder setSkipDisconnectingGattBeforeWritingAccountKey(boolean value) { - this.mSkipDisconnectingGattBeforeWritingAccountKey = value; - return this; - } - - /** - * Set whether to skip connecting profiles. - */ - public Builder setSkipConnectingProfiles(boolean value) { - this.mSkipConnectingProfiles = value; - return this; - } - - /** - * Set whether to ignore Uuid timeout after bonded. - */ - public Builder setIgnoreUuidTimeoutAfterBonded(boolean value) { - this.mIgnoreUuidTimeoutAfterBonded = value; - return this; - } - - /** - * Set whether to include transport type in create bound request. - */ - public Builder setSpecifyCreateBondTransportType(boolean value) { - this.mSpecifyCreateBondTransportType = value; - return this; - } - - /** - * Set transport type used in create bond request. - */ - public Builder setCreateBondTransportType(int value) { - this.mCreateBondTransportType = value; - return this; - } - - /** - * Set whether to increase intent filter priority. - */ - public Builder setIncreaseIntentFilterPriority(boolean value) { - this.mIncreaseIntentFilterPriority = value; - return this; - } - - /** - * Set whether to evaluate performance. - */ - public Builder setEvaluatePerformance(boolean value) { - this.mEvaluatePerformance = value; - return this; - } - - /** - * Set extra logging info. - */ - public Builder setExtraLoggingInformation(ExtraLoggingInformation value) { - this.mExtraLoggingInformation = value; - return this; - } - - /** - * Set whether to enable naming characteristic. - */ - public Builder setEnableNamingCharacteristic(boolean value) { - this.mEnableNamingCharacteristic = value; - return this; - } - - /** - * Set whether to keep writing the account key to the provider, that has already paired with - * the account. - */ - public Builder setKeepSameAccountKeyWrite(boolean value) { - this.mKeepSameAccountKeyWrite = value; - return this; - } - - /** - * Set whether to enable firmware version characteristic. - */ - public Builder setEnableFirmwareVersionCharacteristic(boolean value) { - this.mEnableFirmwareVersionCharacteristic = value; - return this; - } - - /** - * Set whether it is retroactive pairing. - */ - public Builder setIsRetroactivePairing(boolean value) { - this.mIsRetroactivePairing = value; - return this; - } - - /** - * Set number of allowed sdp attempts after bonded. - */ - public Builder setNumSdpAttemptsAfterBonded(int value) { - this.mNumSdpAttemptsAfterBonded = value; - return this; - } - - /** - * Set whether to support Hid device. - */ - public Builder setSupportHidDevice(boolean value) { - this.mSupportHidDevice = value; - return this; - } - - /** - * Set wehther to enable the pairing behavior to handle the state transition from - * BOND_BONDED to BOND_BONDING when directly connecting profiles. - */ - public Builder setEnablePairingWhileDirectlyConnecting(boolean value) { - this.mEnablePairingWhileDirectlyConnecting = value; - return this; - } - - /** - * Set whether to accept consent for fast pair one. - */ - public Builder setAcceptConsentForFastPairOne(boolean value) { - this.mAcceptConsentForFastPairOne = value; - return this; - } - - /** - * Set Gatt connect retry timeout. - */ - public Builder setGattConnectRetryTimeoutMillis(int value) { - this.mGattConnectRetryTimeoutMillis = value; - return this; - } - - /** - * Set whether to enable 128 bit custom gatt characteristic Id. - */ - public Builder setEnable128BitCustomGattCharacteristicsId(boolean value) { - this.mEnable128BitCustomGattCharacteristicsId = value; - return this; - } - - /** - * Set whether to send exception step to validator. - */ - public Builder setEnableSendExceptionStepToValidator(boolean value) { - this.mEnableSendExceptionStepToValidator = value; - return this; - } - - /** - * Set wehther to add the additional data type in the handshake when action over BLE. - */ - public Builder setEnableAdditionalDataTypeWhenActionOverBle(boolean value) { - this.mEnableAdditionalDataTypeWhenActionOverBle = value; - return this; - } - - /** - * Set whether to check bond state when skip connecting profiles. - */ - public Builder setCheckBondStateWhenSkipConnectingProfiles(boolean value) { - this.mCheckBondStateWhenSkipConnectingProfiles = value; - return this; - } - - /** - * Set whether to handle passkey confirmation by UI. - */ - public Builder setHandlePasskeyConfirmationByUi(boolean value) { - this.mHandlePasskeyConfirmationByUi = value; - return this; - } - - /** - * Set wehther to retry gatt connection and secret handshake. - */ - public Builder setRetryGattConnectionAndSecretHandshake(boolean value) { - this.mRetryGattConnectionAndSecretHandshake = value; - return this; - } - - /** - * Set gatt connect short timeout. - */ - public Builder setGattConnectShortTimeoutMs(long value) { - this.mGattConnectShortTimeoutMs = value; - return this; - } - - /** - * Set gatt connect long timeout. - */ - public Builder setGattConnectLongTimeoutMs(long value) { - this.mGattConnectLongTimeoutMs = value; - return this; - } - - /** - * Set gatt connection short timoutout, including retry. - */ - public Builder setGattConnectShortTimeoutRetryMaxSpentTimeMs(long value) { - this.mGattConnectShortTimeoutRetryMaxSpentTimeMs = value; - return this; - } - - /** - * Set address rotate timeout, including retry. - */ - public Builder setAddressRotateRetryMaxSpentTimeMs(long value) { - this.mAddressRotateRetryMaxSpentTimeMs = value; - return this; - } - - /** - * Set pairing retry delay time. - */ - public Builder setPairingRetryDelayMs(long value) { - this.mPairingRetryDelayMs = value; - return this; - } - - /** - * Set secret handshake short timeout. - */ - public Builder setSecretHandshakeShortTimeoutMs(long value) { - this.mSecretHandshakeShortTimeoutMs = value; - return this; - } - - /** - * Set secret handshake long timeout. - */ - public Builder setSecretHandshakeLongTimeoutMs(long value) { - this.mSecretHandshakeLongTimeoutMs = value; - return this; - } - - /** - * Set secret handshake short timeout retry max spent time. - */ - public Builder setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(long value) { - this.mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs = value; - return this; - } - - /** - * Set secret handshake long timeout retry max spent time. - */ - public Builder setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(long value) { - this.mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs = value; - return this; - } - - /** - * Set secret handshake retry attempts allowed. - */ - public Builder setSecretHandshakeRetryAttempts(long value) { - this.mSecretHandshakeRetryAttempts = value; - return this; - } - - /** - * Set secret handshake retry gatt connection max spent time. - */ - public Builder setSecretHandshakeRetryGattConnectionMaxSpentTimeMs(long value) { - this.mSecretHandshakeRetryGattConnectionMaxSpentTimeMs = value; - return this; - } - - /** - * Set signal loss retry max spent time. - */ - public Builder setSignalLostRetryMaxSpentTimeMs(long value) { - this.mSignalLostRetryMaxSpentTimeMs = value; - return this; - } - - /** - * Set gatt connection and secret handshake no retry gatt error. - */ - public Builder setGattConnectionAndSecretHandshakeNoRetryGattError( - ImmutableSet value) { - this.mGattConnectionAndSecretHandshakeNoRetryGattError = value; - return this; - } - - /** - * Set retry secret handshake timeout. - */ - public Builder setRetrySecretHandshakeTimeout(boolean value) { - this.mRetrySecretHandshakeTimeout = value; - return this; - } - - /** - * Set whether to log user manual retry. - */ - public Builder setLogUserManualRetry(boolean value) { - this.mLogUserManualRetry = value; - return this; - } - - /** - * Set pair falure counts. - */ - public Builder setPairFailureCounts(int counts) { - this.mPairFailureCounts = counts; - return this; - } - - /** - * Set whether to use pair flow to show ui when pairing is finished without connecting - * profile.. - */ - public Builder setEnablePairFlowShowUiWithoutProfileConnection(boolean value) { - this.mEnablePairFlowShowUiWithoutProfileConnection = value; - return this; - } - - /** - * Set whether to log pairing with cached module Id. - */ - public Builder setLogPairWithCachedModelId(boolean value) { - this.mLogPairWithCachedModelId = value; - return this; - } - - /** - * Set possible cached device address. - */ - public Builder setPossibleCachedDeviceAddress(String value) { - this.mPossibleCachedDeviceAddress = value; - return this; - } - - /** - * Set paired device count from the same module Id. - */ - public Builder setSameModelIdPairedDeviceCount(int value) { - this.mSameModelIdPairedDeviceCount = value; - return this; - } - - /** - * Set whether the bonded device address is from cache. - */ - public Builder setIsDeviceFinishCheckAddressFromCache(boolean value) { - this.mIsDeviceFinishCheckAddressFromCache = value; - return this; - } - - /** - * Set whether to directly connect profile if modelId is in cache. - */ - public Builder setDirectConnectProfileIfModelIdInCache(boolean value) { - this.mDirectConnectProfileIfModelIdInCache = value; - return this; - } - - /** - * Set cached device address. - */ - public Builder setCachedDeviceAddress(String value) { - this.mCachedDeviceAddress = value; - return this; - } - - /** - * Builds a Preferences instance. - */ - public Preferences build() { - return new Preferences( - this.mGattOperationTimeoutSeconds, - this.mGattConnectionTimeoutSeconds, - this.mBluetoothToggleTimeoutSeconds, - this.mBluetoothToggleSleepSeconds, - this.mClassicDiscoveryTimeoutSeconds, - this.mNumDiscoverAttempts, - this.mDiscoveryRetrySleepSeconds, - this.mIgnoreDiscoveryError, - this.mSdpTimeoutSeconds, - this.mNumSdpAttempts, - this.mNumCreateBondAttempts, - this.mNumConnectAttempts, - this.mNumWriteAccountKeyAttempts, - this.mToggleBluetoothOnFailure, - this.mBluetoothStateUsesPolling, - this.mBluetoothStatePollingMillis, - this.mNumAttempts, - this.mEnableBrEdrHandover, - this.mBrHandoverDataCharacteristicId, - this.mBluetoothSigDataCharacteristicId, - this.mFirmwareVersionCharacteristicId, - this.mBrTransportBlockDataDescriptorId, - this.mWaitForUuidsAfterBonding, - this.mReceiveUuidsAndBondedEventBeforeClose, - this.mRemoveBondTimeoutSeconds, - this.mRemoveBondSleepMillis, - this.mCreateBondTimeoutSeconds, - this.mHidCreateBondTimeoutSeconds, - this.mProxyTimeoutSeconds, - this.mRejectPhonebookAccess, - this.mRejectMessageAccess, - this.mRejectSimAccess, - this.mWriteAccountKeySleepMillis, - this.mSkipDisconnectingGattBeforeWritingAccountKey, - this.mMoreEventLogForQuality, - this.mRetryGattConnectionAndSecretHandshake, - this.mGattConnectShortTimeoutMs, - this.mGattConnectLongTimeoutMs, - this.mGattConnectShortTimeoutRetryMaxSpentTimeMs, - this.mAddressRotateRetryMaxSpentTimeMs, - this.mPairingRetryDelayMs, - this.mSecretHandshakeShortTimeoutMs, - this.mSecretHandshakeLongTimeoutMs, - this.mSecretHandshakeShortTimeoutRetryMaxSpentTimeMs, - this.mSecretHandshakeLongTimeoutRetryMaxSpentTimeMs, - this.mSecretHandshakeRetryAttempts, - this.mSecretHandshakeRetryGattConnectionMaxSpentTimeMs, - this.mSignalLostRetryMaxSpentTimeMs, - this.mGattConnectionAndSecretHandshakeNoRetryGattError, - this.mRetrySecretHandshakeTimeout, - this.mLogUserManualRetry, - this.mPairFailureCounts, - this.mCachedDeviceAddress, - this.mPossibleCachedDeviceAddress, - this.mSameModelIdPairedDeviceCount, - this.mIsDeviceFinishCheckAddressFromCache, - this.mLogPairWithCachedModelId, - this.mDirectConnectProfileIfModelIdInCache, - this.mAcceptPasskey, - this.mSupportedProfileUuids, - this.mProviderInitiatesBondingIfSupported, - this.mAttemptDirectConnectionWhenPreviouslyBonded, - this.mAutomaticallyReconnectGattWhenNeeded, - this.mSkipConnectingProfiles, - this.mIgnoreUuidTimeoutAfterBonded, - this.mSpecifyCreateBondTransportType, - this.mCreateBondTransportType, - this.mIncreaseIntentFilterPriority, - this.mEvaluatePerformance, - this.mExtraLoggingInformation, - this.mEnableNamingCharacteristic, - this.mEnableFirmwareVersionCharacteristic, - this.mKeepSameAccountKeyWrite, - this.mIsRetroactivePairing, - this.mNumSdpAttemptsAfterBonded, - this.mSupportHidDevice, - this.mEnablePairingWhileDirectlyConnecting, - this.mAcceptConsentForFastPairOne, - this.mGattConnectRetryTimeoutMillis, - this.mEnable128BitCustomGattCharacteristicsId, - this.mEnableSendExceptionStepToValidator, - this.mEnableAdditionalDataTypeWhenActionOverBle, - this.mCheckBondStateWhenSkipConnectingProfiles, - this.mHandlePasskeyConfirmationByUi, - this.mEnablePairFlowShowUiWithoutProfileConnection); - } - } - - /** - * Whether a given Uuid is supported. - */ - public boolean isSupportedProfile(short profileUuid) { - return Constants.PROFILES.containsKey(profileUuid) - && Shorts.contains( - Bytes.toShorts(ByteOrder.BIG_ENDIAN, getSupportedProfileUuids()), profileUuid); - } - - /** - * Information that will be used for logging. - */ - public static class ExtraLoggingInformation { - - private final String mModelId; - - private ExtraLoggingInformation(String modelId) { - this.mModelId = modelId; - } - - /** - * Returns model Id. - */ - public String getModelId() { - return mModelId; - } - - /** - * Converts an instance to a builder. - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Creates a builder for ExtraLoggingInformation. - */ - public static Builder builder() { - return new ExtraLoggingInformation.Builder(); - } - - @Override - public String toString() { - return "ExtraLoggingInformation{" + "modelId=" + mModelId + "}"; - } - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (o instanceof ExtraLoggingInformation) { - Preferences.ExtraLoggingInformation that = (Preferences.ExtraLoggingInformation) o; - return this.mModelId.equals(that.getModelId()); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(mModelId); - } - - /** - * Extra logging information builder. - */ - public static class Builder { - - private String mModelId; - - private Builder() { - } - - private Builder(ExtraLoggingInformation source) { - this.mModelId = source.getModelId(); - } - - /** - * Set model ID. - */ - public Builder setModelId(String modelId) { - this.mModelId = modelId; - return this; - } - - /** - * Builds extra logging information. - */ - public ExtraLoggingInformation build() { - return new ExtraLoggingInformation(mModelId); - } - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Reflect.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Reflect.java deleted file mode 100644 index a2603b5246c231323f7f30809c1d4020d1401c46..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Reflect.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Utilities for calling methods using reflection. The main benefit of using this helper is to avoid - * complications around exception handling when calling methods reflectively. It's not safe to use - * Java 8's multicatch on such exceptions, because the java compiler converts multicatch into - * ReflectiveOperationException in some instances, which doesn't work on older sdk versions. - * Instead, use these utilities and catch ReflectionException. - * - *

    Example usage: - * - *

    {@code
    - * try {
    - *   Reflect.on(btAdapter)
    - *       .withMethod("setScanMode", int.class)
    - *       .invoke(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)
    - * } catch (ReflectionException e) { }
    - * }
    - */ -// TODO(b/202549655): remove existing Reflect usage. New usage is not allowed! No exception! -public final class Reflect { - private final Object mTargetObject; - - private Reflect(Object targetObject) { - this.mTargetObject = targetObject; - } - - /** Creates an instance of this helper to invoke methods on the given target object. */ - public static Reflect on(Object targetObject) { - return new Reflect(targetObject); - } - - /** Finds a method with the given name and parameter types. */ - public ReflectionMethod withMethod(String methodName, Class... paramTypes) - throws ReflectionException { - try { - return new ReflectionMethod(mTargetObject.getClass().getMethod(methodName, paramTypes)); - } catch (NoSuchMethodException e) { - throw new ReflectionException(e); - } - } - - /** Represents an invokable method found reflectively. */ - public final class ReflectionMethod { - private final Method mMethod; - - private ReflectionMethod(Method method) { - this.mMethod = method; - } - - /** - * Invokes this instance method with the given parameters. The called method does not return - * a value. - */ - public void invoke(Object... parameters) throws ReflectionException { - try { - mMethod.invoke(mTargetObject, parameters); - } catch (IllegalAccessException e) { - throw new ReflectionException(e); - } catch (InvocationTargetException e) { - throw new ReflectionException(e); - } - } - - /** - * Invokes this instance method with the given parameters. The called method returns a non - * null value. - */ - public Object get(Object... parameters) throws ReflectionException { - Object value; - try { - value = mMethod.invoke(mTargetObject, parameters); - } catch (IllegalAccessException e) { - throw new ReflectionException(e); - } catch (InvocationTargetException e) { - throw new ReflectionException(e); - } - if (value == null) { - throw new ReflectionException(new NullPointerException()); - } - return value; - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ReflectionException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ReflectionException.java deleted file mode 100644 index 1c20c550a425751f530d6038dd57d85f66ce9f40..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ReflectionException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -/** - * An exception thrown during a reflection operation. Like ReflectiveOperationException, except - * compatible on older API versions. - */ -public final class ReflectionException extends Exception { - ReflectionException(Throwable cause) { - super(cause.getMessage(), cause); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SignalLostException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SignalLostException.java deleted file mode 100644 index 244ee6642346b70bf814cbb8b608f1eb1790ed6f..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SignalLostException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -/** Base class for fast pair signal lost exceptions. */ -public class SignalLostException extends PairingException { - SignalLostException(String message, Exception e) { - super(message); - initCause(e); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SignalRotatedException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SignalRotatedException.java deleted file mode 100644 index d0d2a5d636fab799807b5b619dbfd8d04075158f..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SignalRotatedException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -/** Base class for fast pair signal rotated exceptions. */ -public class SignalRotatedException extends PairingException { - private final String mNewAddress; - - SignalRotatedException(String message, String newAddress, Exception e) { - super(message); - this.mNewAddress = newAddress; - initCause(e); - } - - /** Returns the new BLE address for the model ID. */ - public String getNewAddress() { - return mNewAddress; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SimpleBroadcastReceiver.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SimpleBroadcastReceiver.java deleted file mode 100644 index 7f525a7b313cc3f117548bb523b0b95887f4ea63..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/SimpleBroadcastReceiver.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.annotation.TargetApi; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.util.Log; - -import androidx.annotation.Nullable; - -import com.google.common.util.concurrent.SettableFuture; - -import java.util.Arrays; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Like {@link BroadcastReceiver}, but: - * - *
      - *
    • Simpler to create and register, with a list of actions. - *
    • Implements AutoCloseable. If used as a resource in try-with-resources (available on - * KitKat+), unregisters itself automatically. - *
    • Lets you block waiting for your state transition with {@link #await}. - *
    - */ -// AutoCloseable only available on KitKat+. -@TargetApi(VERSION_CODES.KITKAT) -public abstract class SimpleBroadcastReceiver extends BroadcastReceiver implements AutoCloseable { - - private static final String TAG = SimpleBroadcastReceiver.class.getSimpleName(); - - /** - * Creates a one shot receiver. - */ - public static SimpleBroadcastReceiver oneShotReceiver( - Context context, Preferences preferences, String... actions) { - return new SimpleBroadcastReceiver(context, preferences, actions) { - @Override - protected void onReceive(Intent intent) { - close(); - } - }; - } - - private final Context mContext; - private final SettableFuture mIsClosedFuture = SettableFuture.create(); - private long mAwaitExtendSecond; - - // Nullness checker complains about 'this' being @UnderInitialization - @SuppressWarnings("nullness") - public SimpleBroadcastReceiver( - Context context, Preferences preferences, @Nullable Handler handler, - String... actions) { - Log.v(TAG, this + " listening for actions " + Arrays.toString(actions)); - this.mContext = context; - IntentFilter intentFilter = new IntentFilter(); - if (preferences.getIncreaseIntentFilterPriority()) { - intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - } - for (String action : actions) { - intentFilter.addAction(action); - } - context.registerReceiver(this, intentFilter, /* broadcastPermission= */ null, handler); - } - - public SimpleBroadcastReceiver(Context context, Preferences preferences, String... actions) { - this(context, preferences, /* handler= */ null, actions); - } - - /** - * Any exception thrown by this method will be delivered via {@link #await}. - */ - protected abstract void onReceive(Intent intent) throws Exception; - - @Override - public void onReceive(Context context, Intent intent) { - Log.v(TAG, "Got intent with action= " + intent.getAction()); - try { - onReceive(intent); - } catch (Exception e) { - closeWithError(e); - } - } - - @Override - public void close() { - closeWithError(null); - } - - void closeWithError(@Nullable Exception e) { - try { - mContext.unregisterReceiver(this); - } catch (IllegalArgumentException ignored) { - // Ignore. Happens if you unregister twice. - } - if (e == null) { - mIsClosedFuture.set(null); - } else { - mIsClosedFuture.setException(e); - } - } - - /** - * Extends the awaiting time. - */ - public void extendAwaitSecond(int awaitExtendSecond) { - this.mAwaitExtendSecond = awaitExtendSecond; - } - - /** - * Blocks until this receiver has closed (i.e. the state transition that this receiver is - * interested in has completed). Throws an exception on any error. - */ - public void await(long timeout, TimeUnit timeUnit) - throws InterruptedException, ExecutionException, TimeoutException { - Log.v(TAG, this + " waiting on future for " + timeout + " " + timeUnit); - try { - mIsClosedFuture.get(timeout, timeUnit); - } catch (TimeoutException e) { - if (mAwaitExtendSecond <= 0) { - throw e; - } - Log.i(TAG, "Extend timeout for " + mAwaitExtendSecond + " seconds"); - mIsClosedFuture.get(mAwaitExtendSecond, TimeUnit.SECONDS); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/TdsException.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/TdsException.java deleted file mode 100644 index 7382ff371e4adfc92009122934e56cd300dfd3e2..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/TdsException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import com.android.server.nearby.intdefs.FastPairEventIntDefs.BrEdrHandoverErrorCode; - -import com.google.errorprone.annotations.FormatMethod; - -/** - * Thrown when BR/EDR Handover fails. - */ -public class TdsException extends Exception { - - final @BrEdrHandoverErrorCode int mErrorCode; - - @FormatMethod - TdsException(@BrEdrHandoverErrorCode int errorCode, String format, Object... objects) { - super(String.format(format, objects)); - this.mErrorCode = errorCode; - } - - /** Returns error code. */ - public @BrEdrHandoverErrorCode int getErrorCode() { - return mErrorCode; - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/TimingLogger.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/TimingLogger.java deleted file mode 100644 index 83ee3090267bf511d7ca7bf804a1a3019a442e1f..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/TimingLogger.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import android.os.SystemClock; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; - -import java.util.ArrayDeque; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * A profiler for performance metrics. - * - *

    This class aim to break down the execution time for each steps of process to figure out the - * bottleneck. - */ -public class TimingLogger { - - private static final String TAG = TimingLogger.class.getSimpleName(); - - /** - * The name of this session. - */ - private final String mName; - - private final Preferences mPreference; - - /** - * The ordered timing sequence data. It's composed by a paired {@link Timing} generated from - * {@link #start} and {@link #end}. - */ - private final List mTimings; - - private final long mStartTimestampMs; - - /** Constructor. */ - public TimingLogger(String name, Preferences mPreference) { - this.mName = name; - this.mPreference = mPreference; - mTimings = new CopyOnWriteArrayList<>(); - mStartTimestampMs = SystemClock.elapsedRealtime(); - } - - @VisibleForTesting - List getTimings() { - return mTimings; - } - - /** - * Start a new paired timing. - * - * @param label The split name of paired timing. - */ - public void start(String label) { - if (mPreference.getEvaluatePerformance()) { - mTimings.add(new Timing(label)); - } - } - - /** - * End a paired timing. - */ - public void end() { - if (mPreference.getEvaluatePerformance()) { - mTimings.add(new Timing(Timing.END_LABEL)); - } - } - - /** - * Print out the timing data. - */ - public void dump() { - if (!mPreference.getEvaluatePerformance()) { - return; - } - - calculateTiming(); - Log.i(TAG, mName + "[Exclusive time] / [Total time] ([Timestamp])"); - int indentCount = 0; - for (Timing timing : mTimings) { - if (timing.isEndTiming()) { - indentCount--; - continue; - } - indentCount++; - if (timing.mExclusiveTime == timing.mTotalTime) { - Log.i(TAG, getIndentString(indentCount) + timing.mName + " " + timing.mExclusiveTime - + "ms (" + getRelativeTimestamp(timing.getTimestamp()) + ")"); - } else { - Log.i(TAG, getIndentString(indentCount) + timing.mName + " " + timing.mExclusiveTime - + "ms / " + timing.mTotalTime + "ms (" + getRelativeTimestamp( - timing.getTimestamp()) + ")"); - } - } - Log.i(TAG, mName + "end, " + getTotalTime() + "ms"); - } - - private void calculateTiming() { - ArrayDeque arrayDeque = new ArrayDeque<>(); - for (Timing timing : mTimings) { - if (timing.isStartTiming()) { - arrayDeque.addFirst(timing); - continue; - } - - Timing timingStart = arrayDeque.removeFirst(); - final long time = timing.mTimestamp - timingStart.mTimestamp; - timingStart.mExclusiveTime += time; - timingStart.mTotalTime += time; - if (!arrayDeque.isEmpty()) { - arrayDeque.peekFirst().mExclusiveTime -= time; - } - } - } - - private String getIndentString(int indentCount) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < indentCount; i++) { - sb.append(" "); - } - return sb.toString(); - } - - private long getRelativeTimestamp(long timestamp) { - return timestamp - mTimings.get(0).mTimestamp; - } - - @VisibleForTesting - long getTotalTime() { - return mTimings.get(mTimings.size() - 1).mTimestamp - mTimings.get(0).mTimestamp; - } - - /** - * Gets the current latency since this object was created. - */ - public long getLatencyMs() { - return SystemClock.elapsedRealtime() - mStartTimestampMs; - } - - @VisibleForTesting - static class Timing { - - private static final String END_LABEL = "END_LABEL"; - - /** - * The name of this paired timing. - */ - private final String mName; - - /** - * System uptime in millisecond. - */ - private final long mTimestamp; - - /** - * The execution time exclude inner split timings. - */ - private long mExclusiveTime; - - /** - * The execution time within a start and an end timing. - */ - private long mTotalTime; - - private Timing(String name) { - this.mName = name; - mTimestamp = SystemClock.elapsedRealtime(); - mExclusiveTime = 0; - mTotalTime = 0; - } - - @VisibleForTesting - String getName() { - return mName; - } - - @VisibleForTesting - long getTimestamp() { - return mTimestamp; - } - - @VisibleForTesting - long getExclusiveTime() { - return mExclusiveTime; - } - - @VisibleForTesting - long getTotalTime() { - return mTotalTime; - } - - @VisibleForTesting - boolean isStartTiming() { - return !isEndTiming(); - } - - @VisibleForTesting - boolean isEndTiming() { - return END_LABEL.equals(mName); - } - } - - /** - * This class ensures each split timing is paired with a start and an end timing. - */ - public static class ScopedTiming implements AutoCloseable { - - private final TimingLogger mTimingLogger; - - public ScopedTiming(TimingLogger logger, String label) { - mTimingLogger = logger; - mTimingLogger.start(label); - } - - @Override - public void close() { - mTimingLogger.end(); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ToggleBluetoothTask.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ToggleBluetoothTask.java deleted file mode 100644 index 41ac9f512fbee66ec2b01e89f227f9c509fc91d5..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/ToggleBluetoothTask.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.fastpair; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -/** Task for toggling Bluetooth on and back off again. */ -interface ToggleBluetoothTask { - - /** - * Toggles the bluetooth adapter off and back on again to help improve connection reliability. - * - * @throws InterruptedException when waiting for the bluetooth adapter's state to be set has - * been interrupted. - * @throws ExecutionException when waiting for the bluetooth adapter's state to be set has - * failed. - * @throws TimeoutException when the bluetooth adapter's state fails to be set on or off. - */ - void toggleBluetooth() throws InterruptedException, ExecutionException, TimeoutException; -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattConnection.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattConnection.java deleted file mode 100644 index de131e4ee6ce4579b3964b20f5c33113e47bd101..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattConnection.java +++ /dev/null @@ -1,781 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.gatt; - -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.BluetoothStatusCodes; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; - -import com.android.server.nearby.common.bluetooth.BluetoothConsts; -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.BluetoothGattException; -import com.android.server.nearby.common.bluetooth.BluetoothTimeoutException; -import com.android.server.nearby.common.bluetooth.ReservedUuids; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattHelper.ConnectionOptions; -import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattHelper.OperationType; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothDevice; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothGattWrapper; -import com.android.server.nearby.common.bluetooth.util.BluetoothGattUtils; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.Operation; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.SynchronousOperation; - -import com.google.common.base.Preconditions; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; - -/** - * Gatt connection to a Bluetooth device. - */ -public class BluetoothGattConnection implements AutoCloseable { - - private static final String TAG = BluetoothGattConnection.class.getSimpleName(); - - @VisibleForTesting - static final long OPERATION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1); - @VisibleForTesting - static final long SLOW_OPERATION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10); - - @VisibleForTesting - static final int GATT_INTERNAL_ERROR = 129; - @VisibleForTesting - static final int GATT_ERROR = 133; - - private final BluetoothGattWrapper mGatt; - private final BluetoothOperationExecutor mBluetoothOperationExecutor; - private final ConnectionOptions mConnectionOptions; - - private volatile boolean mServicesDiscovered = false; - - private volatile boolean mIsConnected = false; - - private volatile int mMtu = BluetoothConsts.DEFAULT_MTU; - - private final ConcurrentMap mChangeObservers = - new ConcurrentHashMap<>(); - - private final List mCloseListeners = new ArrayList<>(); - - private long mOperationTimeoutMillis = OPERATION_TIMEOUT_MILLIS; - - BluetoothGattConnection( - BluetoothGattWrapper gatt, - BluetoothOperationExecutor bluetoothOperationExecutor, - ConnectionOptions connectionOptions) { - mGatt = gatt; - mBluetoothOperationExecutor = bluetoothOperationExecutor; - mConnectionOptions = connectionOptions; - } - - /** - * Set operation timeout. - */ - public void setOperationTimeout(long timeoutMillis) { - Preconditions.checkArgument(timeoutMillis > 0, "invalid time out value"); - mOperationTimeoutMillis = timeoutMillis; - } - - /** - * Returns connected device. - */ - public BluetoothDevice getDevice() { - return mGatt.getDevice(); - } - - public ConnectionOptions getConnectionOptions() { - return mConnectionOptions; - } - - public boolean isConnected() { - return mIsConnected; - } - - /** - * Get service. - */ - public BluetoothGattService getService(UUID uuid) throws BluetoothException { - Log.d(TAG, String.format("Getting service %s.", uuid)); - if (!mServicesDiscovered) { - discoverServices(); - } - BluetoothGattService match = null; - for (BluetoothGattService service : mGatt.getServices()) { - if (service.getUuid().equals(uuid)) { - if (match != null) { - throw new BluetoothException( - String.format("More than one service %s found on device %s.", - uuid, - mGatt.getDevice())); - } - match = service; - } - } - if (match == null) { - throw new BluetoothException(String.format("Service %s not found on device %s.", - uuid, - mGatt.getDevice())); - } - Log.d(TAG, "Service found."); - return match; - } - - /** - * Returns a list of all characteristics under a given service UUID. - */ - private List getCharacteristics(UUID serviceUuid) - throws BluetoothException { - if (!mServicesDiscovered) { - discoverServices(); - } - ArrayList characteristics = new ArrayList<>(); - for (BluetoothGattService service : mGatt.getServices()) { - // Add all characteristics under this service if its service UUID matches. - if (service.getUuid().equals(serviceUuid)) { - characteristics.addAll(service.getCharacteristics()); - } - } - return characteristics; - } - - /** - * Get characteristic. - */ - public BluetoothGattCharacteristic getCharacteristic(UUID serviceUuid, - UUID characteristicUuid) throws BluetoothException { - Log.d(TAG, String.format("Getting characteristic %s on service %s.", characteristicUuid, - serviceUuid)); - BluetoothGattCharacteristic match = null; - for (BluetoothGattCharacteristic characteristic : getCharacteristics(serviceUuid)) { - if (characteristic.getUuid().equals(characteristicUuid)) { - if (match != null) { - throw new BluetoothException(String.format( - "More than one characteristic %s found on service %s on device %s.", - characteristicUuid, - serviceUuid, - mGatt.getDevice())); - } - match = characteristic; - } - } - if (match == null) { - throw new BluetoothException(String.format( - "Characteristic %s not found on service %s of device %s.", - characteristicUuid, - serviceUuid, - mGatt.getDevice())); - } - Log.d(TAG, "Characteristic found."); - return match; - } - - /** - * Get descriptor. - */ - public BluetoothGattDescriptor getDescriptor(UUID serviceUuid, - UUID characteristicUuid, UUID descriptorUuid) throws BluetoothException { - Log.d(TAG, String.format("Getting descriptor %s on characteristic %s on service %s.", - descriptorUuid, characteristicUuid, serviceUuid)); - BluetoothGattDescriptor match = null; - for (BluetoothGattDescriptor descriptor : - getCharacteristic(serviceUuid, characteristicUuid).getDescriptors()) { - if (descriptor.getUuid().equals(descriptorUuid)) { - if (match != null) { - throw new BluetoothException(String.format("More than one descriptor %s found " - + "on characteristic %s service %s on device %s.", - descriptorUuid, - characteristicUuid, - serviceUuid, - mGatt.getDevice())); - } - match = descriptor; - } - } - if (match == null) { - throw new BluetoothException(String.format( - "Descriptor %s not found on characteristic %s on service %s of device %s.", - descriptorUuid, - characteristicUuid, - serviceUuid, - mGatt.getDevice())); - } - Log.d(TAG, "Descriptor found."); - return match; - } - - /** - * Discover services. - */ - public void discoverServices() throws BluetoothException { - mBluetoothOperationExecutor.execute( - new SynchronousOperation(OperationType.DISCOVER_SERVICES) { - @Nullable - @Override - public Void call() throws BluetoothException { - if (mServicesDiscovered) { - return null; - } - boolean forceRefresh = false; - try { - discoverServicesInternal(); - } catch (BluetoothException e) { - if (!(e instanceof BluetoothGattException)) { - throw e; - } - int errorCode = ((BluetoothGattException) e).getGattErrorCode(); - if (errorCode != GATT_ERROR && errorCode != GATT_INTERNAL_ERROR) { - throw e; - } - Log.e(TAG, e.getMessage() - + "\n Ignore the gatt error for post MNC apis and force " - + "a refresh"); - forceRefresh = true; - } - - forceRefreshServiceCacheIfNeeded(forceRefresh); - - mServicesDiscovered = true; - - return null; - } - }); - } - - private void discoverServicesInternal() throws BluetoothException { - Log.i(TAG, "Starting services discovery."); - long startTimeMillis = System.currentTimeMillis(); - try { - mBluetoothOperationExecutor.execute( - new Operation(OperationType.DISCOVER_SERVICES_INTERNAL, mGatt) { - @Override - public void run() throws BluetoothException { - boolean success = mGatt.discoverServices(); - if (!success) { - throw new BluetoothException( - "gatt.discoverServices returned false."); - } - } - }, - SLOW_OPERATION_TIMEOUT_MILLIS); - Log.i(TAG, String.format("Services discovered successfully in %s ms.", - System.currentTimeMillis() - startTimeMillis)); - } catch (BluetoothException e) { - if (e instanceof BluetoothGattException) { - throw new BluetoothGattException(String.format( - "Failed to discover services on device: %s.", - mGatt.getDevice()), ((BluetoothGattException) e).getGattErrorCode(), e); - } else { - throw new BluetoothException(String.format( - "Failed to discover services on device: %s.", - mGatt.getDevice()), e); - } - } - } - - private boolean hasDynamicServices() { - BluetoothGattService gattService = - mGatt.getService(ReservedUuids.Services.GENERIC_ATTRIBUTE); - if (gattService != null) { - BluetoothGattCharacteristic serviceChange = - gattService.getCharacteristic(ReservedUuids.Characteristics.SERVICE_CHANGE); - if (serviceChange != null) { - return true; - } - } - - // Check whether the server contains a self defined service dynamic characteristic. - gattService = mGatt.getService(BluetoothConsts.SERVICE_DYNAMIC_SERVICE); - if (gattService != null) { - BluetoothGattCharacteristic serviceChange = - gattService.getCharacteristic(BluetoothConsts.SERVICE_DYNAMIC_CHARACTERISTIC); - if (serviceChange != null) { - return true; - } - } - - return false; - } - - private void forceRefreshServiceCacheIfNeeded(boolean forceRefresh) throws BluetoothException { - if (mGatt.getDevice().getBondState() != BluetoothDevice.BOND_BONDED) { - // Device is not bonded, so services should not have been cached. - return; - } - - if (!forceRefresh && !hasDynamicServices()) { - return; - } - Log.i(TAG, "Forcing a refresh of local cache of GATT services"); - boolean success = mGatt.refresh(); - if (!success) { - throw new BluetoothException("gatt.refresh returned false."); - } - discoverServicesInternal(); - } - - /** - * Read characteristic. - */ - public byte[] readCharacteristic(UUID serviceUuid, UUID characteristicUuid) - throws BluetoothException { - return readCharacteristic(getCharacteristic(serviceUuid, characteristicUuid)); - } - - /** - * Read characteristic. - */ - public byte[] readCharacteristic(final BluetoothGattCharacteristic characteristic) - throws BluetoothException { - try { - return mBluetoothOperationExecutor.executeNonnull( - new Operation(OperationType.READ_CHARACTERISTIC, mGatt, - characteristic) { - @Override - public void run() throws BluetoothException { - boolean success = mGatt.readCharacteristic(characteristic); - if (!success) { - throw new BluetoothException( - "gatt.readCharacteristic returned false."); - } - } - }, - mOperationTimeoutMillis); - } catch (BluetoothException e) { - throw new BluetoothException(String.format( - "Failed to read %s on device %s.", - BluetoothGattUtils.toString(characteristic), - mGatt.getDevice()), e); - } - } - - /** - * Writes Characteristic. - */ - public void writeCharacteristic(UUID serviceUuid, UUID characteristicUuid, byte[] value) - throws BluetoothException { - writeCharacteristic(getCharacteristic(serviceUuid, characteristicUuid), value); - } - - /** - * Writes Characteristic. - */ - public void writeCharacteristic(final BluetoothGattCharacteristic characteristic, - final byte[] value) throws BluetoothException { - Log.d(TAG, String.format("Writing %d bytes on %s on device %s.", - value.length, - BluetoothGattUtils.toString(characteristic), - mGatt.getDevice())); - if ((characteristic.getProperties() & (BluetoothGattCharacteristic.PROPERTY_WRITE - | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) == 0) { - throw new BluetoothException(String.format("%s is not writable!", characteristic)); - } - try { - mBluetoothOperationExecutor.execute( - new Operation(OperationType.WRITE_CHARACTERISTIC, mGatt, characteristic) { - @Override - public void run() throws BluetoothException { - int writeCharacteristicResponseCode = mGatt.writeCharacteristic( - characteristic, value, characteristic.getWriteType()); - if (writeCharacteristicResponseCode != BluetoothStatusCodes.SUCCESS) { - throw new BluetoothException( - "gatt.writeCharacteristic returned " - + writeCharacteristicResponseCode); - } - } - }, - mOperationTimeoutMillis); - } catch (BluetoothException e) { - throw new BluetoothException(String.format( - "Failed to write %s on device %s.", - BluetoothGattUtils.toString(characteristic), - mGatt.getDevice()), e); - } - Log.d(TAG, "Writing characteristic done."); - } - - /** - * Reads descriptor. - */ - public byte[] readDescriptor(UUID serviceUuid, UUID characteristicUuid, UUID descriptorUuid) - throws BluetoothException { - return readDescriptor(getDescriptor(serviceUuid, characteristicUuid, descriptorUuid)); - } - - /** - * Reads descriptor. - */ - public byte[] readDescriptor(final BluetoothGattDescriptor descriptor) - throws BluetoothException { - try { - return mBluetoothOperationExecutor.executeNonnull( - new Operation(OperationType.READ_DESCRIPTOR, mGatt, descriptor) { - @Override - public void run() throws BluetoothException { - boolean success = mGatt.readDescriptor(descriptor); - if (!success) { - throw new BluetoothException("gatt.readDescriptor returned false."); - } - } - }, - mOperationTimeoutMillis); - } catch (BluetoothException e) { - throw new BluetoothException(String.format( - "Failed to read %s on %s on device %s.", - descriptor.getUuid(), - BluetoothGattUtils.toString(descriptor), - mGatt.getDevice()), e); - } - } - - /** - * Writes descriptor. - */ - public void writeDescriptor(UUID serviceUuid, UUID characteristicUuid, UUID descriptorUuid, - byte[] value) throws BluetoothException { - writeDescriptor(getDescriptor(serviceUuid, characteristicUuid, descriptorUuid), value); - } - - /** - * Writes descriptor. - */ - public void writeDescriptor(final BluetoothGattDescriptor descriptor, final byte[] value) - throws BluetoothException { - Log.d(TAG, String.format( - "Writing %d bytes on %s on device %s.", - value.length, - BluetoothGattUtils.toString(descriptor), - mGatt.getDevice())); - long startTimeMillis = System.currentTimeMillis(); - try { - mBluetoothOperationExecutor.execute( - new Operation(OperationType.WRITE_DESCRIPTOR, mGatt, descriptor) { - @Override - public void run() throws BluetoothException { - int writeDescriptorResponseCode = mGatt.writeDescriptor(descriptor, - value); - if (writeDescriptorResponseCode != BluetoothStatusCodes.SUCCESS) { - throw new BluetoothException( - "gatt.writeDescriptor returned " - + writeDescriptorResponseCode); - } - } - }, - mOperationTimeoutMillis); - Log.d(TAG, String.format("Writing descriptor done in %s ms.", - System.currentTimeMillis() - startTimeMillis)); - } catch (BluetoothException e) { - throw new BluetoothException(String.format( - "Failed to write %s on device %s.", - BluetoothGattUtils.toString(descriptor), - mGatt.getDevice()), e); - } - } - - /** - * Reads remote Rssi. - */ - public int readRemoteRssi() throws BluetoothException { - try { - return mBluetoothOperationExecutor.executeNonnull( - new Operation(OperationType.READ_RSSI, mGatt) { - @Override - public void run() throws BluetoothException { - boolean success = mGatt.readRemoteRssi(); - if (!success) { - throw new BluetoothException("gatt.readRemoteRssi returned false."); - } - } - }, - mOperationTimeoutMillis); - } catch (BluetoothException e) { - throw new BluetoothException( - String.format("Failed to read rssi on device %s.", mGatt.getDevice()), e); - } - } - - public int getMtu() { - return mMtu; - } - - /** - * Get max data packet size. - */ - public int getMaxDataPacketSize() { - // Per BT specs (3.2.9), only MTU - 3 bytes can be used to transmit data - return mMtu - 3; - } - - /** Set notification enabled or disabled. */ - @VisibleForTesting - public void setNotificationEnabled(BluetoothGattCharacteristic characteristic, boolean enabled) - throws BluetoothException { - boolean isIndication; - int properties = characteristic.getProperties(); - if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) { - isIndication = false; - } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) { - isIndication = true; - } else { - throw new BluetoothException(String.format( - "%s on device %s supports neither notifications nor indications.", - BluetoothGattUtils.toString(characteristic), - mGatt.getDevice())); - } - BluetoothGattDescriptor clientConfigDescriptor = - characteristic.getDescriptor( - ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION); - if (clientConfigDescriptor == null) { - throw new BluetoothException(String.format( - "%s on device %s is missing client config descriptor.", - BluetoothGattUtils.toString(characteristic), - mGatt.getDevice())); - } - long startTime = System.currentTimeMillis(); - Log.d(TAG, String.format("%s %s on characteristic %s.", enabled ? "Enabling" : "Disabling", - isIndication ? "indication" : "notification", characteristic.getUuid())); - - if (enabled) { - mGatt.setCharacteristicNotification(characteristic, enabled); - } - writeDescriptor(clientConfigDescriptor, - enabled - ? (isIndication - ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : - BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) - : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); - if (!enabled) { - mGatt.setCharacteristicNotification(characteristic, enabled); - } - - Log.d(TAG, String.format("Done in %d ms.", System.currentTimeMillis() - startTime)); - } - - /** - * Enables notification. - */ - public ChangeObserver enableNotification(UUID serviceUuid, UUID characteristicUuid) - throws BluetoothException { - return enableNotification(getCharacteristic(serviceUuid, characteristicUuid)); - } - - /** - * Enables notification. - */ - public ChangeObserver enableNotification(final BluetoothGattCharacteristic characteristic) - throws BluetoothException { - return mBluetoothOperationExecutor.executeNonnull( - new SynchronousOperation( - OperationType.NOTIFICATION_CHANGE, - characteristic) { - @Override - public ChangeObserver call() throws BluetoothException { - ChangeObserver changeObserver = new ChangeObserver(); - mChangeObservers.put(characteristic, changeObserver); - setNotificationEnabled(characteristic, true); - return changeObserver; - } - }); - } - - /** - * Disables notification. - */ - public void disableNotification(UUID serviceUuid, UUID characteristicUuid) - throws BluetoothException { - disableNotification(getCharacteristic(serviceUuid, characteristicUuid)); - } - - /** - * Disables notification. - */ - public void disableNotification(final BluetoothGattCharacteristic characteristic) - throws BluetoothException { - mBluetoothOperationExecutor.execute( - new SynchronousOperation( - OperationType.NOTIFICATION_CHANGE, - characteristic) { - @Nullable - @Override - public Void call() throws BluetoothException { - setNotificationEnabled(characteristic, false); - mChangeObservers.remove(characteristic); - return null; - } - }); - } - - /** - * Adds a close listener. - */ - public void addCloseListener(ConnectionCloseListener listener) { - mCloseListeners.add(listener); - if (!mIsConnected) { - listener.onClose(); - return; - } - } - - /** - * Removes a close listener. - */ - public void removeCloseListener(ConnectionCloseListener listener) { - mCloseListeners.remove(listener); - } - - /** onCharacteristicChanged callback. */ - public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic, byte[] value) { - ChangeObserver observer = mChangeObservers.get(characteristic); - if (observer == null) { - return; - } - observer.onValueChange(value); - } - - @Override - public void close() throws BluetoothException { - Log.d(TAG, "close"); - try { - if (!mIsConnected) { - // Don't call disconnect on a closed connection, since Android framework won't - // provide any feedback. - return; - } - mBluetoothOperationExecutor.execute( - new Operation(OperationType.DISCONNECT, mGatt.getDevice()) { - @Override - public void run() throws BluetoothException { - mGatt.disconnect(); - } - }, mOperationTimeoutMillis); - } finally { - mGatt.close(); - } - } - - /** onConnected callback. */ - public void onConnected() { - Log.d(TAG, "onConnected"); - mIsConnected = true; - } - - /** onClosed callback. */ - public void onClosed() { - Log.d(TAG, "onClosed"); - if (!mIsConnected) { - return; - } - mIsConnected = false; - for (ConnectionCloseListener listener : mCloseListeners) { - listener.onClose(); - } - mGatt.close(); - } - - /** - * Observer to wait or be called back when value change. - */ - public static class ChangeObserver { - - private final BlockingDeque mValues = new LinkedBlockingDeque(); - - @GuardedBy("mValues") - @Nullable - private volatile CharacteristicChangeListener mListener; - - /** - * Set listener. - */ - public void setListener(@Nullable CharacteristicChangeListener listener) { - synchronized (mValues) { - mListener = listener; - if (listener != null) { - byte[] value; - while ((value = mValues.poll()) != null) { - listener.onValueChange(value); - } - } - } - } - - /** - * onValueChange callback. - */ - public void onValueChange(byte[] newValue) { - synchronized (mValues) { - CharacteristicChangeListener listener = mListener; - if (listener == null) { - mValues.add(newValue); - } else { - listener.onValueChange(newValue); - } - } - } - - /** - * Waits for update for a given time. - */ - public byte[] waitForUpdate(long timeoutMillis) throws BluetoothException { - byte[] result; - try { - result = mValues.poll(timeoutMillis, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new BluetoothException("Operation interrupted."); - } - if (result == null) { - throw new BluetoothTimeoutException( - String.format("Operation timed out after %dms", timeoutMillis)); - } - return result; - } - } - - /** - * Listener for characteristic data changes over notifications or indications. - */ - public interface CharacteristicChangeListener { - - /** - * onValueChange callback. - */ - void onValueChange(byte[] newValue); - } - - /** - * Listener for connection close events. - */ - public interface ConnectionCloseListener { - - /** - * onClose callback. - */ - void onClose(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java deleted file mode 100644 index 18a9f5f3bbbef35ee98e34e9ab50f2693c4ed9ea..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/gatt/BluetoothGattHelper.java +++ /dev/null @@ -1,690 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.gatt; - -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanSettings; -import android.content.Context; -import android.os.ParcelUuid; -import android.util.Log; - -import androidx.annotation.IntDef; -import androidx.annotation.VisibleForTesting; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothDevice; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothGattCallback; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothGattWrapper; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le.BluetoothLeScanner; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le.ScanCallback; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le.ScanResult; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.BluetoothOperationTimeoutException; -import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.Operation; - -import com.google.common.base.Preconditions; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; - -/** - * Wrapper of {@link BluetoothGattWrapper} that provides blocking methods, errors and timeout - * handling. - */ -@SuppressWarnings("Guava") // java.util.Optional is not available until API 24 -public class BluetoothGattHelper { - - private static final String TAG = BluetoothGattHelper.class.getSimpleName(); - - @VisibleForTesting - static final long LOW_LATENCY_SCAN_MILLIS = TimeUnit.SECONDS.toMillis(5); - private static final long POLL_INTERVAL_MILLIS = 5L /* milliseconds */; - - /** - * BT operation types that can be in flight. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - OperationType.SCAN, - OperationType.CONNECT, - OperationType.DISCOVER_SERVICES, - OperationType.DISCOVER_SERVICES_INTERNAL, - OperationType.NOTIFICATION_CHANGE, - OperationType.READ_CHARACTERISTIC, - OperationType.WRITE_CHARACTERISTIC, - OperationType.READ_DESCRIPTOR, - OperationType.WRITE_DESCRIPTOR, - OperationType.READ_RSSI, - OperationType.WRITE_RELIABLE, - OperationType.CHANGE_MTU, - OperationType.DISCONNECT, - }) - public @interface OperationType { - int SCAN = 0; - int CONNECT = 1; - int DISCOVER_SERVICES = 2; - int DISCOVER_SERVICES_INTERNAL = 3; - int NOTIFICATION_CHANGE = 4; - int READ_CHARACTERISTIC = 5; - int WRITE_CHARACTERISTIC = 6; - int READ_DESCRIPTOR = 7; - int WRITE_DESCRIPTOR = 8; - int READ_RSSI = 9; - int WRITE_RELIABLE = 10; - int CHANGE_MTU = 11; - int DISCONNECT = 12; - } - - @VisibleForTesting - final ScanCallback mScanCallback = new InternalScanCallback(); - @VisibleForTesting - final BluetoothGattCallback mBluetoothGattCallback = - new InternalBluetoothGattCallback(); - @VisibleForTesting - final ConcurrentMap mConnections = - new ConcurrentHashMap<>(); - - private final Context mApplicationContext; - private final BluetoothAdapter mBluetoothAdapter; - private final BluetoothOperationExecutor mBluetoothOperationExecutor; - - @VisibleForTesting - BluetoothGattHelper( - Context applicationContext, - BluetoothAdapter bluetoothAdapter, - BluetoothOperationExecutor bluetoothOperationExecutor) { - mApplicationContext = applicationContext; - mBluetoothAdapter = bluetoothAdapter; - mBluetoothOperationExecutor = bluetoothOperationExecutor; - } - - public BluetoothGattHelper(Context applicationContext, BluetoothAdapter bluetoothAdapter) { - this( - Preconditions.checkNotNull(applicationContext), - Preconditions.checkNotNull(bluetoothAdapter), - new BluetoothOperationExecutor(5)); - } - - /** - * Auto-connects a serice Uuid. - */ - public BluetoothGattConnection autoConnect(final UUID serviceUuid) throws BluetoothException { - Log.d(TAG, String.format("Starting autoconnection to a device advertising service %s.", - serviceUuid)); - BluetoothDevice device = null; - int retries = 3; - final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner(); - if (scanner == null) { - throw new BluetoothException("Bluetooth is disabled or LE is not supported."); - } - final ScanFilter serviceFilter = new ScanFilter.Builder() - .setServiceUuid(new ParcelUuid(serviceUuid)) - .build(); - ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder() - .setReportDelay(0); - final ScanSettings scanSettingsLowLatency = scanSettingsBuilder - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .build(); - final ScanSettings scanSettingsLowPower = scanSettingsBuilder - .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) - .build(); - while (true) { - long startTimeMillis = System.currentTimeMillis(); - try { - Log.d(TAG, "Starting low latency scanning."); - device = - mBluetoothOperationExecutor.executeNonnull( - new Operation(OperationType.SCAN) { - @Override - public void run() throws BluetoothException { - scanner.startScan(Arrays.asList(serviceFilter), - scanSettingsLowLatency, mScanCallback); - } - }, LOW_LATENCY_SCAN_MILLIS); - } catch (BluetoothOperationTimeoutException e) { - Log.d(TAG, String.format( - "Cannot find a nearby device in low latency scanning after %s ms.", - LOW_LATENCY_SCAN_MILLIS)); - } finally { - scanner.stopScan(mScanCallback); - } - if (device == null) { - Log.d(TAG, "Starting low power scanning."); - try { - device = mBluetoothOperationExecutor.executeNonnull( - new Operation(OperationType.SCAN) { - @Override - public void run() throws BluetoothException { - scanner.startScan(Arrays.asList(serviceFilter), - scanSettingsLowPower, mScanCallback); - } - }); - } finally { - scanner.stopScan(mScanCallback); - } - } - Log.d(TAG, String.format("Scanning done in %d ms. Found device %s.", - System.currentTimeMillis() - startTimeMillis, device)); - - try { - return connect(device); - } catch (BluetoothException e) { - retries--; - if (retries == 0) { - throw e; - } else { - Log.d(TAG, String.format( - "Connection failed: %s. Retrying %d more times.", e, retries)); - } - } - } - } - - /** - * Connects to a device using default connection options. - */ - public BluetoothGattConnection connect(BluetoothDevice bluetoothDevice) - throws BluetoothException { - return connect(bluetoothDevice, ConnectionOptions.builder().build()); - } - - /** - * Connects to a device using specifies connection options. - */ - public BluetoothGattConnection connect( - BluetoothDevice bluetoothDevice, ConnectionOptions options) throws BluetoothException { - Log.d(TAG, String.format("Connecting to device %s.", bluetoothDevice)); - long startTimeMillis = System.currentTimeMillis(); - - Operation connectOperation = - new Operation(OperationType.CONNECT, bluetoothDevice) { - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private boolean mIsCanceled = false; - - @GuardedBy("mLock") - @Nullable(/* null before operation is executed */) - private BluetoothGattWrapper mBluetoothGatt; - - @Override - public void run() throws BluetoothException { - synchronized (mLock) { - if (mIsCanceled) { - return; - } - BluetoothGattWrapper bluetoothGattWrapper; - Log.d(TAG, "Use LE transport"); - bluetoothGattWrapper = - bluetoothDevice.connectGatt( - mApplicationContext, - options.autoConnect(), - mBluetoothGattCallback, - android.bluetooth.BluetoothDevice.TRANSPORT_LE); - if (bluetoothGattWrapper == null) { - throw new BluetoothException("connectGatt() returned null."); - } - - try { - // Set connection priority without waiting for connection callback. - // Per code, btif_gatt_client.c, when priority is set before - // connection, this sets preferred connection parameters that will - // be used during the connection establishment. - Optional connectionPriorityOption = - options.connectionPriority(); - if (connectionPriorityOption.isPresent()) { - // requestConnectionPriority can only be called when - // BluetoothGatt is connected to the system BluetoothGatt - // service (see android/bluetooth/BluetoothGatt.java code). - // However, there is no callback to the app to inform when this - // is done. requestConnectionPriority will returns false with no - // side-effect before the service is connected, so we just poll - // here until true is returned. - int connectionPriority = connectionPriorityOption.get(); - long startTimeMillis = System.currentTimeMillis(); - while (!bluetoothGattWrapper.requestConnectionPriority( - connectionPriority)) { - if (System.currentTimeMillis() - startTimeMillis - > options.connectionTimeoutMillis()) { - throw new BluetoothException( - String.format( - Locale.US, - "Failed to set connectionPriority " - + "after %dms.", - options.connectionTimeoutMillis())); - } - try { - Thread.sleep(POLL_INTERVAL_MILLIS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new BluetoothException( - "connect() operation interrupted."); - } - } - } - } catch (Exception e) { - // Make sure to clean connection. - bluetoothGattWrapper.disconnect(); - bluetoothGattWrapper.close(); - throw e; - } - - BluetoothGattConnection connection = new BluetoothGattConnection( - bluetoothGattWrapper, mBluetoothOperationExecutor, options); - mConnections.put(bluetoothGattWrapper, connection); - mBluetoothGatt = bluetoothGattWrapper; - } - } - - @Override - public void cancel() { - // Clean connection if connection times out. - synchronized (mLock) { - if (mIsCanceled) { - return; - } - mIsCanceled = true; - BluetoothGattWrapper bluetoothGattWrapper = mBluetoothGatt; - if (bluetoothGattWrapper == null) { - return; - } - mConnections.remove(bluetoothGattWrapper); - bluetoothGattWrapper.disconnect(); - bluetoothGattWrapper.close(); - } - } - }; - BluetoothGattConnection result; - if (options.autoConnect()) { - result = mBluetoothOperationExecutor.executeNonnull(connectOperation); - } else { - result = - mBluetoothOperationExecutor.executeNonnull( - connectOperation, options.connectionTimeoutMillis()); - } - Log.d(TAG, String.format("Connection success in %d ms.", - System.currentTimeMillis() - startTimeMillis)); - return result; - } - - private BluetoothGattConnection getConnectionByGatt(BluetoothGattWrapper gatt) - throws BluetoothException { - BluetoothGattConnection connection = mConnections.get(gatt); - if (connection == null) { - throw new BluetoothException("Receive callback on unexpected device: " + gatt); - } - return connection; - } - - private class InternalBluetoothGattCallback extends BluetoothGattCallback { - - @Override - public void onConnectionStateChange(BluetoothGattWrapper gatt, int status, int newState) { - BluetoothGattConnection connection; - BluetoothDevice device = gatt.getDevice(); - switch (newState) { - case BluetoothGatt.STATE_CONNECTED: { - connection = mConnections.get(gatt); - if (connection == null) { - Log.w(TAG, String.format( - "Received unexpected successful connection for dev %s! Ignoring.", - device)); - break; - } - - Operation operation = - new Operation<>(OperationType.CONNECT, device); - if (status != BluetoothGatt.GATT_SUCCESS) { - mConnections.remove(gatt); - gatt.disconnect(); - gatt.close(); - mBluetoothOperationExecutor.notifyCompletion(operation, status, null); - break; - } - - // Process connection options - ConnectionOptions options = connection.getConnectionOptions(); - Optional mtuOption = options.mtu(); - if (mtuOption.isPresent()) { - // Requesting MTU and waiting for MTU callback. - boolean success = gatt.requestMtu(mtuOption.get()); - if (!success) { - mBluetoothOperationExecutor.notifyFailure(operation, - new BluetoothException(String.format(Locale.US, - "Failed to request MTU of %d for dev %s: " - + "returned false.", - mtuOption.get(), device))); - // Make sure to clean connection. - mConnections.remove(gatt); - gatt.disconnect(); - gatt.close(); - } - break; - } - - // Connection successful - connection.onConnected(); - mBluetoothOperationExecutor.notifyCompletion(operation, status, connection); - break; - } - case BluetoothGatt.STATE_DISCONNECTED: { - connection = mConnections.remove(gatt); - if (connection == null) { - Log.w(TAG, String.format("Received unexpected disconnection" - + " for device %s! Ignoring.", device)); - break; - } - if (!connection.isConnected()) { - // This is a failed connection attempt - if (status == BluetoothGatt.GATT_SUCCESS) { - // This is weird... considering this as a failure - Log.w(TAG, String.format( - "Received a success for a failed connection " - + "attempt for device %s! Ignoring.", device)); - status = BluetoothGatt.GATT_FAILURE; - } - mBluetoothOperationExecutor - .notifyCompletion(new Operation( - OperationType.CONNECT, device), status, null); - // Clean Gatt object in every case. - gatt.disconnect(); - gatt.close(); - break; - } - connection.onClosed(); - mBluetoothOperationExecutor.notifyCompletion( - new Operation<>(OperationType.DISCONNECT, device), status); - break; - } - default: - Log.e(TAG, "Unexpected connection state: " + newState); - } - } - - @Override - public void onMtuChanged(BluetoothGattWrapper gatt, int mtu, int status) { - BluetoothGattConnection connection = mConnections.get(gatt); - BluetoothDevice device = gatt.getDevice(); - if (connection == null) { - Log.w(TAG, String.format( - "Received unexpected MTU change for device %s! Ignoring.", device)); - return; - } - if (connection.isConnected()) { - // This is the callback for the deprecated BluetoothGattConnection.requestMtu. - mBluetoothOperationExecutor.notifyCompletion( - new Operation<>(OperationType.CHANGE_MTU, gatt), status, mtu); - } else { - // This is the callback when requesting MTU right after connecting. - connection.onConnected(); - mBluetoothOperationExecutor.notifyCompletion( - new Operation<>(OperationType.CONNECT, device), status, connection); - if (status != BluetoothGatt.GATT_SUCCESS) { - Log.w(TAG, String.format( - "%s responds MTU change failed, status %s.", device, status)); - // Clean connection if it's failed. - mConnections.remove(gatt); - gatt.disconnect(); - gatt.close(); - return; - } - } - } - - @Override - public void onServicesDiscovered(BluetoothGattWrapper gatt, int status) { - mBluetoothOperationExecutor.notifyCompletion( - new Operation(OperationType.DISCOVER_SERVICES_INTERNAL, gatt), status); - } - - @Override - public void onCharacteristicRead(BluetoothGattWrapper gatt, - BluetoothGattCharacteristic characteristic, int status) { - mBluetoothOperationExecutor.notifyCompletion( - new Operation(OperationType.READ_CHARACTERISTIC, gatt, characteristic), - status, characteristic.getValue()); - } - - @Override - public void onCharacteristicWrite(BluetoothGattWrapper gatt, - BluetoothGattCharacteristic characteristic, int status) { - mBluetoothOperationExecutor.notifyCompletion(new Operation( - OperationType.WRITE_CHARACTERISTIC, gatt, characteristic), status); - } - - @Override - public void onDescriptorRead(BluetoothGattWrapper gatt, BluetoothGattDescriptor descriptor, - int status) { - mBluetoothOperationExecutor.notifyCompletion( - new Operation(OperationType.READ_DESCRIPTOR, gatt, descriptor), status, - descriptor.getValue()); - } - - @Override - public void onDescriptorWrite(BluetoothGattWrapper gatt, BluetoothGattDescriptor descriptor, - int status) { - Log.d(TAG, String.format("onDescriptorWrite %s, %s, %d", - gatt.getDevice(), descriptor.getUuid(), status)); - mBluetoothOperationExecutor.notifyCompletion( - new Operation(OperationType.WRITE_DESCRIPTOR, gatt, descriptor), status); - } - - @Override - public void onReadRemoteRssi(BluetoothGattWrapper gatt, int rssi, int status) { - mBluetoothOperationExecutor.notifyCompletion( - new Operation(OperationType.READ_RSSI, gatt), status, rssi); - } - - @Override - public void onReliableWriteCompleted(BluetoothGattWrapper gatt, int status) { - mBluetoothOperationExecutor.notifyCompletion( - new Operation(OperationType.WRITE_RELIABLE, gatt), status); - } - - @Override - public void onCharacteristicChanged(BluetoothGattWrapper gatt, - BluetoothGattCharacteristic characteristic) { - byte[] value = characteristic.getValue(); - if (value == null) { - // Value is not supposed to be null, but just to be safe... - value = new byte[0]; - } - Log.d(TAG, String.format("Characteristic %s changed, Gatt device: %s", - characteristic.getUuid(), gatt.getDevice())); - try { - getConnectionByGatt(gatt).onCharacteristicChanged(characteristic, value); - } catch (BluetoothException e) { - Log.e(TAG, "Error in onCharacteristicChanged", e); - } - } - } - - private class InternalScanCallback extends ScanCallback { - - @Override - public void onScanFailed(int errorCode) { - String errorMessage; - switch (errorCode) { - case ScanCallback.SCAN_FAILED_ALREADY_STARTED: - errorMessage = "SCAN_FAILED_ALREADY_STARTED"; - break; - case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED: - errorMessage = "SCAN_FAILED_APPLICATION_REGISTRATION_FAILED"; - break; - case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED: - errorMessage = "SCAN_FAILED_FEATURE_UNSUPPORTED"; - break; - case ScanCallback.SCAN_FAILED_INTERNAL_ERROR: - errorMessage = "SCAN_FAILED_INTERNAL_ERROR"; - break; - default: - errorMessage = "Unknown error code - " + errorCode; - } - mBluetoothOperationExecutor.notifyFailure( - new Operation(OperationType.SCAN), - new BluetoothException("Scan failed: " + errorMessage)); - } - - @Override - public void onScanResult(int callbackType, ScanResult result) { - mBluetoothOperationExecutor.notifySuccess( - new Operation(OperationType.SCAN), result.getDevice()); - } - } - - /** - * Options for {@link #connect}. - */ - public static class ConnectionOptions { - - private boolean mAutoConnect; - private long mConnectionTimeoutMillis; - private Optional mConnectionPriority; - private Optional mMtu; - - private ConnectionOptions(boolean autoConnect, long connectionTimeoutMillis, - Optional connectionPriority, - Optional mtu) { - this.mAutoConnect = autoConnect; - this.mConnectionTimeoutMillis = connectionTimeoutMillis; - this.mConnectionPriority = connectionPriority; - this.mMtu = mtu; - } - - boolean autoConnect() { - return mAutoConnect; - } - - long connectionTimeoutMillis() { - return mConnectionTimeoutMillis; - } - - Optional connectionPriority() { - return mConnectionPriority; - } - - Optional mtu() { - return mMtu; - } - - @Override - public String toString() { - return "ConnectionOptions{" - + "autoConnect=" + mAutoConnect + ", " - + "connectionTimeoutMillis=" + mConnectionTimeoutMillis + ", " - + "connectionPriority=" + mConnectionPriority + ", " - + "mtu=" + mMtu - + "}"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o instanceof ConnectionOptions) { - ConnectionOptions that = (ConnectionOptions) o; - return this.mAutoConnect == that.autoConnect() - && this.mConnectionTimeoutMillis == that.connectionTimeoutMillis() - && this.mConnectionPriority.equals(that.connectionPriority()) - && this.mMtu.equals(that.mtu()); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(mAutoConnect, mConnectionTimeoutMillis, mConnectionPriority, mMtu); - } - - /** - * Creates a builder of ConnectionOptions. - */ - public static Builder builder() { - return new ConnectionOptions.Builder() - .setAutoConnect(false) - .setConnectionTimeoutMillis(TimeUnit.SECONDS.toMillis(5)); - } - - /** - * Builder for {@link ConnectionOptions}. - */ - public static class Builder { - - private boolean mAutoConnect; - private long mConnectionTimeoutMillis; - private Optional mConnectionPriority = Optional.empty(); - private Optional mMtu = Optional.empty(); - - /** - * See {@link android.bluetooth.BluetoothDevice#connectGatt}. - */ - public Builder setAutoConnect(boolean autoConnect) { - this.mAutoConnect = autoConnect; - return this; - } - - /** - * See {@link android.bluetooth.BluetoothGatt#requestConnectionPriority(int)}. - */ - public Builder setConnectionPriority(int connectionPriority) { - this.mConnectionPriority = Optional.of(connectionPriority); - return this; - } - - /** - * See {@link android.bluetooth.BluetoothGatt#requestMtu(int)}. - */ - public Builder setMtu(int mtu) { - this.mMtu = Optional.of(mtu); - return this; - } - - /** - * Sets the timeout for the GATT connection. - */ - public Builder setConnectionTimeoutMillis(long connectionTimeoutMillis) { - this.mConnectionTimeoutMillis = connectionTimeoutMillis; - return this; - } - - /** - * Builds ConnectionOptions. - */ - public ConnectionOptions build() { - return new ConnectionOptions(mAutoConnect, mConnectionTimeoutMillis, - mConnectionPriority, mMtu); - } - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/NonnullProvider.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/NonnullProvider.java deleted file mode 100644 index 16abd99052641ade20f1b5a7f555860047d57b5e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/NonnullProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability; - -/** - * Provider that returns non-null instances. - * - * @param Type of provided instance. - */ -public interface NonnullProvider { - /** Get a non-null instance. */ - T get(); -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/Testability.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/Testability.java deleted file mode 100644 index 6cfdd784a490e12edcf66f12629953374f1ebf78..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/Testability.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability; - -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothDevice; - -import javax.annotation.Nullable; - -/** Util class to convert from or to testable classes. */ -public class Testability { - /** Wraps a Bluetooth device. */ - public static BluetoothDevice wrap(android.bluetooth.BluetoothDevice bluetoothDevice) { - return BluetoothDevice.wrap(bluetoothDevice); - } - - /** Wraps a Bluetooth adapter. */ - @Nullable - public static BluetoothAdapter wrap( - @Nullable android.bluetooth.BluetoothAdapter bluetoothAdapter) { - return BluetoothAdapter.wrap(bluetoothAdapter); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/TimeProvider.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/TimeProvider.java deleted file mode 100644 index a4de913d6a342ed7aaeccca9f0198f1dd4bcf621..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/TimeProvider.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability; - -/** Provider of time for testability. */ -public class TimeProvider { - public long getTimeMillis() { - return System.currentTimeMillis(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/VersionProvider.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/VersionProvider.java deleted file mode 100644 index f46ea7ad05e2099552bf943830cd8fe01d201d06..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/VersionProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability; - -import android.os.Build.VERSION; - -/** - * Provider of android sdk version for testability - */ -public class VersionProvider { - public int getSdkInt() { - return VERSION.SDK_INT; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothAdapter.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothAdapter.java deleted file mode 100644 index afa2a1bc031b444aec4070ce41ba6eaa7191cacb..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothAdapter.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth; - -import android.annotation.TargetApi; -import android.os.Build; - -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le.BluetoothLeAdvertiser; -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le.BluetoothLeScanner; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Mockable wrapper of {@link android.bluetooth.BluetoothAdapter}. - */ -public class BluetoothAdapter { - /** See {@link android.bluetooth.BluetoothAdapter#ACTION_REQUEST_ENABLE}. */ - public static final String ACTION_REQUEST_ENABLE = - android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE; - - /** See {@link android.bluetooth.BluetoothAdapter#ACTION_STATE_CHANGED}. */ - public static final String ACTION_STATE_CHANGED = - android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; - - /** See {@link android.bluetooth.BluetoothAdapter#EXTRA_STATE}. */ - public static final String EXTRA_STATE = - android.bluetooth.BluetoothAdapter.EXTRA_STATE; - - /** See {@link android.bluetooth.BluetoothAdapter#STATE_OFF}. */ - public static final int STATE_OFF = - android.bluetooth.BluetoothAdapter.STATE_OFF; - - /** See {@link android.bluetooth.BluetoothAdapter#STATE_ON}. */ - public static final int STATE_ON = - android.bluetooth.BluetoothAdapter.STATE_ON; - - /** See {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_OFF}. */ - public static final int STATE_TURNING_OFF = - android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF; - - /** See {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_ON}. */ - public static final int STATE_TURNING_ON = - android.bluetooth.BluetoothAdapter.STATE_TURNING_ON; - - private final android.bluetooth.BluetoothAdapter mWrappedBluetoothAdapter; - - private BluetoothAdapter(android.bluetooth.BluetoothAdapter bluetoothAdapter) { - mWrappedBluetoothAdapter = bluetoothAdapter; - } - - /** See {@link android.bluetooth.BluetoothAdapter#disable()}. */ - public boolean disable() { - return mWrappedBluetoothAdapter.disable(); - } - - /** See {@link android.bluetooth.BluetoothAdapter#enable()}. */ - public boolean enable() { - return mWrappedBluetoothAdapter.enable(); - } - - /** See {@link android.bluetooth.BluetoothAdapter#getBluetoothLeScanner}. */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @Nullable - public BluetoothLeScanner getBluetoothLeScanner() { - return BluetoothLeScanner.wrap(mWrappedBluetoothAdapter.getBluetoothLeScanner()); - } - - /** See {@link android.bluetooth.BluetoothAdapter#getBluetoothLeAdvertiser()}. */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @Nullable - public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - return BluetoothLeAdvertiser.wrap(mWrappedBluetoothAdapter.getBluetoothLeAdvertiser()); - } - - /** See {@link android.bluetooth.BluetoothAdapter#getBondedDevices()}. */ - @Nullable - public Set getBondedDevices() { - Set bondedDevices = - mWrappedBluetoothAdapter.getBondedDevices(); - if (bondedDevices == null) { - return null; - } - Set result = new HashSet(); - for (android.bluetooth.BluetoothDevice device : bondedDevices) { - if (device == null) { - continue; - } - result.add(BluetoothDevice.wrap(device)); - } - return Collections.unmodifiableSet(result); - } - - /** See {@link android.bluetooth.BluetoothAdapter#getRemoteDevice(byte[])}. */ - public BluetoothDevice getRemoteDevice(byte[] address) { - return BluetoothDevice.wrap(mWrappedBluetoothAdapter.getRemoteDevice(address)); - } - - /** See {@link android.bluetooth.BluetoothAdapter#getRemoteDevice(String)}. */ - public BluetoothDevice getRemoteDevice(String address) { - return BluetoothDevice.wrap(mWrappedBluetoothAdapter.getRemoteDevice(address)); - } - - /** See {@link android.bluetooth.BluetoothAdapter#isEnabled()}. */ - public boolean isEnabled() { - return mWrappedBluetoothAdapter.isEnabled(); - } - - /** See {@link android.bluetooth.BluetoothAdapter#isDiscovering()}. */ - public boolean isDiscovering() { - return mWrappedBluetoothAdapter.isDiscovering(); - } - - /** See {@link android.bluetooth.BluetoothAdapter#startDiscovery()}. */ - public boolean startDiscovery() { - return mWrappedBluetoothAdapter.startDiscovery(); - } - - /** See {@link android.bluetooth.BluetoothAdapter#cancelDiscovery()}. */ - public boolean cancelDiscovery() { - return mWrappedBluetoothAdapter.cancelDiscovery(); - } - - /** See {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter()}. */ - @Nullable - public static BluetoothAdapter getDefaultAdapter() { - android.bluetooth.BluetoothAdapter adapter = - android.bluetooth.BluetoothAdapter.getDefaultAdapter(); - if (adapter == null) { - return null; - } - return new BluetoothAdapter(adapter); - } - - /** Wraps a Bluetooth adapter. */ - @Nullable - public static BluetoothAdapter wrap( - @Nullable android.bluetooth.BluetoothAdapter bluetoothAdapter) { - if (bluetoothAdapter == null) { - return null; - } - return new BluetoothAdapter(bluetoothAdapter); - } - - /** Unwraps a Bluetooth adapter. */ - public android.bluetooth.BluetoothAdapter unwrap() { - return mWrappedBluetoothAdapter; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java deleted file mode 100644 index 5b45f618fc1511c6ad321a95a5065208444ab16d..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth; - -import android.annotation.TargetApi; -import android.bluetooth.BluetoothClass; -import android.bluetooth.BluetoothSocket; -import android.content.Context; -import android.os.ParcelUuid; - -import java.io.IOException; -import java.util.UUID; - -import javax.annotation.Nullable; - -/** - * Mockable wrapper of {@link android.bluetooth.BluetoothDevice}. - */ -@TargetApi(18) -public class BluetoothDevice { - /** See {@link android.bluetooth.BluetoothDevice#BOND_BONDED}. */ - public static final int BOND_BONDED = android.bluetooth.BluetoothDevice.BOND_BONDED; - - /** See {@link android.bluetooth.BluetoothDevice#BOND_BONDING}. */ - public static final int BOND_BONDING = android.bluetooth.BluetoothDevice.BOND_BONDING; - - /** See {@link android.bluetooth.BluetoothDevice#BOND_NONE}. */ - public static final int BOND_NONE = android.bluetooth.BluetoothDevice.BOND_NONE; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_ACL_CONNECTED}. */ - public static final String ACTION_ACL_CONNECTED = - android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECT_REQUESTED}. */ - public static final String ACTION_ACL_DISCONNECT_REQUESTED = - android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECTED}. */ - public static final String ACTION_ACL_DISCONNECTED = - android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_BOND_STATE_CHANGED}. */ - public static final String ACTION_BOND_STATE_CHANGED = - android.bluetooth.BluetoothDevice.ACTION_BOND_STATE_CHANGED; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_CLASS_CHANGED}. */ - public static final String ACTION_CLASS_CHANGED = - android.bluetooth.BluetoothDevice.ACTION_CLASS_CHANGED; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_FOUND}. */ - public static final String ACTION_FOUND = android.bluetooth.BluetoothDevice.ACTION_FOUND; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_NAME_CHANGED}. */ - public static final String ACTION_NAME_CHANGED = - android.bluetooth.BluetoothDevice.ACTION_NAME_CHANGED; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_PAIRING_REQUEST}. */ - // API 19 only - public static final String ACTION_PAIRING_REQUEST = - "android.bluetooth.device.action.PAIRING_REQUEST"; - - /** See {@link android.bluetooth.BluetoothDevice#ACTION_UUID}. */ - public static final String ACTION_UUID = android.bluetooth.BluetoothDevice.ACTION_UUID; - - /** See {@link android.bluetooth.BluetoothDevice#DEVICE_TYPE_CLASSIC}. */ - public static final int DEVICE_TYPE_CLASSIC = - android.bluetooth.BluetoothDevice.DEVICE_TYPE_CLASSIC; - - /** See {@link android.bluetooth.BluetoothDevice#DEVICE_TYPE_DUAL}. */ - public static final int DEVICE_TYPE_DUAL = android.bluetooth.BluetoothDevice.DEVICE_TYPE_DUAL; - - /** See {@link android.bluetooth.BluetoothDevice#DEVICE_TYPE_LE}. */ - public static final int DEVICE_TYPE_LE = android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE; - - /** See {@link android.bluetooth.BluetoothDevice#DEVICE_TYPE_UNKNOWN}. */ - public static final int DEVICE_TYPE_UNKNOWN = - android.bluetooth.BluetoothDevice.DEVICE_TYPE_UNKNOWN; - - /** See {@link android.bluetooth.BluetoothDevice#ERROR}. */ - public static final int ERROR = android.bluetooth.BluetoothDevice.ERROR; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_BOND_STATE}. */ - public static final String EXTRA_BOND_STATE = - android.bluetooth.BluetoothDevice.EXTRA_BOND_STATE; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_CLASS}. */ - public static final String EXTRA_CLASS = android.bluetooth.BluetoothDevice.EXTRA_CLASS; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE}. */ - public static final String EXTRA_DEVICE = android.bluetooth.BluetoothDevice.EXTRA_DEVICE; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_NAME}. */ - public static final String EXTRA_NAME = android.bluetooth.BluetoothDevice.EXTRA_NAME; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_PAIRING_KEY}. */ - // API 19 only - public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_PAIRING_VARIANT}. */ - // API 19 only - public static final String EXTRA_PAIRING_VARIANT = - "android.bluetooth.device.extra.PAIRING_VARIANT"; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_PREVIOUS_BOND_STATE}. */ - public static final String EXTRA_PREVIOUS_BOND_STATE = - android.bluetooth.BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_RSSI}. */ - public static final String EXTRA_RSSI = android.bluetooth.BluetoothDevice.EXTRA_RSSI; - - /** See {@link android.bluetooth.BluetoothDevice#EXTRA_UUID}. */ - public static final String EXTRA_UUID = android.bluetooth.BluetoothDevice.EXTRA_UUID; - - /** See {@link android.bluetooth.BluetoothDevice#PAIRING_VARIANT_PASSKEY_CONFIRMATION}. */ - // API 19 only - public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; - - /** See {@link android.bluetooth.BluetoothDevice#PAIRING_VARIANT_PIN}. */ - // API 19 only - public static final int PAIRING_VARIANT_PIN = 0; - - private final android.bluetooth.BluetoothDevice mWrappedBluetoothDevice; - - private BluetoothDevice(android.bluetooth.BluetoothDevice bluetoothDevice) { - mWrappedBluetoothDevice = bluetoothDevice; - } - - /** - * See {@link android.bluetooth.BluetoothDevice#connectGatt(Context, boolean, - * android.bluetooth.BluetoothGattCallback)}. - */ - @Nullable(/* when bt service is not available */) - public BluetoothGattWrapper connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback) { - android.bluetooth.BluetoothGatt gatt = - mWrappedBluetoothDevice.connectGatt(context, autoConnect, callback.unwrap()); - if (gatt == null) { - return null; - } - return BluetoothGattWrapper.wrap(gatt); - } - - /** - * See {@link android.bluetooth.BluetoothDevice#connectGatt(Context, boolean, - * android.bluetooth.BluetoothGattCallback, int)}. - */ - @TargetApi(23) - @Nullable(/* when bt service is not available */) - public BluetoothGattWrapper connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport) { - android.bluetooth.BluetoothGatt gatt = - mWrappedBluetoothDevice.connectGatt( - context, autoConnect, callback.unwrap(), transport); - if (gatt == null) { - return null; - } - return BluetoothGattWrapper.wrap(gatt); - } - - - /** - * See {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(UUID)}. - */ - public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { - return mWrappedBluetoothDevice.createRfcommSocketToServiceRecord(uuid); - } - - /** - * See - * {@link android.bluetooth.BluetoothDevice#createInsecureRfcommSocketToServiceRecord(UUID)}. - */ - public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { - return mWrappedBluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid); - } - - /** See {@link android.bluetooth.BluetoothDevice#setPin(byte[])}. */ - @TargetApi(19) - public boolean setPairingConfirmation(byte[] pin) { - return mWrappedBluetoothDevice.setPin(pin); - } - - /** See {@link android.bluetooth.BluetoothDevice#setPairingConfirmation(boolean)}. */ - public boolean setPairingConfirmation(boolean confirm) { - return mWrappedBluetoothDevice.setPairingConfirmation(confirm); - } - - /** See {@link android.bluetooth.BluetoothDevice#fetchUuidsWithSdp()}. */ - public boolean fetchUuidsWithSdp() { - return mWrappedBluetoothDevice.fetchUuidsWithSdp(); - } - - /** See {@link android.bluetooth.BluetoothDevice#createBond()}. */ - public boolean createBond() { - return mWrappedBluetoothDevice.createBond(); - } - - /** See {@link android.bluetooth.BluetoothDevice#getUuids()}. */ - @Nullable(/* on error */) - public ParcelUuid[] getUuids() { - return mWrappedBluetoothDevice.getUuids(); - } - - /** See {@link android.bluetooth.BluetoothDevice#getBondState()}. */ - public int getBondState() { - return mWrappedBluetoothDevice.getBondState(); - } - - /** See {@link android.bluetooth.BluetoothDevice#getAddress()}. */ - public String getAddress() { - return mWrappedBluetoothDevice.getAddress(); - } - - /** See {@link android.bluetooth.BluetoothDevice#getBluetoothClass()}. */ - @Nullable(/* on error */) - public BluetoothClass getBluetoothClass() { - return mWrappedBluetoothDevice.getBluetoothClass(); - } - - /** See {@link android.bluetooth.BluetoothDevice#getType()}. */ - public int getType() { - return mWrappedBluetoothDevice.getType(); - } - - /** See {@link android.bluetooth.BluetoothDevice#getName()}. */ - @Nullable(/* on error */) - public String getName() { - return mWrappedBluetoothDevice.getName(); - } - - /** See {@link android.bluetooth.BluetoothDevice#toString()}. */ - @Override - public String toString() { - return mWrappedBluetoothDevice.toString(); - } - - /** See {@link android.bluetooth.BluetoothDevice#hashCode()}. */ - @Override - public int hashCode() { - return mWrappedBluetoothDevice.hashCode(); - } - - /** See {@link android.bluetooth.BluetoothDevice#equals(Object)}. */ - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof BluetoothDevice)) { - return false; - } - return mWrappedBluetoothDevice.equals(((BluetoothDevice) o).unwrap()); - } - - /** Unwraps a Bluetooth device. */ - public android.bluetooth.BluetoothDevice unwrap() { - return mWrappedBluetoothDevice; - } - - /** Wraps a Bluetooth device. */ - public static BluetoothDevice wrap(android.bluetooth.BluetoothDevice bluetoothDevice) { - return new BluetoothDevice(bluetoothDevice); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattCallback.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattCallback.java deleted file mode 100644 index d36cfa251758b2b06d38db01ff3aa95717eebe87..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattCallback.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth; - -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; - -/** - * Wrapper of {@link android.bluetooth.BluetoothGattCallback} that uses mockable objects. - */ -public abstract class BluetoothGattCallback { - - private final android.bluetooth.BluetoothGattCallback mWrappedBluetoothGattCallback = - new InternalBluetoothGattCallback(); - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onConnectionStateChange( - * android.bluetooth.BluetoothGatt, int, int)} - */ - public void onConnectionStateChange(BluetoothGattWrapper gatt, int status, int newState) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onServicesDiscovered( - * android.bluetooth.BluetoothGatt,int)} - */ - public void onServicesDiscovered(BluetoothGattWrapper gatt, int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onCharacteristicRead( - * android.bluetooth.BluetoothGatt, BluetoothGattCharacteristic, int)} - */ - public void onCharacteristicRead(BluetoothGattWrapper gatt, BluetoothGattCharacteristic - characteristic, int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite( - * android.bluetooth.BluetoothGatt, BluetoothGattCharacteristic, int)} - */ - public void onCharacteristicWrite(BluetoothGattWrapper gatt, - BluetoothGattCharacteristic characteristic, int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onDescriptorRead( - * android.bluetooth.BluetoothGatt, BluetoothGattDescriptor, int)} - */ - public void onDescriptorRead( - BluetoothGattWrapper gatt, BluetoothGattDescriptor descriptor, int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onDescriptorWrite( - * android.bluetooth.BluetoothGatt, BluetoothGattDescriptor, int)} - */ - public void onDescriptorWrite(BluetoothGattWrapper gatt, BluetoothGattDescriptor descriptor, - int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onReadRemoteRssi( - * android.bluetooth.BluetoothGatt, int, int)} - */ - public void onReadRemoteRssi(BluetoothGattWrapper gatt, int rssi, int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattCallback#onReliableWriteCompleted( - * android.bluetooth.BluetoothGatt, int)} - */ - public void onReliableWriteCompleted(BluetoothGattWrapper gatt, int status) {} - - /** - * See - * {@link android.bluetooth.BluetoothGattCallback#onMtuChanged(android.bluetooth.BluetoothGatt, - * int, int)} - */ - public void onMtuChanged(BluetoothGattWrapper gatt, int mtu, int status) {} - - /** - * See - * {@link android.bluetooth.BluetoothGattCallback#onCharacteristicChanged( - * android.bluetooth.BluetoothGatt, BluetoothGattCharacteristic)} - */ - public void onCharacteristicChanged(BluetoothGattWrapper gatt, - BluetoothGattCharacteristic characteristic) {} - - /** Unwraps a Bluetooth Gatt callback. */ - public android.bluetooth.BluetoothGattCallback unwrap() { - return mWrappedBluetoothGattCallback; - } - - /** Forward callback to testable instance. */ - private class InternalBluetoothGattCallback extends android.bluetooth.BluetoothGattCallback { - @Override - public void onConnectionStateChange(android.bluetooth.BluetoothGatt gatt, int status, - int newState) { - BluetoothGattCallback.this.onConnectionStateChange(BluetoothGattWrapper.wrap(gatt), - status, newState); - } - - @Override - public void onServicesDiscovered(android.bluetooth.BluetoothGatt gatt, int status) { - BluetoothGattCallback.this.onServicesDiscovered(BluetoothGattWrapper.wrap(gatt), - status); - } - - @Override - public void onCharacteristicRead(android.bluetooth.BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { - BluetoothGattCallback.this.onCharacteristicRead( - BluetoothGattWrapper.wrap(gatt), characteristic, status); - } - - @Override - public void onCharacteristicWrite(android.bluetooth.BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { - BluetoothGattCallback.this.onCharacteristicWrite( - BluetoothGattWrapper.wrap(gatt), characteristic, status); - } - - @Override - public void onDescriptorRead(android.bluetooth.BluetoothGatt gatt, - BluetoothGattDescriptor descriptor, int status) { - BluetoothGattCallback.this.onDescriptorRead( - BluetoothGattWrapper.wrap(gatt), descriptor, status); - } - - @Override - public void onDescriptorWrite(android.bluetooth.BluetoothGatt gatt, - BluetoothGattDescriptor descriptor, int status) { - BluetoothGattCallback.this.onDescriptorWrite( - BluetoothGattWrapper.wrap(gatt), descriptor, status); - } - - @Override - public void onReadRemoteRssi(android.bluetooth.BluetoothGatt gatt, int rssi, int status) { - BluetoothGattCallback.this.onReadRemoteRssi(BluetoothGattWrapper.wrap(gatt), rssi, - status); - } - - @Override - public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt gatt, int status) { - BluetoothGattCallback.this.onReliableWriteCompleted(BluetoothGattWrapper.wrap(gatt), - status); - } - - @Override - public void onMtuChanged(android.bluetooth.BluetoothGatt gatt, int mtu, int status) { - BluetoothGattCallback.this.onMtuChanged(BluetoothGattWrapper.wrap(gatt), mtu, status); - } - - @Override - public void onCharacteristicChanged(android.bluetooth.BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { - BluetoothGattCallback.this.onCharacteristicChanged( - BluetoothGattWrapper.wrap(gatt), characteristic); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java deleted file mode 100644 index 3f6f3617febf0eb80f1da077348cc6486a438133..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth; - -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattService; - -import java.util.UUID; - -import javax.annotation.Nullable; - -/** - * Mockable wrapper of {@link android.bluetooth.BluetoothGattServer}. - */ -public class BluetoothGattServer { - - /** See {@link android.bluetooth.BluetoothGattServer#STATE_CONNECTED}. */ - public static final int STATE_CONNECTED = android.bluetooth.BluetoothGattServer.STATE_CONNECTED; - - /** See {@link android.bluetooth.BluetoothGattServer#STATE_DISCONNECTED}. */ - public static final int STATE_DISCONNECTED = - android.bluetooth.BluetoothGattServer.STATE_DISCONNECTED; - - private android.bluetooth.BluetoothGattServer mWrappedInstance; - - private BluetoothGattServer(android.bluetooth.BluetoothGattServer instance) { - mWrappedInstance = instance; - } - - /** Wraps a Bluetooth Gatt server. */ - @Nullable - public static BluetoothGattServer wrap( - @Nullable android.bluetooth.BluetoothGattServer instance) { - if (instance == null) { - return null; - } - return new BluetoothGattServer(instance); - } - - /** - * See {@link android.bluetooth.BluetoothGattServer#connect( - * android.bluetooth.BluetoothDevice, boolean)} - */ - public boolean connect(BluetoothDevice device, boolean autoConnect) { - return mWrappedInstance.connect(device.unwrap(), autoConnect); - } - - /** See {@link android.bluetooth.BluetoothGattServer#addService(BluetoothGattService)}. */ - public boolean addService(BluetoothGattService service) { - return mWrappedInstance.addService(service); - } - - /** See {@link android.bluetooth.BluetoothGattServer#clearServices()}. */ - public void clearServices() { - mWrappedInstance.clearServices(); - } - - /** See {@link android.bluetooth.BluetoothGattServer#close()}. */ - public void close() { - mWrappedInstance.close(); - } - - /** - * See {@link android.bluetooth.BluetoothGattServer#notifyCharacteristicChanged( - * android.bluetooth.BluetoothDevice, BluetoothGattCharacteristic, boolean)}. - */ - public boolean notifyCharacteristicChanged(BluetoothDevice device, - BluetoothGattCharacteristic characteristic, boolean confirm) { - return mWrappedInstance.notifyCharacteristicChanged( - device.unwrap(), characteristic, confirm); - } - - /** - * See {@link android.bluetooth.BluetoothGattServer#sendResponse( - * android.bluetooth.BluetoothDevice, int, int, int, byte[])}. - */ - public void sendResponse(BluetoothDevice device, int requestId, int status, int offset, - @Nullable byte[] value) { - mWrappedInstance.sendResponse(device.unwrap(), requestId, status, offset, value); - } - - /** - * See {@link android.bluetooth.BluetoothGattServer#cancelConnection( - * android.bluetooth.BluetoothDevice)}. - */ - public void cancelConnection(BluetoothDevice device) { - mWrappedInstance.cancelConnection(device.unwrap()); - } - - /** See {@link android.bluetooth.BluetoothGattServer#getService(UUID uuid)}. */ - public BluetoothGattService getService(UUID uuid) { - return mWrappedInstance.getService(uuid); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerCallback.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerCallback.java deleted file mode 100644 index 875dad562f5986857eae7fd845467654bce73e2a..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerCallback.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth; - -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; - -/** - * Wrapper of {@link android.bluetooth.BluetoothGattServerCallback} that uses mockable objects. - */ -public abstract class BluetoothGattServerCallback { - - private final android.bluetooth.BluetoothGattServerCallback mWrappedInstance = - new InternalBluetoothGattServerCallback(); - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onCharacteristicReadRequest( - * android.bluetooth.BluetoothDevice, int, int, BluetoothGattCharacteristic)} - */ - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattCharacteristic characteristic) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onCharacteristicWriteRequest( - * android.bluetooth.BluetoothDevice, int, BluetoothGattCharacteristic, boolean, boolean, int, - * byte[])} - */ - public void onCharacteristicWriteRequest(BluetoothDevice device, - int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, - boolean responseNeeded, - int offset, - byte[] value) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onConnectionStateChange( - * android.bluetooth.BluetoothDevice, int, int)} - */ - public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onDescriptorReadRequest( - * android.bluetooth.BluetoothDevice, int, int, BluetoothGattDescriptor)} - */ - public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, - BluetoothGattDescriptor descriptor) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onDescriptorWriteRequest( - * android.bluetooth.BluetoothDevice, int, BluetoothGattDescriptor, boolean, boolean, int, - * byte[])} - */ - public void onDescriptorWriteRequest(BluetoothDevice device, - int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, - boolean responseNeeded, - int offset, - byte[] value) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onExecuteWrite( - * android.bluetooth.BluetoothDevice, int, boolean)} - */ - public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onMtuChanged( - * android.bluetooth.BluetoothDevice, int)} - */ - public void onMtuChanged(BluetoothDevice device, int mtu) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onNotificationSent( - * android.bluetooth.BluetoothDevice, int)} - */ - public void onNotificationSent(BluetoothDevice device, int status) {} - - /** - * See {@link android.bluetooth.BluetoothGattServerCallback#onServiceAdded(int, - * BluetoothGattService)} - */ - public void onServiceAdded(int status, BluetoothGattService service) {} - - /** Unwraps a Bluetooth Gatt server callback. */ - public android.bluetooth.BluetoothGattServerCallback unwrap() { - return mWrappedInstance; - } - - /** Forward callback to testable instance. */ - private class InternalBluetoothGattServerCallback extends - android.bluetooth.BluetoothGattServerCallback { - @Override - public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, - int requestId, int offset, BluetoothGattCharacteristic characteristic) { - BluetoothGattServerCallback.this.onCharacteristicReadRequest( - BluetoothDevice.wrap(device), requestId, offset, characteristic); - } - - @Override - public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, - int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, - boolean responseNeeded, - int offset, - byte[] value) { - BluetoothGattServerCallback.this.onCharacteristicWriteRequest( - BluetoothDevice.wrap(device), - requestId, - characteristic, - preparedWrite, - responseNeeded, - offset, - value); - } - - @Override - public void onConnectionStateChange(android.bluetooth.BluetoothDevice device, int status, - int newState) { - BluetoothGattServerCallback.this.onConnectionStateChange( - BluetoothDevice.wrap(device), status, newState); - } - - @Override - public void onDescriptorReadRequest(android.bluetooth.BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { - BluetoothGattServerCallback.this.onDescriptorReadRequest(BluetoothDevice.wrap(device), - requestId, offset, descriptor); - } - - @Override - public void onDescriptorWriteRequest(android.bluetooth.BluetoothDevice device, - int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, - boolean responseNeeded, - int offset, - byte[] value) { - BluetoothGattServerCallback.this.onDescriptorWriteRequest(BluetoothDevice.wrap(device), - requestId, - descriptor, - preparedWrite, - responseNeeded, - offset, - value); - } - - @Override - public void onExecuteWrite(android.bluetooth.BluetoothDevice device, int requestId, - boolean execute) { - BluetoothGattServerCallback.this.onExecuteWrite(BluetoothDevice.wrap(device), requestId, - execute); - } - - @Override - public void onMtuChanged(android.bluetooth.BluetoothDevice device, int mtu) { - BluetoothGattServerCallback.this.onMtuChanged(BluetoothDevice.wrap(device), mtu); - } - - @Override - public void onNotificationSent(android.bluetooth.BluetoothDevice device, int status) { - BluetoothGattServerCallback.this.onNotificationSent( - BluetoothDevice.wrap(device), status); - } - - @Override - public void onServiceAdded(int status, BluetoothGattService service) { - BluetoothGattServerCallback.this.onServiceAdded(status, service); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapper.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapper.java deleted file mode 100644 index 453ee5d694bdb8629bd501d61c5b1b3704caee24..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapper.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth; - -import android.annotation.TargetApi; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; -import android.os.Build; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.UUID; - -import javax.annotation.Nullable; - -/** Mockable wrapper of {@link android.bluetooth.BluetoothGatt}. */ -@TargetApi(Build.VERSION_CODES.TIRAMISU) -public class BluetoothGattWrapper { - private final android.bluetooth.BluetoothGatt mWrappedBluetoothGatt; - - private BluetoothGattWrapper(android.bluetooth.BluetoothGatt bluetoothGatt) { - mWrappedBluetoothGatt = bluetoothGatt; - } - - /** See {@link android.bluetooth.BluetoothGatt#getDevice()}. */ - public BluetoothDevice getDevice() { - return BluetoothDevice.wrap(mWrappedBluetoothGatt.getDevice()); - } - - /** See {@link android.bluetooth.BluetoothGatt#getServices()}. */ - public List getServices() { - return mWrappedBluetoothGatt.getServices(); - } - - /** See {@link android.bluetooth.BluetoothGatt#getService(UUID)}. */ - @Nullable(/* null if service is not found */) - public BluetoothGattService getService(UUID uuid) { - return mWrappedBluetoothGatt.getService(uuid); - } - - /** See {@link android.bluetooth.BluetoothGatt#discoverServices()}. */ - public boolean discoverServices() { - return mWrappedBluetoothGatt.discoverServices(); - } - - /** - * Hidden method. Clears the internal cache and forces a refresh of the services from the remote - * device. - */ - // TODO(b/201300471): remove refresh call using reflection. - public boolean refresh() { - try { - Method refreshMethod = android.bluetooth.BluetoothGatt.class.getMethod("refresh"); - return (Boolean) refreshMethod.invoke(mWrappedBluetoothGatt); - } catch (NoSuchMethodException - | IllegalAccessException - | IllegalArgumentException - | InvocationTargetException e) { - return false; - } - } - - /** - * See {@link android.bluetooth.BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}. - */ - public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { - return mWrappedBluetoothGatt.readCharacteristic(characteristic); - } - - /** - * See {@link android.bluetooth.BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, - * byte[], int)} . - */ - public int writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] value, - int writeType) { - return mWrappedBluetoothGatt.writeCharacteristic(characteristic, value, writeType); - } - - /** See {@link android.bluetooth.BluetoothGatt#readDescriptor(BluetoothGattDescriptor)}. */ - public boolean readDescriptor(BluetoothGattDescriptor descriptor) { - return mWrappedBluetoothGatt.readDescriptor(descriptor); - } - - /** - * See {@link android.bluetooth.BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, - * byte[])}. - */ - public int writeDescriptor(BluetoothGattDescriptor descriptor, byte[] value) { - return mWrappedBluetoothGatt.writeDescriptor(descriptor, value); - } - - /** See {@link android.bluetooth.BluetoothGatt#readRemoteRssi()}. */ - public boolean readRemoteRssi() { - return mWrappedBluetoothGatt.readRemoteRssi(); - } - - /** See {@link android.bluetooth.BluetoothGatt#requestConnectionPriority(int)}. */ - public boolean requestConnectionPriority(int connectionPriority) { - return mWrappedBluetoothGatt.requestConnectionPriority(connectionPriority); - } - - /** See {@link android.bluetooth.BluetoothGatt#requestMtu(int)}. */ - public boolean requestMtu(int mtu) { - return mWrappedBluetoothGatt.requestMtu(mtu); - } - - /** See {@link android.bluetooth.BluetoothGatt#setCharacteristicNotification}. */ - public boolean setCharacteristicNotification( - BluetoothGattCharacteristic characteristic, boolean enable) { - return mWrappedBluetoothGatt.setCharacteristicNotification(characteristic, enable); - } - - /** See {@link android.bluetooth.BluetoothGatt#disconnect()}. */ - public void disconnect() { - mWrappedBluetoothGatt.disconnect(); - } - - /** See {@link android.bluetooth.BluetoothGatt#close()}. */ - public void close() { - mWrappedBluetoothGatt.close(); - } - - /** See {@link android.bluetooth.BluetoothGatt#hashCode()}. */ - @Override - public int hashCode() { - return mWrappedBluetoothGatt.hashCode(); - } - - /** See {@link android.bluetooth.BluetoothGatt#equals(Object)}. */ - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof BluetoothGattWrapper)) { - return false; - } - return mWrappedBluetoothGatt.equals(((BluetoothGattWrapper) o).unwrap()); - } - - /** Unwraps a Bluetooth Gatt instance. */ - public android.bluetooth.BluetoothGatt unwrap() { - return mWrappedBluetoothGatt; - } - - /** Wraps a Bluetooth Gatt instance. */ - public static BluetoothGattWrapper wrap(android.bluetooth.BluetoothGatt gatt) { - return new BluetoothGattWrapper(gatt); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java deleted file mode 100644 index 6fe44324a6c4afaa618a3f29b618595d0afb9ec0..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le; - -import android.annotation.TargetApi; -import android.bluetooth.le.AdvertiseCallback; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertiseSettings; -import android.os.Build; - -import javax.annotation.Nullable; - -/** - * Mockable wrapper of {@link android.bluetooth.le.BluetoothLeAdvertiser}. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class BluetoothLeAdvertiser { - - private final android.bluetooth.le.BluetoothLeAdvertiser mWrappedInstance; - - private BluetoothLeAdvertiser( - android.bluetooth.le.BluetoothLeAdvertiser bluetoothLeAdvertiser) { - mWrappedInstance = bluetoothLeAdvertiser; - } - - /** - * See {@link android.bluetooth.le.BluetoothLeAdvertiser#startAdvertising(AdvertiseSettings, - * AdvertiseData, AdvertiseCallback)}. - */ - public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, - AdvertiseCallback callback) { - mWrappedInstance.startAdvertising(settings, advertiseData, callback); - } - - /** - * See {@link android.bluetooth.le.BluetoothLeAdvertiser#startAdvertising(AdvertiseSettings, - * AdvertiseData, AdvertiseData, AdvertiseCallback)}. - */ - public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, - AdvertiseData scanResponse, AdvertiseCallback callback) { - mWrappedInstance.startAdvertising(settings, advertiseData, scanResponse, callback); - } - - /** - * See {@link android.bluetooth.le.BluetoothLeAdvertiser#stopAdvertising(AdvertiseCallback)}. - */ - public void stopAdvertising(AdvertiseCallback callback) { - mWrappedInstance.stopAdvertising(callback); - } - - /** Wraps a Bluetooth LE advertiser. */ - @Nullable - public static BluetoothLeAdvertiser wrap( - @Nullable android.bluetooth.le.BluetoothLeAdvertiser bluetoothLeAdvertiser) { - if (bluetoothLeAdvertiser == null) { - return null; - } - return new BluetoothLeAdvertiser(bluetoothLeAdvertiser); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java deleted file mode 100644 index 8a13abe5d3e87ee47178da34e17b72d66072f10a..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le; - -import android.annotation.TargetApi; -import android.app.PendingIntent; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanSettings; -import android.os.Build; - -import java.util.List; - -import javax.annotation.Nullable; - -/** - * Mockable wrapper of {@link android.bluetooth.le.BluetoothLeScanner}. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class BluetoothLeScanner { - - private final android.bluetooth.le.BluetoothLeScanner mWrappedBluetoothLeScanner; - - private BluetoothLeScanner(android.bluetooth.le.BluetoothLeScanner bluetoothLeScanner) { - mWrappedBluetoothLeScanner = bluetoothLeScanner; - } - - /** - * See {@link android.bluetooth.le.BluetoothLeScanner#startScan(List, ScanSettings, - * android.bluetooth.le.ScanCallback)}. - */ - public void startScan(List filters, ScanSettings settings, - ScanCallback callback) { - mWrappedBluetoothLeScanner.startScan(filters, settings, callback.unwrap()); - } - - /** - * See {@link android.bluetooth.le.BluetoothLeScanner#startScan(List, ScanSettings, - * PendingIntent)}. - */ - public void startScan( - List filters, ScanSettings settings, PendingIntent callbackIntent) { - mWrappedBluetoothLeScanner.startScan(filters, settings, callbackIntent); - } - - /** - * See {@link - * android.bluetooth.le.BluetoothLeScanner#startScan(android.bluetooth.le.ScanCallback)}. - */ - public void startScan(ScanCallback callback) { - mWrappedBluetoothLeScanner.startScan(callback.unwrap()); - } - - /** - * See - * {@link android.bluetooth.le.BluetoothLeScanner#stopScan(android.bluetooth.le.ScanCallback)}. - */ - public void stopScan(ScanCallback callback) { - mWrappedBluetoothLeScanner.stopScan(callback.unwrap()); - } - - /** See {@link android.bluetooth.le.BluetoothLeScanner#stopScan(PendingIntent)}. */ - public void stopScan(PendingIntent callbackIntent) { - mWrappedBluetoothLeScanner.stopScan(callbackIntent); - } - - /** Wraps a Bluetooth LE scanner. */ - @Nullable - public static BluetoothLeScanner wrap( - @Nullable android.bluetooth.le.BluetoothLeScanner bluetoothLeScanner) { - if (bluetoothLeScanner == null) { - return null; - } - return new BluetoothLeScanner(bluetoothLeScanner); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanCallback.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanCallback.java deleted file mode 100644 index 70926a77191481a665b34b89dc28a8103bffcbdf..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanCallback.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le; - -import android.annotation.TargetApi; -import android.os.Build; - -import java.util.ArrayList; -import java.util.List; - -/** - * Wrapper of {@link android.bluetooth.le.ScanCallback} that uses mockable objects. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public abstract class ScanCallback { - - /** See {@link android.bluetooth.le.ScanCallback#SCAN_FAILED_ALREADY_STARTED} */ - public static final int SCAN_FAILED_ALREADY_STARTED = - android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED; - - /** See {@link android.bluetooth.le.ScanCallback#SCAN_FAILED_APPLICATION_REGISTRATION_FAILED} */ - public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = - android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED; - - /** See {@link android.bluetooth.le.ScanCallback#SCAN_FAILED_FEATURE_UNSUPPORTED} */ - public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = - android.bluetooth.le.ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED; - - /** See {@link android.bluetooth.le.ScanCallback#SCAN_FAILED_INTERNAL_ERROR} */ - public static final int SCAN_FAILED_INTERNAL_ERROR = - android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR; - - private final android.bluetooth.le.ScanCallback mWrappedScanCallback = - new InternalScanCallback(); - - /** - * See {@link android.bluetooth.le.ScanCallback#onScanFailed(int)} - */ - public void onScanFailed(int errorCode) {} - - /** - * See - * {@link android.bluetooth.le.ScanCallback#onScanResult(int, android.bluetooth.le.ScanResult)}. - */ - public void onScanResult(int callbackType, ScanResult result) {} - - /** - * See {@link - * android.bluetooth.le.ScanCallback#onBatchScanResult(List)}. - */ - public void onBatchScanResults(List results) {} - - /** Unwraps scan callback. */ - public android.bluetooth.le.ScanCallback unwrap() { - return mWrappedScanCallback; - } - - /** Forward callback to testable instance. */ - private class InternalScanCallback extends android.bluetooth.le.ScanCallback { - @Override - public void onScanFailed(int errorCode) { - ScanCallback.this.onScanFailed(errorCode); - } - - @Override - public void onScanResult(int callbackType, android.bluetooth.le.ScanResult result) { - ScanCallback.this.onScanResult(callbackType, ScanResult.wrap(result)); - } - - @Override - public void onBatchScanResults(List results) { - List wrappedScanResults = new ArrayList<>(); - for (android.bluetooth.le.ScanResult result : results) { - wrappedScanResults.add(ScanResult.wrap(result)); - } - ScanCallback.this.onBatchScanResults(wrappedScanResults); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanResult.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanResult.java deleted file mode 100644 index 1a6b7b304b6f72a1bbb7720701945ea8fae755d1..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanResult.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.testability.android.bluetooth.le; - -import android.annotation.TargetApi; -import android.bluetooth.le.ScanRecord; -import android.os.Build; - -import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothDevice; - -import javax.annotation.Nullable; - -/** - * Mockable wrapper of {@link android.bluetooth.le.ScanResult}. - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class ScanResult { - - private final android.bluetooth.le.ScanResult mWrappedScanResult; - - private ScanResult(android.bluetooth.le.ScanResult scanResult) { - mWrappedScanResult = scanResult; - } - - /** See {@link android.bluetooth.le.ScanResult#getScanRecord()}. */ - @Nullable - public ScanRecord getScanRecord() { - return mWrappedScanResult.getScanRecord(); - } - - /** See {@link android.bluetooth.le.ScanResult#getRssi()}. */ - public int getRssi() { - return mWrappedScanResult.getRssi(); - } - - /** See {@link android.bluetooth.le.ScanResult#getTimestampNanos()}. */ - public long getTimestampNanos() { - return mWrappedScanResult.getTimestampNanos(); - } - - /** See {@link android.bluetooth.le.ScanResult#getDevice()}. */ - public BluetoothDevice getDevice() { - return BluetoothDevice.wrap(mWrappedScanResult.getDevice()); - } - - /** Creates a wrapper of scan result. */ - public static ScanResult wrap(android.bluetooth.le.ScanResult scanResult) { - return new ScanResult(scanResult); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java deleted file mode 100644 index bb51920f18533fdda8c65e92774cb5c84f26a981..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.util; - -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattService; - -import javax.annotation.Nullable; - -/** - * Utils for Gatt profile. - */ -public class BluetoothGattUtils { - - /** - * Returns a string message for a BluetoothGatt status codes. - */ - public static String getMessageForStatusCode(int statusCode) { - switch (statusCode) { - case BluetoothGatt.GATT_SUCCESS: - return "GATT_SUCCESS"; - case BluetoothGatt.GATT_FAILURE: - return "GATT_FAILURE"; - case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION: - return "GATT_INSUFFICIENT_AUTHENTICATION"; - case BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION: - return "GATT_INSUFFICIENT_AUTHORIZATION"; - case BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION: - return "GATT_INSUFFICIENT_ENCRYPTION"; - case BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH: - return "GATT_INVALID_ATTRIBUTE_LENGTH"; - case BluetoothGatt.GATT_INVALID_OFFSET: - return "GATT_INVALID_OFFSET"; - case BluetoothGatt.GATT_READ_NOT_PERMITTED: - return "GATT_READ_NOT_PERMITTED"; - case BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED: - return "GATT_REQUEST_NOT_SUPPORTED"; - case BluetoothGatt.GATT_WRITE_NOT_PERMITTED: - return "GATT_WRITE_NOT_PERMITTED"; - case BluetoothGatt.GATT_CONNECTION_CONGESTED: - return "GATT_CONNECTION_CONGESTED"; - default: - return "Unknown error code"; - } - } - - /** Creates a user-readable string from a {@link BluetoothGattDescriptor}. */ - public static String toString(@Nullable BluetoothGattDescriptor descriptor) { - if (descriptor == null) { - return "null descriptor"; - } - return String.format("descriptor %s on %s", - descriptor.getUuid(), - toString(descriptor.getCharacteristic())); - } - - /** Creates a user-readable string from a {@link BluetoothGattCharacteristic}. */ - public static String toString(@Nullable BluetoothGattCharacteristic characteristic) { - if (characteristic == null) { - return "null characteristic"; - } - return String.format("characteristic %s on %s", - characteristic.getUuid(), - toString(characteristic.getService())); - } - - /** Creates a user-readable string from a {@link BluetoothGattService}. */ - public static String toString(@Nullable BluetoothGattService service) { - if (service == null) { - return "null service"; - } - return String.format("service %s", service.getUuid()); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/util/BluetoothOperationExecutor.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/util/BluetoothOperationExecutor.java deleted file mode 100644 index fecf48362425a3375e52d67b043d61eb7bb55666..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/bluetooth/util/BluetoothOperationExecutor.java +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.bluetooth.util; - -import android.bluetooth.BluetoothGatt; -import android.util.Log; - -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.BluetoothGattException; -import com.android.server.nearby.common.bluetooth.testability.NonnullProvider; -import com.android.server.nearby.common.bluetooth.testability.TimeProvider; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Joiner; -import com.google.common.base.Objects; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.annotation.Nullable; - -/** - * Scheduler to coordinate parallel bluetooth operations. - */ -public class BluetoothOperationExecutor { - - private static final String TAG = BluetoothOperationExecutor.class.getSimpleName(); - - /** - * Special value to indicate that the result is null (since {@link BlockingQueue} doesn't allow - * null elements). - */ - private static final Object NULL_RESULT = new Object(); - - /** - * Special value to indicate that there should be no timeout on the operation. - */ - private static final long NO_TIMEOUT = -1; - - private final NonnullProvider> mBlockingQueueProvider; - private final TimeProvider mTimeProvider; - @VisibleForTesting - final Map, Queue> mOperationResultQueues = new HashMap<>(); - private final Semaphore mOperationSemaphore; - - /** - * New instance that limits concurrent operations to maxConcurrentOperations. - */ - public BluetoothOperationExecutor(int maxConcurrentOperations) { - this( - new Semaphore(maxConcurrentOperations, true), - new TimeProvider(), - new NonnullProvider>() { - @Override - public BlockingQueue get() { - return new LinkedBlockingDeque(); - } - }); - } - - /** - * Constructor for unit tests. - */ - @VisibleForTesting - BluetoothOperationExecutor(Semaphore operationSemaphore, - TimeProvider timeProvider, - NonnullProvider> blockingQueueProvider) { - mOperationSemaphore = operationSemaphore; - mTimeProvider = timeProvider; - mBlockingQueueProvider = blockingQueueProvider; - } - - /** - * Executes the operation and waits for its completion. - */ - @Nullable - public T execute(Operation operation) throws BluetoothException { - return getResult(schedule(operation)); - } - - /** - * Executes the operation and waits for its completion and returns a non-null result. - */ - public T executeNonnull(Operation operation) throws BluetoothException { - T result = getResult(schedule(operation)); - if (result == null) { - throw new BluetoothException( - String.format(Locale.US, "Operation %s returned a null result.", operation)); - } - return result; - } - - /** - * Executes the operation and waits for its completion with a timeout. - */ - @Nullable - public T execute(Operation bluetoothOperation, long timeoutMillis) - throws BluetoothException, BluetoothOperationTimeoutException { - return getResult(schedule(bluetoothOperation), timeoutMillis); - } - - /** - * Executes the operation and waits for its completion with a timeout and returns a non-null - * result. - */ - public T executeNonnull(Operation bluetoothOperation, long timeoutMillis) - throws BluetoothException { - T result = getResult(schedule(bluetoothOperation), timeoutMillis); - if (result == null) { - throw new BluetoothException( - String.format(Locale.US, "Operation %s returned a null result.", - bluetoothOperation)); - } - return result; - } - - /** - * Schedules an operation and returns a {@link Future} that waits on operation completion and - * gets its result. - */ - public Future schedule(Operation bluetoothOperation) { - BlockingQueue resultQueue = mBlockingQueueProvider.get(); - mOperationResultQueues.put(bluetoothOperation, resultQueue); - - boolean semaphoreAcquired = mOperationSemaphore.tryAcquire(); - Log.d(TAG, String.format(Locale.US, - "Scheduling operation %s; %d permits available; Semaphore acquired: %b", - bluetoothOperation, - mOperationSemaphore.availablePermits(), - semaphoreAcquired)); - - if (semaphoreAcquired) { - bluetoothOperation.execute(this); - } - return new BluetoothOperationFuture(resultQueue, bluetoothOperation, semaphoreAcquired); - } - - /** - * Notifies that this operation has completed with success. - */ - public void notifySuccess(Operation bluetoothOperation) { - postResult(bluetoothOperation, null); - } - - /** - * Notifies that this operation has completed with success and with a result. - */ - public void notifySuccess(Operation bluetoothOperation, T result) { - postResult(bluetoothOperation, result); - } - - /** - * Notifies that this operation has completed with the given BluetoothGatt status code (which - * may indicate success or failure). - */ - public void notifyCompletion(Operation bluetoothOperation, int status) { - notifyCompletion(bluetoothOperation, status, null); - } - - /** - * Notifies that this operation has completed with the given BluetoothGatt status code (which - * may indicate success or failure) and with a result. - */ - public void notifyCompletion(Operation bluetoothOperation, int status, - @Nullable T result) { - if (status != BluetoothGatt.GATT_SUCCESS) { - notifyFailure(bluetoothOperation, new BluetoothGattException( - String.format(Locale.US, - "Operation %s failed: %d - %s.", bluetoothOperation, status, - BluetoothGattUtils.getMessageForStatusCode(status)), - status)); - return; - } - postResult(bluetoothOperation, result); - } - - /** - * Notifies that this operation has completed with failure. - */ - public void notifyFailure(Operation bluetoothOperation, BluetoothException exception) { - postResult(bluetoothOperation, exception); - } - - private void postResult(Operation bluetoothOperation, @Nullable Object result) { - Queue resultQueue = mOperationResultQueues.get(bluetoothOperation); - if (resultQueue == null) { - Log.e(TAG, String.format(Locale.US, - "Receive completion for unexpected operation: %s.", bluetoothOperation)); - return; - } - resultQueue.add(result == null ? NULL_RESULT : result); - mOperationResultQueues.remove(bluetoothOperation); - mOperationSemaphore.release(); - Log.d(TAG, String.format(Locale.US, - "Released semaphore for operation %s. There are %d permits left", - bluetoothOperation, mOperationSemaphore.availablePermits())); - } - - /** - * Waits for all future on the list to complete, ignoring the results. - */ - public void waitFor(List> futures) throws BluetoothException { - for (Future future : futures) { - if (future == null) { - continue; - } - getResult(future); - } - } - - /** - * Waits with timeout for all future on the list to complete, ignoring the results. - */ - public void waitFor(List> futures, long timeoutMillis) - throws BluetoothException { - long startTime = mTimeProvider.getTimeMillis(); - for (Future future : futures) { - if (future == null) { - continue; - } - getResult(future, - timeoutMillis - (mTimeProvider.getTimeMillis() - startTime)); - } - } - - /** - * Waits for a future to complete and returns the result. - */ - @Nullable - public static T getResult(Future future) throws BluetoothException { - return getResultInternal(future, NO_TIMEOUT); - } - - /** - * Waits for a future to complete and returns the result with timeout. - */ - @Nullable - public static T getResult(Future future, long timeoutMillis) throws BluetoothException { - return getResultInternal(future, Math.max(0, timeoutMillis)); - } - - @Nullable - private static T getResultInternal(Future future, long timeoutMillis) - throws BluetoothException { - try { - if (timeoutMillis == NO_TIMEOUT) { - return future.get(); - } else { - return future.get(timeoutMillis, TimeUnit.MILLISECONDS); - } - } catch (InterruptedException e) { - try { - boolean cancelSuccess = future.cancel(true); - if (!cancelSuccess && future.isDone()) { - // Operation has succeeded before we send cancel to it. - return getResultInternal(future, NO_TIMEOUT); - } - } finally { - // Re-interrupt the thread last since we're recursively calling getResultInternal. - // We know the future is done, so there's no need to be interrupted while we call. - Thread.currentThread().interrupt(); - } - throw new BluetoothException("Wait interrupted"); - } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof BluetoothException) { - throw (BluetoothException) cause; - } - throw new RuntimeException(e); - } catch (TimeoutException e) { - boolean cancelSuccess = future.cancel(true); - if (!cancelSuccess && future.isDone()) { - // Operation has succeeded before we send cancel to it. - return getResultInternal(future, NO_TIMEOUT); - } - throw new BluetoothOperationTimeoutException( - String.format(Locale.US, "Wait timed out after %s ms.", timeoutMillis), e); - } - } - - /** - * Asynchronous bluetooth operation to schedule. - * - *

    An instance that doesn't implemented run() can be used to notify operation result. - * - * @param Type of provided instance. - */ - public static class Operation { - - private Object[] mElements; - - public Operation(Object... elements) { - mElements = elements; - } - - /** - * Executes operation using executor. - */ - public void execute(BluetoothOperationExecutor executor) { - try { - run(); - } catch (BluetoothException e) { - executor.postResult(this, e); - } - } - - /** - * Run function. Not supported. - */ - @SuppressWarnings("unused") - public void run() throws BluetoothException { - throw new RuntimeException("Not implemented"); - } - - /** - * Try to cancel operation when a timeout occurs. - */ - public void cancel() { - } - - @Override - public boolean equals(@Nullable Object o) { - if (o == null) { - return false; - } - if (!Operation.class.isInstance(o)) { - return false; - } - Operation other = (Operation) o; - return Arrays.equals(mElements, other.mElements); - } - - @Override - public int hashCode() { - return Objects.hashCode(mElements); - } - - @Override - public String toString() { - return Joiner.on('-').join(mElements); - } - } - - /** - * Synchronous bluetooth operation to schedule. - * - * @param Type of provided instance. - */ - public static class SynchronousOperation extends Operation { - - public SynchronousOperation(Object... elements) { - super(elements); - } - - @Override - public void execute(BluetoothOperationExecutor executor) { - try { - Object result = call(); - if (result == null) { - result = NULL_RESULT; - } - executor.postResult(this, result); - } catch (BluetoothException e) { - executor.postResult(this, e); - } - } - - /** - * Call function. Not supported. - */ - @SuppressWarnings("unused") - @Nullable - public T call() throws BluetoothException { - throw new RuntimeException("Not implemented"); - } - } - - /** - * {@link Future} to wait / get result of an operation. - * - *

  • Waits for operation to complete - *
  • Handles timeouts if needed - *
  • Queues identical Bluetooth operations - *
  • Unwraps Exceptions and null values - */ - private class BluetoothOperationFuture implements Future { - - private final Object mLock = new Object(); - - /** - * Queue that will be used to store the result. It should normally contains one element - * maximum, but using a queue avoid some race conditions. - */ - private final BlockingQueue mResultQueue; - private final Operation mBluetoothOperation; - private final boolean mOperationExecuted; - private boolean mIsCancelled = false; - private boolean mIsDone = false; - - BluetoothOperationFuture(BlockingQueue resultQueue, - Operation bluetoothOperation, boolean operationExecuted) { - mResultQueue = resultQueue; - mBluetoothOperation = bluetoothOperation; - mOperationExecuted = operationExecuted; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - synchronized (mLock) { - if (mIsDone) { - return false; - } - if (mIsCancelled) { - return true; - } - mBluetoothOperation.cancel(); - mIsCancelled = true; - notifyFailure(mBluetoothOperation, new BluetoothException("Operation cancelled.")); - return true; - } - } - - @Override - public boolean isCancelled() { - synchronized (mLock) { - return mIsCancelled; - } - } - - @Override - public boolean isDone() { - synchronized (mLock) { - return mIsDone; - } - } - - @Override - @Nullable - public T get() throws InterruptedException, ExecutionException { - try { - return getInternal(NO_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - throw new RuntimeException(e); // This is not supposed to be thrown - } - } - - @Override - @Nullable - public T get(long timeoutMillis, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return getInternal(Math.max(0, timeoutMillis), unit); - } - - @SuppressWarnings("unchecked") - @Nullable - private T getInternal(long timeoutMillis, TimeUnit unit) - throws ExecutionException, InterruptedException, TimeoutException { - // Prevent parallel executions of this method. - long startTime = mTimeProvider.getTimeMillis(); - synchronized (this) { - synchronized (mLock) { - if (mIsDone) { - throw new ExecutionException( - new BluetoothException("get() called twice...")); - } - } - if (!mOperationExecuted) { - if (timeoutMillis == NO_TIMEOUT) { - mOperationSemaphore.acquire(); - } else { - if (!mOperationSemaphore.tryAcquire(timeoutMillis - - (mTimeProvider.getTimeMillis() - startTime), unit)) { - throw new TimeoutException(String.format(Locale.US, - "A timeout occurred when processing %s after %s %s.", - mBluetoothOperation, timeoutMillis, unit)); - } - } - mBluetoothOperation.execute(BluetoothOperationExecutor.this); - } - Object result; - - if (timeoutMillis == NO_TIMEOUT) { - result = mResultQueue.take(); - } else { - result = mResultQueue.poll( - timeoutMillis - (mTimeProvider.getTimeMillis() - startTime), unit); - } - - if (result == null) { - throw new TimeoutException(String.format(Locale.US, - "A timeout occurred when processing %s after %s ms.", - mBluetoothOperation, timeoutMillis)); - } - synchronized (mLock) { - mIsDone = true; - } - if (result instanceof BluetoothException) { - throw new ExecutionException((BluetoothException) result); - } - if (result == NULL_RESULT) { - result = null; - } - return (T) result; - } - } - } - - /** - * Exception thrown when an operation execution times out. Since state of the system is unknown - * afterward (operation may still complete or not), it is recommended to disconnect and - * reconnect. - */ - public static class BluetoothOperationTimeoutException extends BluetoothException { - - public BluetoothOperationTimeoutException(String message) { - super(message); - } - - public BluetoothOperationTimeoutException(String message, Throwable cause) { - super(message, cause); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/eventloop/Annotations.java b/nearby/service/java/com/android/server/nearby/common/eventloop/Annotations.java deleted file mode 100644 index 44c9422203268364e478865ecaa7bfeca21f8003..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/eventloop/Annotations.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.eventloop; - -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.CLASS; - -import androidx.annotation.AnyThread; -import androidx.annotation.BinderThread; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * A collection of threading annotations relating to EventLoop. These should be used in conjunction - * with {@link UiThread}, {@link BinderThread}, {@link WorkerThread}, and {@link AnyThread}. - */ -public class Annotations { - - /** - * Denotes that the annotated method or constructor should only be called on the EventLoop - * thread. - */ - @Retention(CLASS) - @Target({METHOD, CONSTRUCTOR, TYPE}) - public @interface EventThread { - } - - /** Denotes that the annotated method or constructor should only be called on a Network - * thread. */ - @Retention(CLASS) - @Target({METHOD, CONSTRUCTOR, TYPE}) - public @interface NetworkThread { - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/eventloop/EventLoop.java b/nearby/service/java/com/android/server/nearby/common/eventloop/EventLoop.java deleted file mode 100644 index c89366f3bb3ddf91ff2d5d2e65fd0bc01bd2b23e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/eventloop/EventLoop.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.eventloop; - -import android.annotation.Nullable; -import android.os.Handler; -import android.os.Looper; - -/** - * Handles executing runnables on a background thread. - * - *

    Nearby services follow an event loop model where events can be queued and delivered in the - * future. All code that is run in this EventLoop is guaranteed to be run on this thread. The main - * advantage of this model is that all modules don't have to deal with synchronization and race - * conditions, while making it easy to handle the several asynchronous tasks that are expected to be - * needed for this type of provider (such as starting a WiFi scan and waiting for the result, - * starting BLE scans, doing a server request and waiting for the response etc.). - * - *

    Code that needs to wait for an event should not spawn a new thread nor sleep. It should simply - * deliver a new message to the event queue when the reply of the event happens. - */ -// TODO(b/177675274): Resolve nullness suppression. -@SuppressWarnings("nullness") -public class EventLoop { - - private final Interface mImpl; - - private EventLoop(Interface impl) { - this.mImpl = impl; - } - - protected EventLoop(String name) { - this(new HandlerEventLoopImpl(name)); - } - - /** Creates an EventLoop. */ - public static EventLoop newInstance(String name) { - return new EventLoop(name); - } - - /** Creates an EventLoop. */ - public static EventLoop newInstance(String name, Looper looper) { - return new EventLoop(new HandlerEventLoopImpl(name, looper)); - } - - /** Marks the EventLoop as destroyed. Any further messages received will be ignored. */ - public void destroy() { - mImpl.destroy(); - } - - /** - * Posts a runnable to this event loop, blocking until the runnable has been executed. This - * should - * be used rarely. It could be useful, for example, for a runnable that initializes the system - * and - * must block the posting of all other runnables. - * - * @param runnable a Runnable to post. This method will not return until the run() method of the - * given runnable has executed on the background thread. - */ - public void postAndWait(final NamedRunnable runnable) throws InterruptedException { - mImpl.postAndWait(runnable); - } - - /** - * Posts a runnable to this to the front of the event loop, blocking until the runnable has been - * executed. This should be used rarely, as it can starve the event loop. - * - * @param runnable a Runnable to post. This method will not return until the run() method of the - * given runnable has executed on the background thread. - */ - public void postToFrontAndWait(final NamedRunnable runnable) throws InterruptedException { - mImpl.postToFrontAndWait(runnable); - } - - /** Checks if there are any pending posts of the Runnable in the queue. */ - public boolean isPosted(NamedRunnable runnable) { - return mImpl.isPosted(runnable); - } - - /** - * Run code on the event loop thread. - * - * @param runnable the runnable to execute. - */ - public void postRunnable(NamedRunnable runnable) { - mImpl.postRunnable(runnable); - } - - /** - * Run code to be executed when there is no runnable scheduled. - * - * @param runnable last runnable to execute. - */ - public void postEmptyQueueRunnable(final NamedRunnable runnable) { - mImpl.postEmptyQueueRunnable(runnable); - } - - /** - * Run code on the event loop thread after delayedMillis. - * - * @param runnable the runnable to execute. - * @param delayedMillis the number of milliseconds before executing the runnable. - */ - public void postRunnableDelayed(NamedRunnable runnable, long delayedMillis) { - mImpl.postRunnableDelayed(runnable, delayedMillis); - } - - /** - * Removes and cancels the specified {@code runnable} if it had not posted/started yet. Calling - * with null does nothing. - */ - public void removeRunnable(@Nullable NamedRunnable runnable) { - mImpl.removeRunnable(runnable); - } - - /** Asserts that the current operation is being executed in the Event Loop's thread. */ - public void checkThread() { - mImpl.checkThread(); - } - - public Handler getHandler() { - return mImpl.getHandler(); - } - - interface Interface { - void destroy(); - - void postAndWait(NamedRunnable runnable) throws InterruptedException; - - void postToFrontAndWait(NamedRunnable runnable) throws InterruptedException; - - boolean isPosted(NamedRunnable runnable); - - void postRunnable(NamedRunnable runnable); - - void postEmptyQueueRunnable(NamedRunnable runnable); - - void postRunnableDelayed(NamedRunnable runnable, long delayedMillis); - - void removeRunnable(NamedRunnable runnable); - - void checkThread(); - - Handler getHandler(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/eventloop/HandlerEventLoopImpl.java b/nearby/service/java/com/android/server/nearby/common/eventloop/HandlerEventLoopImpl.java deleted file mode 100644 index 018dcdb1ba0573cee286ab15f27f274bd4fcb9b7..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/eventloop/HandlerEventLoopImpl.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.eventloop; - -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.MessageQueue; -import android.os.Process; -import android.os.SystemClock; -import android.util.Log; - -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - - -/** - * Handles executing runnables on a background thread. - * - *

    Nearby services follow an event loop model where events can be queued and delivered in the - * future. All code that is run in this package is guaranteed to be run on this thread. The main - * advantage of this model is that all modules don't have to deal with synchronization and race - * conditions, while making it easy to handle the several asynchronous tasks that are expected to be - * needed for this type of provider (such as starting a WiFi scan and waiting for the result, - * starting BLE scans, doing a server request and waiting for the response etc.). - * - *

    Code that needs to wait for an event should not spawn a new thread nor sleep. It should simply - * deliver a new message to the event queue when the reply of the event happens. - * - *

    - */ -// TODO(b/203471261) use executor instead of handler -// TODO(b/177675274): Resolve nullness suppression. -@SuppressWarnings("nullness") -final class HandlerEventLoopImpl implements EventLoop.Interface { - /** The {@link Message#what} code for all messages that we post to the EventLoop. */ - private static final int WHAT = 0; - - private static final long ELAPSED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(5); - private static final long RUNNABLE_DELAY_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(2); - private static final String TAG = HandlerEventLoopImpl.class.getSimpleName(); - private final MyHandler mHandler; - - private volatile boolean mIsDestroyed = false; - - /** Constructs an EventLoop. */ - HandlerEventLoopImpl(String name) { - this(name, createHandlerThread(name)); - } - - HandlerEventLoopImpl(String name, Looper looper) { - - mHandler = new MyHandler(looper); - Log.d(TAG, - "Created EventLoop for thread '" + looper.getThread().getName() - + "(id: " + looper.getThread().getId() + ")'"); - } - - private static Looper createHandlerThread(String name) { - HandlerThread handlerThread = new HandlerThread(name, Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - - return handlerThread.getLooper(); - } - - /** - * Wrapper to satisfy Android Lint. {@link Looper#getQueue()} is public and available since ICS, - * but was marked @hide until Marshmallow. Tested that this code doesn't crash pre-Marshmallow. - * /aosp-ics/frameworks/base/core/java/android/os/Looper.java?l=218 - */ - @SuppressLint("NewApi") - private static MessageQueue getQueue(Handler handler) { - return handler.getLooper().getQueue(); - } - - /** Marks the EventLoop as destroyed. Any further messages received will be ignored. */ - @Override - public void destroy() { - Looper looper = mHandler.getLooper(); - Log.d(TAG, - "Destroying EventLoop for thread " + looper.getThread().getName() - + " (id: " + looper.getThread().getId() + ")"); - looper.quit(); - mIsDestroyed = true; - } - - /** - * Posts a runnable to this event loop, blocking until the runnable has been executed. This - * should - * be used rarely. It could be useful, for example, for a runnable that initializes the system - * and - * must block the posting of all other runnables. - * - * @param runnable a Runnable to post. This method will not return until the run() method of the - * given runnable has executed on the background thread. - */ - @Override - public void postAndWait(final NamedRunnable runnable) throws InterruptedException { - internalPostAndWait(runnable, false); - } - - @Override - public void postToFrontAndWait(final NamedRunnable runnable) throws InterruptedException { - internalPostAndWait(runnable, true); - } - - /** Checks if there are any pending posts of the Runnable in the queue. */ - @Override - public boolean isPosted(NamedRunnable runnable) { - return mHandler.hasMessages(WHAT, runnable); - } - - /** - * Run code on the event loop thread. - * - * @param runnable the runnable to execute. - */ - @Override - public void postRunnable(NamedRunnable runnable) { - Log.d(TAG, "Posting " + runnable); - mHandler.post(runnable, 0L, false); - } - - /** - * Run code to be executed when there is no runnable scheduled. - * - * @param runnable last runnable to execute. - */ - @Override - public void postEmptyQueueRunnable(final NamedRunnable runnable) { - mHandler.post( - () -> - getQueue(mHandler) - .addIdleHandler( - () -> { - if (mHandler.hasMessages(WHAT)) { - return true; - } else { - // Only stop if start has not been called since - // this was queued - runnable.run(); - return false; - } - })); - } - - /** - * Run code on the event loop thread after delayedMillis. - * - * @param runnable the runnable to execute. - * @param delayedMillis the number of milliseconds before executing the runnable. - */ - @Override - public void postRunnableDelayed(NamedRunnable runnable, long delayedMillis) { - Log.d(TAG, "Posting " + runnable + " [delay " + delayedMillis + "]"); - mHandler.post(runnable, delayedMillis, false); - } - - /** - * Removes and cancels the specified {@code runnable} if it had not posted/started yet. Calling - * with null does nothing. - */ - @Override - public void removeRunnable(@Nullable NamedRunnable runnable) { - if (runnable != null) { - // Removes any pending sent messages where what=WHAT and obj=runnable. We can't use - // removeCallbacks(runnable) because we're not posting the runnable directly, we're - // sending a Message with the runnable as its obj. - mHandler.removeMessages(WHAT, runnable); - } - } - - /** Asserts that the current operation is being executed in the Event Loop's thread. */ - @Override - public void checkThread() { - - Thread currentThread = Looper.myLooper().getThread(); - Thread expectedThread = mHandler.getLooper().getThread(); - if (currentThread.getId() != expectedThread.getId()) { - throw new IllegalStateException( - String.format( - "This method must run in the EventLoop thread '%s (id: %s)'. " - + "Was called from thread '%s (id: %s)'.", - expectedThread.getName(), - expectedThread.getId(), - currentThread.getName(), - currentThread.getId())); - } - - } - - @Override - public Handler getHandler() { - return mHandler; - } - - private void internalPostAndWait(final NamedRunnable runnable, boolean postToFront) - throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - NamedRunnable delegate = - new NamedRunnable(runnable.name) { - @Override - public void run() { - try { - runnable.run(); - } finally { - latch.countDown(); - } - } - }; - - Log.d(TAG, "Posting " + delegate + " and wait"); - if (!mHandler.post(delegate, 0L, postToFront)) { - // Do not wait if delegate is not posted. - Log.d(TAG, delegate + " not posted"); - latch.countDown(); - } - latch.await(); - } - - /** Handler that executes code on a private event loop thread. */ - private class MyHandler extends Handler { - - MyHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - NamedRunnable runnable = (NamedRunnable) msg.obj; - - if (mIsDestroyed) { - Log.w(TAG, "Runnable " + runnable - + " attempted to run after the EventLoop was destroyed. Ignoring"); - return; - } - Log.i(TAG, "Executing " + runnable); - - // Did this runnable start much later than we expected it to? If so, then log. - long expectedStartTime = (long) msg.arg1 << 32 | (msg.arg2 & 0xFFFFFFFFL); - logIfExceedsThreshold( - RUNNABLE_DELAY_THRESHOLD_MS, expectedStartTime, runnable, "was delayed for"); - - long startTimeMillis = SystemClock.elapsedRealtime(); - try { - runnable.run(); - } catch (Exception t) { - Log.e(TAG, runnable + "crashed."); - throw t; - } finally { - logIfExceedsThreshold(ELAPSED_THRESHOLD_MS, startTimeMillis, runnable, "ran for"); - } - } - - private boolean post(NamedRunnable runnable, long delayedMillis, boolean postToFront) { - if (mIsDestroyed) { - Log.w(TAG, runnable + " not posted since EventLoop is destroyed"); - return false; - } - long expectedStartTime = SystemClock.elapsedRealtime() + delayedMillis; - int arg1 = (int) (expectedStartTime >> 32); - int arg2 = (int) expectedStartTime; - Message message = obtainMessage(WHAT, arg1, arg2, runnable /* obj */); - boolean sent = - postToFront - ? sendMessageAtFrontOfQueue(message) - : sendMessageDelayed(message, delayedMillis); - if (!sent) { - Log.w(TAG, runnable + "not posted since looper is exiting"); - } - return sent; - } - - private void logIfExceedsThreshold( - long thresholdMillis, long startTimeMillis, NamedRunnable runnable, - String message) { - long elapsedMillis = SystemClock.elapsedRealtime() - startTimeMillis; - if (elapsedMillis > thresholdMillis) { - String elapsedFormatted = - new SimpleDateFormat("mm:ss.SSS", Locale.US).format(elapsedMillis); - Log.w(TAG, runnable + " " + message + " " + elapsedFormatted); - } - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/eventloop/NamedRunnable.java b/nearby/service/java/com/android/server/nearby/common/eventloop/NamedRunnable.java deleted file mode 100644 index 578e3f6298629fe9b7489376c1d1e78db6ca43e3..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/eventloop/NamedRunnable.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.common.eventloop; - -/** A Runnable with a name, for logging purposes. */ -public abstract class NamedRunnable implements Runnable { - public final String name; - - public NamedRunnable(String name) { - this.name = name; - } - - @Override - public String toString() { - return "Runnable[" + name + "]"; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/fastpair/IconUtils.java b/nearby/service/java/com/android/server/nearby/common/fastpair/IconUtils.java deleted file mode 100644 index 35a1a9fa8398fba57ce3a24d9571818b6cb56bbd..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/fastpair/IconUtils.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.fastpair; - -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; - -import androidx.annotation.VisibleForTesting; -import androidx.core.graphics.ColorUtils; - -/** Utility methods for icon size verification. */ -public class IconUtils { - private static final int MIN_ICON_SIZE = 16; - private static final int DESIRED_ICON_SIZE = 32; - private static final double NOTIFICATION_BACKGROUND_PADDING_PERCENTAGE = 0.125; - private static final double NOTIFICATION_BACKGROUND_ALPHA = 0.7; - - /** - * Verify that the icon is non null and falls in the small bucket. Just because an icon isn't - * small doesn't guarantee it is large or exists. - */ - @VisibleForTesting - static boolean isIconSizedSmall(@Nullable Bitmap bitmap) { - if (bitmap == null) { - return false; - } - int min = MIN_ICON_SIZE; - int desired = DESIRED_ICON_SIZE; - return bitmap.getWidth() >= min - && bitmap.getWidth() < desired - && bitmap.getHeight() >= min - && bitmap.getHeight() < desired; - } - - /** - * Verify that the icon is non null and falls in the regular / default size bucket. Doesn't - * guarantee if not regular then it is small. - */ - @VisibleForTesting - static boolean isIconSizedRegular(@Nullable Bitmap bitmap) { - if (bitmap == null) { - return false; - } - return bitmap.getWidth() >= DESIRED_ICON_SIZE - && bitmap.getHeight() >= DESIRED_ICON_SIZE; - } - - // All icons that are sized correctly (larger than the min icon size) are resize on the server - // to the desired icon size so that they appear correct in notifications. - - /** - * All icons that are sized correctly (larger than the min icon size) are resize on the server - * to the desired icon size so that they appear correct in notifications. - */ - public static boolean isIconSizeCorrect(@Nullable Bitmap bitmap) { - if (bitmap == null) { - return false; - } - return isIconSizedSmall(bitmap) || isIconSizedRegular(bitmap); - } - - /** Adds a circular, white background to the bitmap. */ - @Nullable - public static Bitmap addWhiteCircleBackground(Context context, @Nullable Bitmap bitmap) { - if (bitmap == null) { - return null; - } - - if (bitmap.getWidth() != bitmap.getHeight()) { - return bitmap; - } - - int padding = (int) (bitmap.getWidth() * NOTIFICATION_BACKGROUND_PADDING_PERCENTAGE); - Bitmap bitmapWithBackground = - Bitmap.createBitmap( - bitmap.getWidth() + (2 * padding), - bitmap.getHeight() + (2 * padding), - bitmap.getConfig()); - Canvas canvas = new Canvas(bitmapWithBackground); - Paint paint = new Paint(); - paint.setColor( - ColorUtils.setAlphaComponent( - Color.WHITE, (int) (255 * NOTIFICATION_BACKGROUND_ALPHA))); - paint.setStyle(Paint.Style.FILL); - paint.setAntiAlias(true); - canvas.drawCircle( - bitmapWithBackground.getWidth() / 2f, - bitmapWithBackground.getHeight() / 2f, - bitmapWithBackground.getWidth() / 2f, - paint); - canvas.drawBitmap(bitmap, padding, padding, null); - return bitmapWithBackground; - } -} - diff --git a/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java b/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java deleted file mode 100644 index 67d87e337802050a380ebe4a8e1e53f986ffa95e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.fastpair.service; - -/** Handles intents to {@link com.android.server.nearby.fastpair.FastPairManager}. */ -public class UserActionHandlerBase { - public static final String PREFIX = "com.android.server.nearby.fastpair."; - public static final String ACTION_PREFIX = "com.android.server.nearby:"; - - public static final String EXTRA_ITEM_ID = PREFIX + "EXTRA_ITEM_ID"; - public static final String EXTRA_COMPANION_APP = ACTION_PREFIX + "EXTRA_COMPANION_APP"; - public static final String EXTRA_MAC_ADDRESS = PREFIX + "EXTRA_MAC_ADDRESS"; - -} - diff --git a/nearby/service/java/com/android/server/nearby/common/locator/Locator.java b/nearby/service/java/com/android/server/nearby/common/locator/Locator.java deleted file mode 100644 index f8b43a6ae2a0cf67e0ba354a0480041e9b7d526c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/locator/Locator.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.locator; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.ContextWrapper; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -/** Collection of bindings that map service types to their respective implementation(s). */ -public class Locator { - private static final Object UNBOUND = new Object(); - private final Context mContext; - @Nullable - private Locator mParent; - private final String mTag; // For debugging - private final Map, Object> mBindings = new HashMap<>(); - private final ArrayList mModules = new ArrayList<>(); - - /** Thrown upon attempt to bind an interface twice. */ - public static class DuplicateBindingException extends RuntimeException { - DuplicateBindingException(String msg) { - super(msg); - } - } - - /** Constructor with a null parent. */ - public Locator(Context context) { - this(context, null); - } - - /** - * Constructor. Supply a valid context and the Locator's parent. - * - *

    To find a suitable parent you may want to use findLocator. - */ - public Locator(Context context, @Nullable Locator parent) { - this.mContext = context; - this.mParent = parent; - this.mTag = context.getClass().getName(); - } - - /** Attaches the parent to the locator. */ - public void attachParent(Locator parent) { - this.mParent = parent; - } - - /** Associates the specified type with the supplied instance. */ - public Locator bind(Class type, T instance) { - bindKeyValue(type, instance); - return this; - } - - /** For tests only. Disassociates the specified type from any instance. */ - @VisibleForTesting - public Locator overrideBindingForTest(Class type, T instance) { - mBindings.remove(type); - return bind(type, instance); - } - - /** For tests only. Force Locator to return null when try to get an instance. */ - @VisibleForTesting - public Locator removeBindingForTest(Class type) { - Locator locator = this; - do { - locator.mBindings.put(type, UNBOUND); - locator = locator.mParent; - } while (locator != null); - return this; - } - - /** Binds a module. */ - public synchronized Locator bind(Module module) { - mModules.add(module); - return this; - } - - /** - * Searches the chain of locators for a binding for the given type. - * - * @throws IllegalStateException if no binding is found. - */ - public T get(Class type) { - T instance = getOptional(type); - if (instance != null) { - return instance; - } - - String errorMessage = getUnboundErrorMessage(type); - throw new IllegalStateException(errorMessage); - } - - private String getUnboundErrorMessage(Class type) { - StringBuilder sb = new StringBuilder(); - sb.append("Unbound type: ").append(type.getName()).append("\n").append( - "Searched locators:\n"); - Locator locator = this; - while (true) { - sb.append(locator.mTag); - locator = locator.mParent; - if (locator == null) { - break; - } - sb.append(" ->\n"); - } - return sb.toString(); - } - - /** - * Searches the chain of locators for a binding for the given type. Returns null if no locator - * was - * found. - */ - @Nullable - public T getOptional(Class type) { - Locator locator = this; - do { - T instance = locator.getInstance(type); - if (instance != null) { - return instance; - } - locator = locator.mParent; - } while (locator != null); - return null; - } - - private synchronized void bindKeyValue(Class key, T value) { - Object boundInstance = mBindings.get(key); - if (boundInstance != null) { - if (boundInstance == UNBOUND) { - Log.w(mTag, "Bind call too late - someone already tried to get: " + key); - } else { - throw new DuplicateBindingException("Duplicate binding: " + key); - } - } - mBindings.put(key, value); - } - - // Suppress warning of cast from Object -> T - @SuppressWarnings("unchecked") - @Nullable - private synchronized T getInstance(Class type) { - if (mContext == null) { - throw new IllegalStateException("Locator not initialized yet."); - } - - T instance = (T) mBindings.get(type); - if (instance != null) { - return instance != UNBOUND ? instance : null; - } - - // Ask modules to supply a binding - int moduleCount = mModules.size(); - for (int i = 0; i < moduleCount; i++) { - mModules.get(i).configure(mContext, type, this); - } - - instance = (T) mBindings.get(type); - if (instance == null) { - mBindings.put(type, UNBOUND); - } - return instance; - } - - /** - * Iterates over all bound objects and gives the modules a chance to clean up the objects they - * have created. - */ - public synchronized void destroy() { - for (Class type : mBindings.keySet()) { - Object instance = mBindings.get(type); - if (instance == UNBOUND) { - continue; - } - - for (Module module : mModules) { - module.destroy(mContext, type, instance); - } - } - mBindings.clear(); - } - - /** Returns true if there are no bindings. */ - public boolean isEmpty() { - return mBindings.isEmpty(); - } - - /** Returns the parent locator or null if no parent. */ - @Nullable - public Locator getParent() { - return mParent; - } - - /** - * Finds the first locator, then searches the chain of locators for a binding for the given - * type. - * - * @throws IllegalStateException if no binding is found. - */ - public static T get(Context context, Class type) { - Locator locator = findLocator(context); - if (locator == null) { - throw new IllegalStateException("No locator found in context " + context); - } - return locator.get(type); - } - - /** - * Find the first locator from the context wrapper. - */ - public static T getFromContextWrapper(LocatorContextWrapper wrapper, Class type) { - Locator locator = wrapper.getLocator(); - if (locator == null) { - throw new IllegalStateException("No locator found in context wrapper"); - } - return locator.get(type); - } - - /** - * Finds the first locator, then searches the chain of locators for a binding for the given - * type. - * Returns null if no binding was found. - */ - @Nullable - public static T getOptional(Context context, Class type) { - Locator locator = findLocator(context); - if (locator == null) { - return null; - } - return locator.getOptional(type); - } - - /** Finds the first locator in the context hierarchy. */ - @Nullable - public static Locator findLocator(Context context) { - Context applicationContext = context.getApplicationContext(); - boolean applicationContextVisited = false; - - Context searchContext = context; - do { - Locator locator = tryGetLocator(searchContext); - if (locator != null) { - return locator; - } - - applicationContextVisited |= (searchContext == applicationContext); - - if (searchContext instanceof ContextWrapper) { - searchContext = ((ContextWrapper) context).getBaseContext(); - - if (searchContext == null) { - throw new IllegalStateException( - "Invalid ContextWrapper -- If this is a Robolectric test, " - + "have you called ActivityController.create()?"); - } - } else if (!applicationContextVisited) { - searchContext = applicationContext; - } else { - searchContext = null; - } - } while (searchContext != null); - - return null; - } - - @Nullable - private static Locator tryGetLocator(Object object) { - if (object instanceof LocatorContext) { - Locator locator = ((LocatorContext) object).getLocator(); - if (locator == null) { - throw new IllegalStateException( - "LocatorContext must not return null Locator: " + object); - } - return locator; - } - return null; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/locator/LocatorContext.java b/nearby/service/java/com/android/server/nearby/common/locator/LocatorContext.java deleted file mode 100644 index 06eef8aacb37a54d4422cdd23c77adc1e5618b0d..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/locator/LocatorContext.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.locator; - -/** - * An object that has a {@link Locator}. The locator can be used to resolve service types to their - * respective implementation(s). - */ -public interface LocatorContext { - /** Returns the locator. May not return null. */ - Locator getLocator(); -} diff --git a/nearby/service/java/com/android/server/nearby/common/locator/LocatorContextWrapper.java b/nearby/service/java/com/android/server/nearby/common/locator/LocatorContextWrapper.java deleted file mode 100644 index 03df33f843b4dcbfba83368838ddd1d4ec237945..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/locator/LocatorContextWrapper.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.locator; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.ContextWrapper; - -/** - * Wraps a Context and associates it with a Locator, optionally linking it with a parent locator. - */ -public class LocatorContextWrapper extends ContextWrapper implements LocatorContext { - private final Locator mLocator; - private final Context mContext; - /** Constructs a context wrapper with a Locator linked to the passed locator. */ - public LocatorContextWrapper(Context context, @Nullable Locator parentLocator) { - super(context); - mContext = context; - // Assigning under initialization object, but it's safe, since locator is used lazily. - this.mLocator = new Locator(this, parentLocator); - } - - /** - * Constructs a context wrapper. - * - *

    Uses the Locator associated with the passed context as the parent. - */ - public LocatorContextWrapper(Context context) { - this(context, Locator.findLocator(context)); - } - - /** - * Get the context of the context wrapper. - */ - public Context getContext() { - return mContext; - } - - @Override - public Locator getLocator() { - return mLocator; - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/locator/Module.java b/nearby/service/java/com/android/server/nearby/common/locator/Module.java deleted file mode 100644 index 0131c44c07ba19773cf6088b235e0471ef464c8c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/locator/Module.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.locator; - -import android.content.Context; - -/** Configures late bindings of service types to their concrete implementations. */ -public abstract class Module { - /** - * Configures the binding between the {@code type} and its implementation by calling methods on - * the {@code locator}, for example: - * - *

    {@code
    -     * void configure(Context context, Class type, Locator locator) {
    -     *   if (type == MyService.class) {
    -     *     locator.bind(MyService.class, new MyImplementation(context));
    -     *   }
    -     * }
    -     * }
    - * - *

    If the module does not recognize the specified type, the method does not have to do - * anything. - */ - public abstract void configure(Context context, Class type, Locator locator); - - /** - * Notifies you that a binding of class {@code type} is no longer needed and can now release - * everything it was holding on to, such as a database connection. - * - *

    {@code
    -     * void destroy(Context context, Class type, Object instance) {
    -     *   if (type == MyService.class) {
    -     *     ((MyService) instance).destroy();
    -     *   }
    -     * }
    -     * }
    - * - *

    If the module does not recognize the specified type, the method does not have to do - * anything. - */ - public void destroy(Context context, Class type, Object instance) {} -} - diff --git a/nearby/service/java/com/android/server/nearby/common/servicemonitor/CurrentUserServiceProvider.java b/nearby/service/java/com/android/server/nearby/common/servicemonitor/CurrentUserServiceProvider.java deleted file mode 100644 index 80248e8bfca9288e7af4a66721c02f9b61966b48..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/servicemonitor/CurrentUserServiceProvider.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.servicemonitor; - -import static android.content.pm.PackageManager.GET_META_DATA; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; -import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; - -import android.app.ActivityManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ResolveInfo; -import android.os.UserHandle; - -import com.android.internal.util.Preconditions; -import com.android.server.nearby.common.servicemonitor.ServiceMonitor.ServiceChangedListener; -import com.android.server.nearby.common.servicemonitor.ServiceMonitor.ServiceProvider; - -import java.util.Comparator; -import java.util.List; - -/** - * This is mostly borrowed from frameworks CurrentUserServiceSupplier. - * Provides services based on the current active user and version as defined in the service - * manifest. This implementation uses {@link android.content.pm.PackageManager#MATCH_SYSTEM_ONLY} to - * ensure only system (ie, privileged) services are matched. It also handles services that are not - * direct boot aware, and will automatically pick the best service as the user's direct boot state - * changes. - */ -public final class CurrentUserServiceProvider extends BroadcastReceiver implements - ServiceProvider { - - private static final String TAG = "CurrentUserServiceProvider"; - - private static final String EXTRA_SERVICE_VERSION = "serviceVersion"; - - // This is equal to the hidden Intent.ACTION_USER_SWITCHED. - private static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; - // This is equal to the hidden Intent.EXTRA_USER_HANDLE. - private static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; - // This is equal to the hidden UserHandle.USER_NULL. - private static final int USER_NULL = -10000; - - private static final Comparator sBoundServiceInfoComparator = (o1, o2) -> { - if (o1 == o2) { - return 0; - } else if (o1 == null) { - return -1; - } else if (o2 == null) { - return 1; - } - - // ServiceInfos with higher version numbers always win. - return Integer.compare(o1.getVersion(), o2.getVersion()); - }; - - /** Bound service information with version information. */ - public static class BoundServiceInfo extends ServiceMonitor.BoundServiceInfo { - - private static int parseUid(ResolveInfo resolveInfo) { - return resolveInfo.serviceInfo.applicationInfo.uid; - } - - private static int parseVersion(ResolveInfo resolveInfo) { - int version = Integer.MIN_VALUE; - if (resolveInfo.serviceInfo.metaData != null) { - version = resolveInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, version); - } - return version; - } - - private final int mVersion; - - protected BoundServiceInfo(String action, ResolveInfo resolveInfo) { - this( - action, - parseUid(resolveInfo), - new ComponentName( - resolveInfo.serviceInfo.packageName, - resolveInfo.serviceInfo.name), - parseVersion(resolveInfo)); - } - - protected BoundServiceInfo(String action, int uid, ComponentName componentName, - int version) { - super(action, uid, componentName); - mVersion = version; - } - - public int getVersion() { - return mVersion; - } - - @Override - public String toString() { - return super.toString() + "@" + mVersion; - } - } - - /** - * Creates an instance with the specific service details. - * - * @param context the context the provider is to use - * @param action the action the service must declare in its intent-filter - */ - public static CurrentUserServiceProvider create(Context context, String action) { - return new CurrentUserServiceProvider(context, action); - } - - private final Context mContext; - private final Intent mIntent; - private volatile ServiceChangedListener mListener; - - private CurrentUserServiceProvider(Context context, String action) { - mContext = context; - mIntent = new Intent(action); - } - - @Override - public boolean hasMatchingService() { - int intentQueryFlags = - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY; - List resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser( - mIntent, intentQueryFlags, UserHandle.SYSTEM); - return !resolveInfos.isEmpty(); - } - - @Override - public void register(ServiceChangedListener listener) { - Preconditions.checkState(mListener == null); - - mListener = listener; - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ACTION_USER_SWITCHED); - intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); - mContext.registerReceiverForAllUsers(this, intentFilter, null, - ForegroundThread.getHandler()); - } - - @Override - public void unregister() { - Preconditions.checkArgument(mListener != null); - - mListener = null; - mContext.unregisterReceiver(this); - } - - @Override - public BoundServiceInfo getServiceInfo() { - BoundServiceInfo bestServiceInfo = null; - - // only allow services in the correct direct boot state to match - int intentQueryFlags = MATCH_DIRECT_BOOT_AUTO | GET_META_DATA | MATCH_SYSTEM_ONLY; - List resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser( - mIntent, intentQueryFlags, UserHandle.of(ActivityManager.getCurrentUser())); - for (ResolveInfo resolveInfo : resolveInfos) { - BoundServiceInfo serviceInfo = - new BoundServiceInfo(mIntent.getAction(), resolveInfo); - - if (sBoundServiceInfoComparator.compare(serviceInfo, bestServiceInfo) > 0) { - bestServiceInfo = serviceInfo; - } - } - - return bestServiceInfo; - } - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action == null) { - return; - } - int userId = intent.getIntExtra(EXTRA_USER_HANDLE, USER_NULL); - if (userId == USER_NULL) { - return; - } - ServiceChangedListener listener = mListener; - if (listener == null) { - return; - } - - switch (action) { - case ACTION_USER_SWITCHED: - listener.onServiceChanged(); - break; - case Intent.ACTION_USER_UNLOCKED: - // user unlocked implies direct boot mode may have changed - if (userId == ActivityManager.getCurrentUser()) { - listener.onServiceChanged(); - } - break; - default: - break; - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/servicemonitor/ForegroundThread.java b/nearby/service/java/com/android/server/nearby/common/servicemonitor/ForegroundThread.java deleted file mode 100644 index 2c363f89d5d30133b00e6c4ebc3fd6cca524ad4c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/servicemonitor/ForegroundThread.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.servicemonitor; - -import android.os.Handler; -import android.os.HandlerThread; - -import com.android.modules.utils.HandlerExecutor; - -import java.util.concurrent.Executor; - -/** - * Thread for asynchronous event processing. This thread is configured as - * {@link android.os.Process#THREAD_PRIORITY_FOREGROUND}, which means more CPU - * resources will be dedicated to it, and it will be treated like "a user - * interface that the user is interacting with." - *

    - * This thread is best suited for tasks that the user is actively waiting for, - * or for tasks that the user expects to be executed immediately. - * - */ -public final class ForegroundThread extends HandlerThread { - private static ForegroundThread sInstance; - private static Handler sHandler; - private static HandlerExecutor sHandlerExecutor; - - private ForegroundThread() { - super("nearbyfg", android.os.Process.THREAD_PRIORITY_FOREGROUND); - } - - private static void ensureThreadLocked() { - if (sInstance == null) { - sInstance = new ForegroundThread(); - sInstance.start(); - sHandler = new Handler(sInstance.getLooper()); - sHandlerExecutor = new HandlerExecutor(sHandler); - } - } - - /** Get ForegroundThread singleton instance. */ - public static ForegroundThread get() { - synchronized (ForegroundThread.class) { - ensureThreadLocked(); - return sInstance; - } - } - - /** Get ForegroundThread singleton handler. */ - public static Handler getHandler() { - synchronized (ForegroundThread.class) { - ensureThreadLocked(); - return sHandler; - } - } - - /** Get ForegroundThread singleton executor. */ - public static Executor getExecutor() { - synchronized (ForegroundThread.class) { - ensureThreadLocked(); - return sHandlerExecutor; - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/servicemonitor/PackageWatcher.java b/nearby/service/java/com/android/server/nearby/common/servicemonitor/PackageWatcher.java deleted file mode 100644 index 7d1db5781f43d67148f8348cc01941ab2d5d5118..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/servicemonitor/PackageWatcher.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.servicemonitor; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; - -import com.android.modules.utils.BackgroundThread; - -import java.util.Objects; - -/** - * This is mostly from frameworks PackageMonitor. - * Helper class for watching somePackagesChanged. - */ -public abstract class PackageWatcher extends BroadcastReceiver { - static final String TAG = "PackageWatcher"; - static final IntentFilter sPackageFilt = new IntentFilter(); - static final IntentFilter sNonDataFilt = new IntentFilter(); - static final IntentFilter sExternalFilt = new IntentFilter(); - - static { - sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); - sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); - sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); - sPackageFilt.addDataScheme("package"); - sNonDataFilt.addAction(Intent.ACTION_PACKAGES_SUSPENDED); - sNonDataFilt.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); - sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - } - - Context mRegisteredContext; - Handler mRegisteredHandler; - boolean mSomePackagesChanged; - - public PackageWatcher() { - } - - void register(Context context, Looper thread, boolean externalStorage) { - register(context, externalStorage, - (thread == null) ? BackgroundThread.getHandler() : new Handler(thread)); - } - - void register(Context context, boolean externalStorage, Handler handler) { - if (mRegisteredContext != null) { - throw new IllegalStateException("Already registered"); - } - mRegisteredContext = context; - mRegisteredHandler = Objects.requireNonNull(handler); - context.registerReceiverForAllUsers(this, sPackageFilt, null, mRegisteredHandler); - context.registerReceiverForAllUsers(this, sNonDataFilt, null, mRegisteredHandler); - if (externalStorage) { - context.registerReceiverForAllUsers(this, sExternalFilt, null, mRegisteredHandler); - } - } - - void unregister() { - if (mRegisteredContext == null) { - throw new IllegalStateException("Not registered"); - } - mRegisteredContext.unregisterReceiver(this); - mRegisteredContext = null; - } - - // Called when some package has been changed. - abstract void onSomePackagesChanged(); - - String getPackageName(Intent intent) { - Uri uri = intent.getData(); - String pkg = uri != null ? uri.getSchemeSpecificPart() : null; - return pkg; - } - - @Override - public void onReceive(Context context, Intent intent) { - mSomePackagesChanged = false; - - String action = intent.getAction(); - if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - // We consider something to have changed regardless of whether - // this is just an update, because the update is now finished - // and the contents of the package may have changed. - mSomePackagesChanged = true; - } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - String pkg = getPackageName(intent); - if (pkg != null) { - if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - mSomePackagesChanged = true; - } - } - } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { - String pkg = getPackageName(intent); - if (pkg != null) { - mSomePackagesChanged = true; - } - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { - mSomePackagesChanged = true; - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { - mSomePackagesChanged = true; - } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) { - mSomePackagesChanged = true; - } else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) { - mSomePackagesChanged = true; - } - - if (mSomePackagesChanged) { - onSomePackagesChanged(); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/common/servicemonitor/ServiceMonitor.java b/nearby/service/java/com/android/server/nearby/common/servicemonitor/ServiceMonitor.java deleted file mode 100644 index a86af85512afdfc84680e8fa69cb17c19ef1475c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/servicemonitor/ServiceMonitor.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.servicemonitor; - -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; - -import java.io.PrintWriter; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * This is exported from frameworks ServiceWatcher. - * A ServiceMonitor is responsible for continuously maintaining an active binding to a service - * selected by it's {@link ServiceProvider}. The {@link ServiceProvider} may change the service it - * selects over time, and the currently bound service may crash, restart, have a user change, have - * changes made to its package, and so on and so forth. The ServiceMonitor is responsible for - * maintaining the binding across all these changes. - * - *

    Clients may invoke {@link BinderOperation}s on the ServiceMonitor, and it will make a best - * effort to run these on the currently bound service, but individual operations may fail (if there - * is no service currently bound for instance). In order to help clients maintain the correct state, - * clients may supply a {@link ServiceListener}, which is informed when the ServiceMonitor connects - * and disconnects from a service. This allows clients to bring a bound service back into a known - * state on connection, and then run binder operations from there. In order to help clients - * accomplish this, ServiceMonitor guarantees that {@link BinderOperation}s and the - * {@link ServiceListener} will always be run on the same thread, so that strong ordering guarantees - * can be established between them. - * - * There is never any guarantee of whether a ServiceMonitor is currently connected to a service, and - * whether any particular {@link BinderOperation} will succeed. Clients must ensure they do not rely - * on this, and instead use {@link ServiceListener} notifications as necessary to recover from - * failures. - */ -public interface ServiceMonitor { - - /** - * Operation to run on a binder interface. All operations will be run on the thread used by the - * ServiceMonitor this is run with. - */ - interface BinderOperation { - /** Invoked to run the operation. Run on the ServiceMonitor thread. */ - void run(IBinder binder) throws RemoteException; - - /** - * Invoked if {@link #run(IBinder)} could not be invoked because there was no current - * binding, or if {@link #run(IBinder)} threw an exception ({@link RemoteException} or - * {@link RuntimeException}). This callback is only intended for resource deallocation and - * cleanup in response to a single binder operation, it should not be used to propagate - * errors further. Run on the ServiceMonitor thread. - */ - default void onError() {} - } - - /** - * Listener for bind and unbind events. All operations will be run on the thread used by the - * ServiceMonitor this is run with. - * - * @param type of bound service - */ - interface ServiceListener { - /** Invoked when a service is bound. Run on the ServiceMonitor thread. */ - void onBind(IBinder binder, TBoundServiceInfo service) throws RemoteException; - - /** Invoked when a service is unbound. Run on the ServiceMonitor thread. */ - void onUnbind(); - } - - /** - * A listener for when a {@link ServiceProvider} decides that the current service has changed. - */ - interface ServiceChangedListener { - /** - * Should be invoked when the current service may have changed. - */ - void onServiceChanged(); - } - - /** - * This provider encapsulates the logic of deciding what service a {@link ServiceMonitor} should - * be bound to at any given moment. - * - * @param type of bound service - */ - interface ServiceProvider { - /** - * Should return true if there exists at least one service capable of meeting the criteria - * of this provider. This does not imply that {@link #getServiceInfo()} will always return a - * non-null result, as any service may be disqualified for various reasons at any point in - * time. May be invoked at any time from any thread and thus should generally not have any - * dependency on the other methods in this interface. - */ - boolean hasMatchingService(); - - /** - * Invoked when the provider should start monitoring for any changes that could result in a - * different service selection, and should invoke - * {@link ServiceChangedListener#onServiceChanged()} in that case. {@link #getServiceInfo()} - * may be invoked after this method is called. - */ - void register(ServiceChangedListener listener); - - /** - * Invoked when the provider should stop monitoring for any changes that could result in a - * different service selection, should no longer invoke - * {@link ServiceChangedListener#onServiceChanged()}. {@link #getServiceInfo()} will not be - * invoked after this method is called. - */ - void unregister(); - - /** - * Must be implemented to return the current service selected by this provider. May return - * null if no service currently meets the criteria. Only invoked while registered. - */ - @Nullable TBoundServiceInfo getServiceInfo(); - } - - /** - * Information on the service selected as the best option for binding. - */ - class BoundServiceInfo { - - protected final @Nullable String mAction; - protected final int mUid; - protected final ComponentName mComponentName; - - protected BoundServiceInfo(String action, int uid, ComponentName componentName) { - mAction = action; - mUid = uid; - mComponentName = Objects.requireNonNull(componentName); - } - - /** Returns the action associated with this bound service. */ - public @Nullable String getAction() { - return mAction; - } - - /** Returns the component of this bound service. */ - public ComponentName getComponentName() { - return mComponentName; - } - - @Override - public final boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof BoundServiceInfo)) { - return false; - } - - BoundServiceInfo that = (BoundServiceInfo) o; - return mUid == that.mUid - && Objects.equals(mAction, that.mAction) - && mComponentName.equals(that.mComponentName); - } - - @Override - public final int hashCode() { - return Objects.hash(mAction, mUid, mComponentName); - } - - @Override - public String toString() { - if (mComponentName == null) { - return "none"; - } else { - return mUid + "/" + mComponentName.flattenToShortString(); - } - } - } - - /** - * Creates a new ServiceMonitor instance. - */ - static ServiceMonitor create( - Context context, - String tag, - ServiceProvider serviceProvider, - @Nullable ServiceListener serviceListener) { - return create(context, ForegroundThread.getHandler(), ForegroundThread.getExecutor(), tag, - serviceProvider, serviceListener); - } - - /** - * Creates a new ServiceMonitor instance that runs on the given handler. - */ - static ServiceMonitor create( - Context context, - Handler handler, - Executor executor, - String tag, - ServiceProvider serviceProvider, - @Nullable ServiceListener serviceListener) { - return new ServiceMonitorImpl<>(context, handler, executor, tag, serviceProvider, - serviceListener); - } - - /** - * Returns true if there is at least one service that the ServiceMonitor could hypothetically - * bind to, as selected by the {@link ServiceProvider}. - */ - boolean checkServiceResolves(); - - /** - * Registers the ServiceMonitor, so that it will begin maintaining an active binding to the - * service selected by {@link ServiceProvider}, until {@link #unregister()} is called. - */ - void register(); - - /** - * Unregisters the ServiceMonitor, so that it will release any active bindings. If the - * ServiceMonitor is currently bound, this will result in one final - * {@link ServiceListener#onUnbind()} invocation, which may happen after this method completes - * (but which is guaranteed to occur before any further - * {@link ServiceListener#onBind(IBinder, BoundServiceInfo)} invocation in response to a later - * call to {@link #register()}). - */ - void unregister(); - - /** - * Runs the given binder operation on the currently bound service (if available). The operation - * will always fail if the ServiceMonitor is not currently registered. - */ - void runOnBinder(BinderOperation operation); - - /** - * Dumps ServiceMonitor information. - */ - void dump(PrintWriter pw); -} diff --git a/nearby/service/java/com/android/server/nearby/common/servicemonitor/ServiceMonitorImpl.java b/nearby/service/java/com/android/server/nearby/common/servicemonitor/ServiceMonitorImpl.java deleted file mode 100644 index d0d6c3b7a7cd153f828b75a0cfeebf4d07b5594a..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/common/servicemonitor/ServiceMonitorImpl.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.common.servicemonitor; - -import static android.content.Context.BIND_AUTO_CREATE; -import static android.content.Context.BIND_NOT_FOREGROUND; - -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; -import com.android.server.nearby.common.servicemonitor.ServiceMonitor.BoundServiceInfo; -import com.android.server.nearby.common.servicemonitor.ServiceMonitor.ServiceChangedListener; - -import java.io.PrintWriter; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * Implementation of ServiceMonitor. Keeping the implementation separate from the interface allows - * us to store the generic relationship between the service provider and the service listener, while - * hiding the generics from clients, simplifying the API. - */ -class ServiceMonitorImpl implements ServiceMonitor, - ServiceChangedListener { - - private static final String TAG = "ServiceMonitor"; - private static final boolean D = Log.isLoggable(TAG, Log.DEBUG); - private static final long RETRY_DELAY_MS = 15 * 1000; - - // This is the same as Context.BIND_NOT_VISIBLE. - private static final int BIND_NOT_VISIBLE = 0x40000000; - - final Context mContext; - final Handler mHandler; - final Executor mExecutor; - final String mTag; - final ServiceProvider mServiceProvider; - final @Nullable ServiceListener mServiceListener; - - private final PackageWatcher mPackageWatcher = new PackageWatcher() { - @Override - public void onSomePackagesChanged() { - onServiceChanged(false); - } - }; - - @GuardedBy("this") - private boolean mRegistered = false; - @GuardedBy("this") - private MyServiceConnection mServiceConnection = new MyServiceConnection(null); - - ServiceMonitorImpl(Context context, Handler handler, Executor executor, String tag, - ServiceProvider serviceProvider, - ServiceListener serviceListener) { - mContext = context; - mExecutor = executor; - mHandler = handler; - mTag = tag; - mServiceProvider = serviceProvider; - mServiceListener = serviceListener; - } - - @Override - public boolean checkServiceResolves() { - return mServiceProvider.hasMatchingService(); - } - - @Override - public synchronized void register() { - Preconditions.checkState(!mRegistered); - - mRegistered = true; - mPackageWatcher.register(mContext, /*externalStorage=*/ true, mHandler); - mServiceProvider.register(this); - - onServiceChanged(false); - } - - @Override - public synchronized void unregister() { - Preconditions.checkState(mRegistered); - - mServiceProvider.unregister(); - mPackageWatcher.unregister(); - mRegistered = false; - - onServiceChanged(false); - } - - @Override - public synchronized void onServiceChanged() { - onServiceChanged(false); - } - - @Override - public synchronized void runOnBinder(BinderOperation operation) { - MyServiceConnection serviceConnection = mServiceConnection; - mHandler.post(() -> serviceConnection.runOnBinder(operation)); - } - - synchronized void onServiceChanged(boolean forceRebind) { - TBoundServiceInfo newBoundServiceInfo; - if (mRegistered) { - newBoundServiceInfo = mServiceProvider.getServiceInfo(); - } else { - newBoundServiceInfo = null; - } - - if (forceRebind || !Objects.equals(mServiceConnection.getBoundServiceInfo(), - newBoundServiceInfo)) { - Log.i(TAG, "[" + mTag + "] chose new implementation " + newBoundServiceInfo); - MyServiceConnection oldServiceConnection = mServiceConnection; - MyServiceConnection newServiceConnection = new MyServiceConnection(newBoundServiceInfo); - mServiceConnection = newServiceConnection; - mHandler.post(() -> { - oldServiceConnection.unbind(); - newServiceConnection.bind(); - }); - } - } - - @Override - public String toString() { - MyServiceConnection serviceConnection; - synchronized (this) { - serviceConnection = mServiceConnection; - } - - return serviceConnection.getBoundServiceInfo().toString(); - } - - @Override - public void dump(PrintWriter pw) { - MyServiceConnection serviceConnection; - synchronized (this) { - serviceConnection = mServiceConnection; - } - - pw.println("target service=" + serviceConnection.getBoundServiceInfo()); - pw.println("connected=" + serviceConnection.isConnected()); - } - - // runs on the handler thread, and expects most of its methods to be called from that thread - private class MyServiceConnection implements ServiceConnection { - - private final @Nullable TBoundServiceInfo mBoundServiceInfo; - - // volatile so that isConnected can be called from any thread easily - private volatile @Nullable IBinder mBinder; - private @Nullable Runnable mRebinder; - - MyServiceConnection(@Nullable TBoundServiceInfo boundServiceInfo) { - mBoundServiceInfo = boundServiceInfo; - } - - // may be called from any thread - @Nullable TBoundServiceInfo getBoundServiceInfo() { - return mBoundServiceInfo; - } - - // may be called from any thread - boolean isConnected() { - return mBinder != null; - } - - void bind() { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - - if (mBoundServiceInfo == null) { - return; - } - - if (D) { - Log.d(TAG, "[" + mTag + "] binding to " + mBoundServiceInfo); - } - - Intent bindIntent = new Intent(mBoundServiceInfo.getAction()) - .setComponent(mBoundServiceInfo.getComponentName()); - if (!mContext.bindService(bindIntent, - BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE, - mExecutor, this)) { - Log.e(TAG, "[" + mTag + "] unexpected bind failure - retrying later"); - mRebinder = this::bind; - mHandler.postDelayed(mRebinder, RETRY_DELAY_MS); - } else { - mRebinder = null; - } - } - - void unbind() { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - - if (mBoundServiceInfo == null) { - return; - } - - if (D) { - Log.d(TAG, "[" + mTag + "] unbinding from " + mBoundServiceInfo); - } - - if (mRebinder != null) { - mHandler.removeCallbacks(mRebinder); - mRebinder = null; - } else { - mContext.unbindService(this); - } - - onServiceDisconnected(mBoundServiceInfo.getComponentName()); - } - - void runOnBinder(BinderOperation operation) { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - - if (mBinder == null) { - operation.onError(); - return; - } - - try { - operation.run(mBinder); - } catch (RuntimeException | RemoteException e) { - // binders may propagate some specific non-RemoteExceptions from the other side - // through the binder as well - we cannot allow those to crash the system server - Log.e(TAG, "[" + mTag + "] error running operation on " + mBoundServiceInfo, e); - operation.onError(); - } - } - - @Override - public final void onServiceConnected(ComponentName component, IBinder binder) { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - Preconditions.checkState(mBinder == null); - - Log.i(TAG, "[" + mTag + "] connected to " + component.toShortString()); - - mBinder = binder; - - if (mServiceListener != null) { - try { - mServiceListener.onBind(binder, mBoundServiceInfo); - } catch (RuntimeException | RemoteException e) { - // binders may propagate some specific non-RemoteExceptions from the other side - // through the binder as well - we cannot allow those to crash the system server - Log.e(TAG, "[" + mTag + "] error running operation on " + mBoundServiceInfo, e); - } - } - } - - @Override - public final void onServiceDisconnected(ComponentName component) { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - - if (mBinder == null) { - return; - } - - Log.i(TAG, "[" + mTag + "] disconnected from " + mBoundServiceInfo); - - mBinder = null; - if (mServiceListener != null) { - mServiceListener.onUnbind(); - } - } - - @Override - public final void onBindingDied(ComponentName component) { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - - Log.w(TAG, "[" + mTag + "] " + mBoundServiceInfo + " died"); - - // introduce a small delay to prevent spamming binding over and over, since the likely - // cause of a binding dying is some package event that may take time to recover from - mHandler.postDelayed(() -> onServiceChanged(true), 500); - } - - @Override - public final void onNullBinding(ComponentName component) { - Log.e(TAG, "[" + mTag + "] " + mBoundServiceInfo + " has null binding"); - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/Constant.java b/nearby/service/java/com/android/server/nearby/fastpair/Constant.java deleted file mode 100644 index 0695b5f7ca75d80e67dc3caa27bb7d964830d4d2..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/Constant.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair; - -/** - * String constant for half sheet. - */ -public class Constant { - - /** - * Value represents true for {@link android.provider.Settings.Secure} - */ - public static final int SETTINGS_TRUE_VALUE = 1; - - /** - * Tag for Fast Pair service related logs. - */ - public static final String TAG = "FastPairService"; - - public static final String EXTRA_BINDER = "com.android.server.nearby.fastpair.BINDER"; - public static final String EXTRA_BUNDLE = "com.android.server.nearby.fastpair.BUNDLE_EXTRA"; - public static final String ACTION_FAST_PAIR_HALF_SHEET_CANCEL = - "com.android.nearby.ACTION_FAST_PAIR_HALF_SHEET_CANCEL"; - public static final String EXTRA_HALF_SHEET_INFO = - "com.android.nearby.halfsheet.HALF_SHEET"; - public static final String EXTRA_HALF_SHEET_TYPE = - "com.android.nearby.halfsheet.HALF_SHEET_TYPE"; - public static final String DEVICE_PAIRING_FRAGMENT_TYPE = "DEVICE_PAIRING"; -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java deleted file mode 100644 index 2ecce4721b6c0c86265ecb23d43b2f603b3b6a58..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.fastpair; - -import static com.android.server.nearby.fastpair.Constant.TAG; - -import static com.google.common.primitives.Bytes.concat; - -import android.accounts.Account; -import android.annotation.Nullable; -import android.content.Context; -import android.nearby.FastPairDevice; -import android.nearby.NearbyDevice; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.nearby.common.ble.decode.FastPairDecoder; -import com.android.server.nearby.common.ble.util.RangingUtils; -import com.android.server.nearby.common.bloomfilter.BloomFilter; -import com.android.server.nearby.common.bloomfilter.FastPairBloomFilterHasher; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.fastpair.cache.FastPairCacheManager; -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager; -import com.android.server.nearby.provider.FastPairDataProvider; -import com.android.server.nearby.util.DataUtils; -import com.android.server.nearby.util.Hex; - -import java.util.List; - -import service.proto.Cache; -import service.proto.Data; -import service.proto.Rpcs; - -/** - * Handler that handle fast pair related broadcast. - */ -public class FastPairAdvHandler { - Context mContext; - String mBleAddress; - // Need to be deleted after notification manager in use. - private boolean mIsFirst = false; - private FastPairDataProvider mPairDataProvider; - private static final double NEARBY_DISTANCE_THRESHOLD = 0.6; - - /** The types about how the bloomfilter is processed. */ - public enum ProcessBloomFilterType { - IGNORE, // The bloomfilter is not handled. e.g. distance is too far away. - CACHE, // The bloomfilter is recognized in the local cache. - FOOTPRINT, // Need to check the bloomfilter from the footprints. - ACCOUNT_KEY_HIT // The specified account key was hit the bloom filter. - } - - /** - * Constructor function. - */ - public FastPairAdvHandler(Context context) { - mContext = context; - } - - @VisibleForTesting - FastPairAdvHandler(Context context, FastPairDataProvider dataProvider) { - mContext = context; - mPairDataProvider = dataProvider; - } - - /** - * Handles all of the scanner result. Fast Pair will handle model id broadcast bloomfilter - * broadcast and battery level broadcast. - */ - public void handleBroadcast(NearbyDevice device) { - FastPairDevice fastPairDevice = (FastPairDevice) device; - mBleAddress = fastPairDevice.getBluetoothAddress(); - if (mPairDataProvider == null) { - mPairDataProvider = FastPairDataProvider.getInstance(); - } - if (mPairDataProvider == null) { - return; - } - - if (FastPairDecoder.checkModelId(fastPairDevice.getData())) { - byte[] model = FastPairDecoder.getModelId(fastPairDevice.getData()); - Log.d(TAG, "On discovery model id " + Hex.bytesToStringLowercase(model)); - // Use api to get anti spoofing key from model id. - try { - List accountList = mPairDataProvider.loadFastPairEligibleAccounts(); - Rpcs.GetObservedDeviceResponse response = - mPairDataProvider.loadFastPairAntispoofKeyDeviceMetadata(model); - if (response == null) { - Log.e(TAG, "server does not have model id " - + Hex.bytesToStringLowercase(model)); - return; - } - // Check the distance of the device if the distance is larger than the threshold - // do not show half sheet. - if (!isNearby(fastPairDevice.getRssi(), - response.getDevice().getBleTxPower() == 0 ? fastPairDevice.getTxPower() - : response.getDevice().getBleTxPower())) { - return; - } - Locator.get(mContext, FastPairHalfSheetManager.class).showHalfSheet( - DataUtils.toScanFastPairStoreItem( - response, mBleAddress, - accountList.isEmpty() ? null : accountList.get(0).name)); - } catch (IllegalStateException e) { - Log.e(TAG, "OEM does not construct fast pair data proxy correctly"); - } - } else { - // Start to process bloom filter - try { - List accountList = mPairDataProvider.loadFastPairEligibleAccounts(); - byte[] bloomFilterByteArray = FastPairDecoder - .getBloomFilter(fastPairDevice.getData()); - byte[] bloomFilterSalt = FastPairDecoder - .getBloomFilterSalt(fastPairDevice.getData()); - if (bloomFilterByteArray == null || bloomFilterByteArray.length == 0) { - return; - } - for (Account account : accountList) { - List listDevices = - mPairDataProvider.loadFastPairDeviceWithAccountKey(account); - Data.FastPairDeviceWithAccountKey recognizedDevice = - findRecognizedDevice(listDevices, - new BloomFilter(bloomFilterByteArray, - new FastPairBloomFilterHasher()), bloomFilterSalt); - - if (recognizedDevice != null) { - Log.d(TAG, "find matched device show notification to remind" - + " user to pair"); - // Check the distance of the device if the distance is larger than the - // threshold - // do not show half sheet. - if (!isNearby(fastPairDevice.getRssi(), - recognizedDevice.getDiscoveryItem().getTxPower() == 0 - ? fastPairDevice.getTxPower() - : recognizedDevice.getDiscoveryItem().getTxPower())) { - return; - } - // Check if the device is already paired - List storedFastPairItemList = - Locator.get(mContext, FastPairCacheManager.class) - .getAllSavedStoredFastPairItem(); - Cache.StoredFastPairItem recognizedStoredFastPairItem = - findRecognizedDeviceFromCachedItem(storedFastPairItemList, - new BloomFilter(bloomFilterByteArray, - new FastPairBloomFilterHasher()), bloomFilterSalt); - if (recognizedStoredFastPairItem != null) { - // The bloomfilter is recognized in the cache so the device is paired - // before - Log.d(TAG, "bloom filter is recognized in the cache"); - continue; - } else { - Log.d(TAG, "bloom filter is recognized not paired before should" - + "show subsequent pairing notification"); - if (mIsFirst) { - mIsFirst = false; - // Get full info from api the initial request will only return - // part of the info due to size limit. - List resList = - mPairDataProvider.loadFastPairDeviceWithAccountKey(account, - List.of(recognizedDevice.getAccountKey() - .toByteArray())); - if (resList != null && resList.size() > 0) { - //Saved device from footprint does not have ble address so - // fill ble address with current scan result. - Cache.StoredDiscoveryItem storedDiscoveryItem = - resList.get(0).getDiscoveryItem().toBuilder() - .setMacAddress( - fastPairDevice.getBluetoothAddress()) - .build(); - Locator.get(mContext, FastPairController.class).pair( - new DiscoveryItem(mContext, storedDiscoveryItem), - resList.get(0).getAccountKey().toByteArray(), - /** companionApp=*/null); - } - } - } - - return; - } - } - } catch (IllegalStateException e) { - Log.e(TAG, "OEM does not construct fast pair data proxy correctly"); - } - - } - } - - /** - * Checks the bloom filter to see if any of the devices are recognized and should have a - * notification displayed for them. A device is recognized if the account key + salt combination - * is inside the bloom filter. - */ - @Nullable - static Data.FastPairDeviceWithAccountKey findRecognizedDevice( - List devices, BloomFilter bloomFilter, byte[] salt) { - Log.d(TAG, "saved devices size in the account is " + devices.size()); - for (Data.FastPairDeviceWithAccountKey device : devices) { - if (device.getAccountKey().toByteArray() == null || salt == null) { - return null; - } - byte[] rotatedKey = concat(device.getAccountKey().toByteArray(), salt); - StringBuilder sb = new StringBuilder(); - for (byte b : rotatedKey) { - sb.append(b); - } - if (bloomFilter.possiblyContains(rotatedKey)) { - Log.d(TAG, "match " + sb.toString()); - return device; - } else { - Log.d(TAG, "not match " + sb.toString()); - } - } - return null; - } - - @Nullable - static Cache.StoredFastPairItem findRecognizedDeviceFromCachedItem( - List devices, BloomFilter bloomFilter, byte[] salt) { - for (Cache.StoredFastPairItem device : devices) { - if (device.getAccountKey().toByteArray() == null || salt == null) { - return null; - } - byte[] rotatedKey = concat(device.getAccountKey().toByteArray(), salt); - if (bloomFilter.possiblyContains(rotatedKey)) { - return device; - } - } - return null; - } - - /** - * Check the device distance for certain rssi value. - */ - boolean isNearby(int rssi, int txPower) { - return RangingUtils.distanceFromRssiAndTxPower(rssi, txPower) < NEARBY_DISTANCE_THRESHOLD; - } - -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java deleted file mode 100644 index e1db7e56e68deda7c61d459022dad5a46d4a8677..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairController.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair; - -import static com.google.common.primitives.Bytes.concat; - -import android.accounts.Account; -import android.annotation.Nullable; -import android.content.Context; -import android.nearby.FastPairDevice; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.WorkerThread; - -import com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress; -import com.android.server.nearby.common.eventloop.Annotations; -import com.android.server.nearby.common.eventloop.EventLoop; -import com.android.server.nearby.common.eventloop.NamedRunnable; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.fastpair.cache.FastPairCacheManager; -import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo; -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager; -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager; -import com.android.server.nearby.fastpair.notification.FastPairNotificationManager; -import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase; -import com.android.server.nearby.provider.FastPairDataProvider; - -import com.google.common.hash.Hashing; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import service.proto.Cache; - -/** - * FastPair controller after get the info from intent handler Fast Pair controller is responsible - * for pairing control. - */ -public class FastPairController { - private static final String TAG = "FastPairController"; - private final Context mContext; - private final EventLoop mEventLoop; - private final FastPairCacheManager mFastPairCacheManager; - private final FootprintsDeviceManager mFootprintsDeviceManager; - private boolean mIsFastPairing = false; - // boolean flag whether upload to footprint or not. - private boolean mShouldUpload = false; - @Nullable - private Callback mCallback; - - public FastPairController(Context context) { - mContext = context; - mEventLoop = Locator.get(mContext, EventLoop.class); - mFastPairCacheManager = Locator.get(mContext, FastPairCacheManager.class); - mFootprintsDeviceManager = Locator.get(mContext, FootprintsDeviceManager.class); - } - - /** - * Should be called on create lifecycle. - */ - @WorkerThread - public void onCreate() { - mEventLoop.postRunnable(new NamedRunnable("FastPairController::InitializeScanner") { - @Override - public void run() { - // init scanner here and start scan. - } - }); - } - - /** - * Should be called on destroy lifecycle. - */ - @WorkerThread - public void onDestroy() { - mEventLoop.postRunnable(new NamedRunnable("FastPairController::DestroyScanner") { - @Override - public void run() { - // Unregister scanner from here - } - }); - } - - /** - * Pairing function. - */ - public void pair(FastPairDevice fastPairDevice) { - byte[] discoveryItem = fastPairDevice.getData(); - String modelId = fastPairDevice.getModelId(); - - Log.v(TAG, "pair: fastPairDevice " + fastPairDevice); - mEventLoop.postRunnable( - new NamedRunnable("fastPairWith=" + modelId) { - @Override - public void run() { - try { - DiscoveryItem item = new DiscoveryItem(mContext, - Cache.StoredDiscoveryItem.parseFrom(discoveryItem)); - if (TextUtils.isEmpty(item.getMacAddress())) { - Log.w(TAG, "There is no mac address in the DiscoveryItem," - + " ignore pairing"); - return; - } - // Check enabled state to prevent multiple pair attempts if we get the - // intent more than once (this can happen due to an Android platform - // bug - b/31459521). - if (item.getState() - != Cache.StoredDiscoveryItem.State.STATE_ENABLED) { - Log.d(TAG, "Incorrect state, ignore pairing"); - return; - } - boolean useLargeNotifications = - item.getAuthenticationPublicKeySecp256R1() != null; - FastPairNotificationManager fastPairNotificationManager = - new FastPairNotificationManager(mContext, item, - useLargeNotifications); - FastPairHalfSheetManager fastPairHalfSheetManager = - Locator.get(mContext, FastPairHalfSheetManager.class); - mFastPairCacheManager.saveDiscoveryItem(item); - - PairingProgressHandlerBase pairingProgressHandlerBase = - PairingProgressHandlerBase.create( - mContext, - item, - /* companionApp= */ null, - /* accountKey= */ null, - mFootprintsDeviceManager, - fastPairNotificationManager, - fastPairHalfSheetManager, - /* isRetroactivePair= */ false); - - pair(item, - /* accountKey= */ null, - /* companionApp= */ null, - pairingProgressHandlerBase); - } catch (InvalidProtocolBufferException e) { - Log.w(TAG, - "Error parsing serialized discovery item with size " - + discoveryItem.length); - } - } - }); - } - - /** - * Subsequent pairing entry. - */ - public void pair(DiscoveryItem item, - @Nullable byte[] accountKey, - @Nullable String companionApp) { - FastPairNotificationManager fastPairNotificationManager = - new FastPairNotificationManager(mContext, item, false); - FastPairHalfSheetManager fastPairHalfSheetManager = - Locator.get(mContext, FastPairHalfSheetManager.class); - PairingProgressHandlerBase pairingProgressHandlerBase = - PairingProgressHandlerBase.create( - mContext, - item, - /* companionApp= */ null, - /* accountKey= */ accountKey, - mFootprintsDeviceManager, - fastPairNotificationManager, - fastPairHalfSheetManager, - /* isRetroactivePair= */ false); - pair(item, accountKey, companionApp, pairingProgressHandlerBase); - } - /** - * Pairing function - */ - @Annotations.EventThread - public void pair( - DiscoveryItem item, - @Nullable byte[] accountKey, - @Nullable String companionApp, - PairingProgressHandlerBase pairingProgressHandlerBase) { - if (mIsFastPairing) { - Log.d(TAG, "FastPair: fastpairing, skip pair request"); - return; - } - mIsFastPairing = true; - Log.d(TAG, "FastPair: start pair"); - - // Hide all "tap to pair" notifications until after the flow completes. - mEventLoop.removeRunnable(mReEnableAllDeviceItemsRunnable); - if (mCallback != null) { - mCallback.fastPairUpdateDeviceItemsEnabled(false); - } - - Future task = - FastPairManager.pair( - Executors.newSingleThreadExecutor(), - mContext, - item, - accountKey, - companionApp, - mFootprintsDeviceManager, - pairingProgressHandlerBase); - mIsFastPairing = false; - } - - /** Fixes a companion app package name with extra spaces. */ - private static String trimCompanionApp(String companionApp) { - return companionApp == null ? null : companionApp.trim(); - } - - /** - * Function to handle when scanner find bloomfilter. - */ - @Annotations.EventThread - public FastPairAdvHandler.ProcessBloomFilterType onBloomFilterDetect(FastPairAdvHandler handler, - boolean advertiseInRange) { - if (mIsFastPairing) { - return FastPairAdvHandler.ProcessBloomFilterType.IGNORE; - } - // Check if the device is in the cache or footprint. - return FastPairAdvHandler.ProcessBloomFilterType.CACHE; - } - - /** - * Add newly paired device info to footprint - */ - @WorkerThread - public void addDeviceToFootprint(String publicAddress, byte[] accountKey, - DiscoveryItem discoveryItem) { - if (!mShouldUpload) { - return; - } - Log.d(TAG, "upload device to footprint"); - FastPairManager.processBackgroundTask(() -> { - Cache.StoredDiscoveryItem storedDiscoveryItem = - prepareStoredDiscoveryItemForFootprints(discoveryItem); - byte[] hashValue = - Hashing.sha256() - .hashBytes( - concat(accountKey, BluetoothAddress.decode(publicAddress))) - .asBytes(); - FastPairUploadInfo uploadInfo = - new FastPairUploadInfo(storedDiscoveryItem, ByteString.copyFrom(accountKey), - ByteString.copyFrom(hashValue)); - // account data place holder here - try { - FastPairDataProvider fastPairDataProvider = FastPairDataProvider.getInstance(); - if (fastPairDataProvider == null) { - return; - } - List accountList = fastPairDataProvider.loadFastPairEligibleAccounts(); - if (accountList.size() > 0) { - fastPairDataProvider.optIn(accountList.get(0)); - fastPairDataProvider.upload(accountList.get(0), uploadInfo); - } - } catch (IllegalStateException e) { - Log.e(TAG, "OEM does not construct fast pair data proxy correctly"); - } - }); - } - - @Nullable - private Cache.StoredDiscoveryItem getStoredDiscoveryItemFromAddressForFootprints( - String bleAddress) { - - List discoveryItems = new ArrayList<>(); - //cacheManager.getAllDiscoveryItems(); - for (DiscoveryItem discoveryItem : discoveryItems) { - if (bleAddress.equals(discoveryItem.getMacAddress())) { - return prepareStoredDiscoveryItemForFootprints(discoveryItem); - } - } - return null; - } - - static Cache.StoredDiscoveryItem prepareStoredDiscoveryItemForFootprints( - DiscoveryItem discoveryItem) { - Cache.StoredDiscoveryItem.Builder storedDiscoveryItem = - discoveryItem.getCopyOfStoredItem().toBuilder(); - // Strip the mac address so we aren't storing it in the cloud and ensure the item always - // starts as enabled and in a good state. - storedDiscoveryItem.clearMacAddress(); - - return storedDiscoveryItem.build(); - } - - /** - * FastPairConnection will check whether write account key result if the account key is - * generated change the parameter. - */ - public void setShouldUpload(boolean shouldUpload) { - mShouldUpload = shouldUpload; - } - - private final NamedRunnable mReEnableAllDeviceItemsRunnable = - new NamedRunnable("reEnableAllDeviceItems") { - @Override - public void run() { - if (mCallback != null) { - mCallback.fastPairUpdateDeviceItemsEnabled(true); - } - } - }; - - interface Callback { - void fastPairUpdateDeviceItemsEnabled(boolean enabled); - } -} \ No newline at end of file diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java deleted file mode 100644 index f3680802449f6ad16a79507437acfcf0d7cff5b3..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair; - -import static com.android.server.nearby.fastpair.Constant.TAG; - -import android.annotation.Nullable; -import android.annotation.WorkerThread; -import android.app.KeyguardManager; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothManager; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.database.ContentObserver; -import android.nearby.FastPairDevice; -import android.nearby.NearbyDevice; -import android.nearby.NearbyManager; -import android.nearby.ScanCallback; -import android.nearby.ScanRequest; -import android.net.Uri; -import android.provider.Settings; -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.android.server.nearby.common.ble.decode.FastPairDecoder; -import com.android.server.nearby.common.bluetooth.BluetoothException; -import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection; -import com.android.server.nearby.common.bluetooth.fastpair.FastPairDualConnection; -import com.android.server.nearby.common.bluetooth.fastpair.PairingException; -import com.android.server.nearby.common.bluetooth.fastpair.Preferences; -import com.android.server.nearby.common.bluetooth.fastpair.ReflectionException; -import com.android.server.nearby.common.bluetooth.fastpair.SimpleBroadcastReceiver; -import com.android.server.nearby.common.eventloop.Annotations; -import com.android.server.nearby.common.eventloop.EventLoop; -import com.android.server.nearby.common.eventloop.NamedRunnable; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.common.locator.LocatorContextWrapper; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.fastpair.cache.FastPairCacheManager; -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager; -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager; -import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase; -import com.android.server.nearby.util.ForegroundThread; -import com.android.server.nearby.util.Hex; - -import com.google.common.collect.ImmutableList; -import com.google.protobuf.ByteString; - -import java.security.GeneralSecurityException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import service.proto.Cache; -import service.proto.Rpcs; - -/** - * FastPairManager is the class initiated in nearby service to handle Fast Pair related - * work. - */ - -public class FastPairManager { - - private static final String ACTION_PREFIX = UserActionHandler.PREFIX; - private static final int WAIT_FOR_UNLOCK_MILLIS = 5000; - - /** A notification ID which should be dismissed */ - public static final String EXTRA_NOTIFICATION_ID = ACTION_PREFIX + "EXTRA_NOTIFICATION_ID"; - public static final String ACTION_RESOURCES_APK = "android.nearby.SHOW_HALFSHEET"; - - private static Executor sFastPairExecutor; - - private ContentObserver mFastPairScanChangeContentObserver = null; - - final LocatorContextWrapper mLocatorContextWrapper; - final IntentFilter mIntentFilter; - final Locator mLocator; - private boolean mScanEnabled; - - private final BroadcastReceiver mScreenBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_SCREEN_ON) - || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { - Log.d(TAG, "onReceive: ACTION_SCREEN_ON or boot complete."); - invalidateScan(); - } else if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { - processBluetoothConnectionEvent(intent); - } - } - }; - - public FastPairManager(LocatorContextWrapper contextWrapper) { - mLocatorContextWrapper = contextWrapper; - mIntentFilter = new IntentFilter(); - mLocator = mLocatorContextWrapper.getLocator(); - mLocator.bind(new FastPairModule()); - Rpcs.GetObservedDeviceResponse getObservedDeviceResponse = - Rpcs.GetObservedDeviceResponse.newBuilder().build(); - } - - final ScanCallback mScanCallback = new ScanCallback() { - @Override - public void onDiscovered(@NonNull NearbyDevice device) { - Locator.get(mLocatorContextWrapper, FastPairAdvHandler.class).handleBroadcast(device); - } - - @Override - public void onUpdated(@NonNull NearbyDevice device) { - FastPairDevice fastPairDevice = (FastPairDevice) device; - byte[] modelArray = FastPairDecoder.getModelId(fastPairDevice.getData()); - Log.d(TAG, "update model id" + Hex.bytesToStringLowercase(modelArray)); - } - - @Override - public void onLost(@NonNull NearbyDevice device) { - FastPairDevice fastPairDevice = (FastPairDevice) device; - byte[] modelArray = FastPairDecoder.getModelId(fastPairDevice.getData()); - Log.d(TAG, "lost model id" + Hex.bytesToStringLowercase(modelArray)); - } - }; - - /** - * Function called when nearby service start. - */ - public void initiate() { - mIntentFilter.addAction(Intent.ACTION_SCREEN_ON); - mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF); - mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); - mIntentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); - - mLocatorContextWrapper.getContext() - .registerReceiver(mScreenBroadcastReceiver, mIntentFilter); - - Locator.getFromContextWrapper(mLocatorContextWrapper, FastPairCacheManager.class); - // Default false for now. - mScanEnabled = NearbyManager.getFastPairScanEnabled(mLocatorContextWrapper.getContext()); - registerFastPairScanChangeContentObserver(mLocatorContextWrapper.getContentResolver()); - } - - /** - * Function to free up fast pair resource. - */ - public void cleanUp() { - mLocatorContextWrapper.getContext().unregisterReceiver(mScreenBroadcastReceiver); - if (mFastPairScanChangeContentObserver != null) { - mLocatorContextWrapper.getContentResolver().unregisterContentObserver( - mFastPairScanChangeContentObserver); - } - } - - /** - * Starts fast pair process. - */ - @Annotations.EventThread - public static Future pair( - ExecutorService executor, - Context context, - DiscoveryItem item, - @Nullable byte[] accountKey, - @Nullable String companionApp, - FootprintsDeviceManager footprints, - PairingProgressHandlerBase pairingProgressHandlerBase) { - return executor.submit( - () -> pairInternal(context, item, companionApp, accountKey, footprints, - pairingProgressHandlerBase), /* result= */ null); - } - - /** - * Starts fast pair - */ - @WorkerThread - public static void pairInternal( - Context context, - DiscoveryItem item, - @Nullable String companionApp, - @Nullable byte[] accountKey, - FootprintsDeviceManager footprints, - PairingProgressHandlerBase pairingProgressHandlerBase) { - FastPairHalfSheetManager fastPairHalfSheetManager = - Locator.get(context, FastPairHalfSheetManager.class); - try { - pairingProgressHandlerBase.onPairingStarted(); - if (pairingProgressHandlerBase.skipWaitingScreenUnlock()) { - // Do nothing due to we are not showing the status notification in some pairing - // types, e.g. the retroactive pairing. - } else { - // If the screen is locked when the user taps to pair, the screen will unlock. We - // must wait for the unlock to complete before showing the status notification, or - // it won't be heads-up. - pairingProgressHandlerBase.onWaitForScreenUnlock(); - waitUntilScreenIsUnlocked(context); - pairingProgressHandlerBase.onScreenUnlocked(); - } - BluetoothAdapter bluetoothAdapter = getBluetoothAdapter(context); - - boolean isBluetoothEnabled = bluetoothAdapter != null && bluetoothAdapter.isEnabled(); - if (!isBluetoothEnabled) { - if (bluetoothAdapter == null || !bluetoothAdapter.enable()) { - Log.d(TAG, "FastPair: Failed to enable bluetooth"); - return; - } - Log.v(TAG, "FastPair: Enabling bluetooth for fast pair"); - - Locator.get(context, EventLoop.class) - .postRunnable( - new NamedRunnable("enableBluetoothToast") { - @Override - public void run() { - Log.d(TAG, "Enable bluetooth toast test"); - } - }); - // Set up call back to call this function again once bluetooth has been - // enabled; this does not seem to be a problem as the device connects without a - // problem, but in theory the timeout also includes turning on bluetooth now. - } - - pairingProgressHandlerBase.onReadyToPair(); - - String modelId = item.getTriggerId(); - Preferences.Builder prefsBuilder = - Preferences.builderFromGmsLog() - .setEnableBrEdrHandover(false) - .setIgnoreDiscoveryError(true); - pairingProgressHandlerBase.onSetupPreferencesBuilder(prefsBuilder); - if (item.getFastPairInformation() != null) { - prefsBuilder.setSkipConnectingProfiles( - item.getFastPairInformation().getDataOnlyConnection()); - } - // When add watch and auto device needs to change the config - prefsBuilder.setRejectMessageAccess(true); - prefsBuilder.setRejectPhonebookAccess(true); - prefsBuilder.setHandlePasskeyConfirmationByUi(false); - - FastPairConnection connection = new FastPairDualConnection( - context, item.getMacAddress(), - prefsBuilder.build(), - null); - pairingProgressHandlerBase.onPairingSetupCompleted(); - - FastPairConnection.SharedSecret sharedSecret; - if ((accountKey != null || item.getAuthenticationPublicKeySecp256R1() != null)) { - sharedSecret = - connection.pair( - accountKey != null ? accountKey - : item.getAuthenticationPublicKeySecp256R1()); - if (accountKey == null) { - // Account key is null so it is initial pairing - if (sharedSecret != null) { - Locator.get(context, FastPairController.class).addDeviceToFootprint( - connection.getPublicAddress(), sharedSecret.getKey(), item); - cacheFastPairDevice(context, connection.getPublicAddress(), - sharedSecret.getKey(), item); - } - } - } else { - // Fast Pair one - connection.pair(); - } - // TODO(b/213373051): Merge logic with pairingProgressHandlerBase or delete the - // pairingProgressHandlerBase class. - fastPairHalfSheetManager.showPairingSuccessHalfSheet(connection.getPublicAddress()); - pairingProgressHandlerBase.onPairingSuccess(connection.getPublicAddress()); - } catch (BluetoothException - | InterruptedException - | ReflectionException - | TimeoutException - | ExecutionException - | PairingException - | GeneralSecurityException e) { - Log.e(TAG, "Failed to pair.", e); - - // TODO(b/213373051): Merge logic with pairingProgressHandlerBase or delete the - // pairingProgressHandlerBase class. - fastPairHalfSheetManager.showPairingFailed(); - pairingProgressHandlerBase.onPairingFailed(e); - } - } - - private static void cacheFastPairDevice(Context context, String publicAddress, byte[] key, - DiscoveryItem item) { - try { - Locator.get(context, EventLoop.class).postAndWait( - new NamedRunnable("FastPairCacheDevice") { - @Override - public void run() { - Cache.StoredFastPairItem storedFastPairItem = - Cache.StoredFastPairItem.newBuilder() - .setMacAddress(publicAddress) - .setAccountKey(ByteString.copyFrom(key)) - .setModelId(item.getTriggerId()) - .addAllFeatures(item.getFastPairInformation() == null - ? ImmutableList.of() : - item.getFastPairInformation().getFeaturesList()) - .setDiscoveryItem(item.getCopyOfStoredItem()) - .build(); - Locator.get(context, FastPairCacheManager.class) - .putStoredFastPairItem(storedFastPairItem); - } - } - ); - } catch (InterruptedException e) { - Log.e(TAG, "Fail to insert paired device into cache"); - } - } - - /** Checks if the pairing is initial pairing with fast pair 2.0 design. */ - public static boolean isThroughFastPair2InitialPairing( - DiscoveryItem item, @Nullable byte[] accountKey) { - return accountKey == null && item.getAuthenticationPublicKeySecp256R1() != null; - } - - private static void waitUntilScreenIsUnlocked(Context context) - throws InterruptedException, ExecutionException, TimeoutException { - KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); - - // KeyguardManager's isScreenLocked() counterintuitively returns false when the lock screen - // is showing if the user has set "swipe to unlock" (i.e. no required password, PIN, or - // pattern) So we use this method instead, which returns true when on the lock screen - // regardless. - if (keyguardManager.isKeyguardLocked()) { - Log.v(TAG, "FastPair: Screen is locked, waiting until unlocked " - + "to show status notifications."); - try (SimpleBroadcastReceiver isUnlockedReceiver = - SimpleBroadcastReceiver.oneShotReceiver( - context, FlagUtils.getPreferencesBuilder().build(), - Intent.ACTION_USER_PRESENT)) { - isUnlockedReceiver.await(WAIT_FOR_UNLOCK_MILLIS, TimeUnit.MILLISECONDS); - } - } - } - - private void registerFastPairScanChangeContentObserver(ContentResolver resolver) { - mFastPairScanChangeContentObserver = new ContentObserver(ForegroundThread.getHandler()) { - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - setScanEnabled( - NearbyManager.getFastPairScanEnabled(mLocatorContextWrapper.getContext())); - } - }; - try { - resolver.registerContentObserver( - Settings.Secure.getUriFor(NearbyManager.FAST_PAIR_SCAN_ENABLED), - /* notifyForDescendants= */ false, - mFastPairScanChangeContentObserver); - } catch (SecurityException e) { - Log.e(TAG, "Failed to register content observer for fast pair scan.", e); - } - } - - /** - * Processed task in a background thread - */ - @Annotations.EventThread - public static void processBackgroundTask(Runnable runnable) { - getExecutor().execute(runnable); - } - - /** - * This function should only be called on main thread since there is no lock - */ - private static Executor getExecutor() { - if (sFastPairExecutor != null) { - return sFastPairExecutor; - } - sFastPairExecutor = Executors.newSingleThreadExecutor(); - return sFastPairExecutor; - } - - /** - * Null when the Nearby Service is not available. - */ - @Nullable - private NearbyManager getNearbyManager() { - return (NearbyManager) mLocatorContextWrapper - .getApplicationContext().getSystemService(Context.NEARBY_SERVICE); - } - private void setScanEnabled(boolean scanEnabled) { - if (mScanEnabled == scanEnabled) { - return; - } - mScanEnabled = scanEnabled; - invalidateScan(); - } - - /** - * Starts or stops scanning according to mAllowScan value. - */ - private void invalidateScan() { - NearbyManager nearbyManager = getNearbyManager(); - if (nearbyManager == null) { - Log.w(TAG, "invalidateScan: " - + "failed to start or stop scannning because NearbyManager is null."); - return; - } - if (mScanEnabled) { - Log.v(TAG, "invalidateScan: scan is enabled"); - nearbyManager.startScan(new ScanRequest.Builder() - .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR).build(), - ForegroundThread.getExecutor(), - mScanCallback); - } else { - Log.v(TAG, "invalidateScan: scan is disabled"); - nearbyManager.stopScan(mScanCallback); - } - } - - /** - * When certain device is forgotten we need to remove the info from database because the info - * is no longer useful. - */ - private void processBluetoothConnectionEvent(Intent intent) { - int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, - BluetoothDevice.ERROR); - if (bondState == BluetoothDevice.BOND_NONE) { - BluetoothDevice device = - intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (device != null) { - Log.d("FastPairService", "Forget device detect"); - processBackgroundTask(new Runnable() { - @Override - public void run() { - mLocatorContextWrapper.getLocator().get(FastPairCacheManager.class) - .removeStoredFastPairItem(device.getAddress()); - } - }); - } - - } - } - - /** - * Helper function to get bluetooth adapter. - */ - @Nullable - public static BluetoothAdapter getBluetoothAdapter(Context context) { - BluetoothManager manager = context.getSystemService(BluetoothManager.class); - return manager == null ? null : manager.getAdapter(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java deleted file mode 100644 index d7946d1e8ecaef863c69ee4c1c3c41505b1e78b2..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair; - -import android.content.Context; - -import com.android.server.nearby.common.eventloop.EventLoop; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.common.locator.Module; -import com.android.server.nearby.fastpair.cache.FastPairCacheManager; -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager; -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager; - -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; - -/** - * Module that associates all of the fast pair related singleton class - */ -public class FastPairModule extends Module { - /** - * Initiate the class that needs to be singleton. - */ - @Override - public void configure(Context context, Class type, Locator locator) { - if (type.equals(FastPairCacheManager.class)) { - locator.bind(FastPairCacheManager.class, new FastPairCacheManager(context)); - } else if (type.equals(FootprintsDeviceManager.class)) { - locator.bind(FootprintsDeviceManager.class, new FootprintsDeviceManager()); - } else if (type.equals(EventLoop.class)) { - locator.bind(EventLoop.class, EventLoop.newInstance("NearbyFastPair")); - } else if (type.equals(FastPairController.class)) { - locator.bind(FastPairController.class, new FastPairController(context)); - } else if (type.equals(FastPairCacheManager.class)) { - locator.bind(FastPairCacheManager.class, new FastPairCacheManager(context)); - } else if (type.equals(FastPairHalfSheetManager.class)) { - locator.bind(FastPairHalfSheetManager.class, new FastPairHalfSheetManager(context)); - } else if (type.equals(FastPairAdvHandler.class)) { - locator.bind(FastPairAdvHandler.class, new FastPairAdvHandler(context)); - } else if (type.equals(Clock.class)) { - locator.bind(Clock.class, new Clock() { - @Override - public ZoneId getZone() { - return null; - } - - @Override - public Clock withZone(ZoneId zone) { - return null; - } - - @Override - public Instant instant() { - return null; - } - }); - } - - } - - /** - * Clean up the singleton classes. - */ - @Override - public void destroy(Context context, Class type, Object instance) { - super.destroy(context, type, instance); - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FlagUtils.java b/nearby/service/java/com/android/server/nearby/fastpair/FlagUtils.java deleted file mode 100644 index 883a1f895f9fecbec9862dd048aeee3c88d8400a..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/FlagUtils.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair; - -import android.text.TextUtils; - -import com.android.server.nearby.common.bluetooth.fastpair.Preferences; - -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableSet; - -/** - * This is fast pair connection preference - */ -public class FlagUtils { - private static final int GATT_OPERATION_TIME_OUT_SECOND = 10; - private static final int GATT_CONNECTION_TIME_OUT_SECOND = 15; - private static final int BLUETOOTH_TOGGLE_TIME_OUT_SECOND = 10; - private static final int BLUETOOTH_TOGGLE_SLEEP_TIME_OUT_SECOND = 2; - private static final int CLASSIC_DISCOVERY_TIME_OUT_SECOND = 13; - private static final int NUM_DISCOVER_ATTEMPTS = 3; - private static final int DISCOVERY_RETRY_SLEEP_SECONDS = 1; - private static final int SDP_TIME_OUT_SECONDS = 10; - private static final int NUM_SDP_ATTEMPTS = 0; - private static final int NUM_CREATED_BOND_ATTEMPTS = 3; - private static final int NUM_CONNECT_ATTEMPT = 2; - private static final int NUM_WRITE_ACCOUNT_KEY_ATTEMPT = 3; - private static final boolean TOGGLE_BLUETOOTH_ON_FAILURE = false; - private static final boolean BLUETOOTH_STATE_POOLING = true; - private static final int BLUETOOTH_STATE_POOLING_MILLIS = 1000; - private static final int NUM_ATTEMPTS = 2; - private static final short BREDR_HANDOVER_DATA_CHARACTERISTIC_ID = 11265; // 0x2c01 - private static final short BLUETOOTH_SIG_DATA_CHARACTERISTIC_ID = 11266; // 0x2c02 - private static final short TRANSPORT_BLOCK_DATA_CHARACTERISTIC_ID = 11267; // 0x2c03 - private static final boolean WAIT_FOR_UUID_AFTER_BONDING = true; - private static final boolean RECEIVE_UUID_AND_BONDED_EVENT_BEFORE_CLOSE = true; - private static final int REMOVE_BOND_TIME_OUT_SECONDS = 5; - private static final int REMOVE_BOND_SLEEP_MILLIS = 1000; - private static final int CREATE_BOND_TIME_OUT_SECONDS = 15; - private static final int HIDE_CREATED_BOND_TIME_OUT_SECONDS = 40; - private static final int PROXY_TIME_OUT_SECONDS = 2; - private static final boolean REJECT_ACCESS = false; - private static final boolean ACCEPT_PASSKEY = true; - private static final int WRITE_ACCOUNT_KEY_SLEEP_MILLIS = 2000; - private static final boolean PROVIDER_INITIATE_BONDING = false; - private static final boolean SPECIFY_CREATE_BOND_TRANSPORT_TYPE = false; - private static final int CREATE_BOND_TRANSPORT_TYPE = 0; - private static final boolean KEEP_SAME_ACCOUNT_KEY_WRITE = true; - private static final boolean ENABLE_NAMING_CHARACTERISTIC = true; - private static final boolean CHECK_FIRMWARE_VERSION = true; - private static final int SDP_ATTEMPTS_AFTER_BONDED = 1; - private static final boolean SUPPORT_HID = false; - private static final boolean ENABLE_PAIRING_WHILE_DIRECTLY_CONNECTING = true; - private static final boolean ACCEPT_CONSENT_FOR_FP_ONE = true; - private static final int GATT_CONNECT_RETRY_TIMEOUT_MILLIS = 18000; - private static final boolean ENABLE_128BIT_CUSTOM_GATT_CHARACTERISTIC = true; - private static final boolean ENABLE_SEND_EXCEPTION_STEP_TO_VALIDATOR = true; - private static final boolean ENABLE_ADDITIONAL_DATA_TYPE_WHEN_ACTION_OVER_BLE = true; - private static final boolean CHECK_BOND_STATE_WHEN_SKIP_CONNECTING_PROFILE = true; - private static final boolean MORE_LOG_FOR_QUALITY = true; - private static final boolean RETRY_GATT_CONNECTION_AND_SECRET_HANDSHAKE = true; - private static final int GATT_CONNECT_SHORT_TIMEOUT_MS = 7000; - private static final int GATT_CONNECTION_LONG_TIME_OUT_MS = 15000; - private static final int GATT_CONNECT_SHORT_TIMEOUT_RETRY_MAX_SPENT_TIME_MS = 1000; - private static final int ADDRESS_ROTATE_RETRY_MAX_SPENT_TIME_MS = 15000; - private static final int PAIRING_RETRY_DELAY_MS = 100; - private static final int HANDSHAKE_SHORT_TIMEOUT_MS = 3000; - private static final int HANDSHAKE_LONG_TIMEOUT_MS = 1000; - private static final int SECRET_HANDSHAKE_SHORT_TIMEOUT_RETRY_MAX_SPENT_TIME_MS = 5000; - private static final int SECRET_HANDSHAKE_LONG_TIMEOUT_RETRY_MAX_SPENT_TIME_MS = 7000; - private static final int SECRET_HANDSHAKE_RETRY_ATTEMPTS = 3; - private static final int SECRET_HANDSHAKE_RETRY_GATT_CONNECTION_MAX_SPENT_TIME_MS = 15000; - private static final int SIGNAL_LOST_RETRY_MAX_SPENT_TIME_MS = 15000; - private static final boolean RETRY_SECRET_HANDSHAKE_TIMEOUT = false; - private static final boolean LOG_USER_MANUAL_RETRY = true; - private static final boolean ENABLE_PAIR_FLOW_SHOW_UI_WITHOUT_PROFILE_CONNECTION = false; - private static final boolean LOG_USER_MANUAL_CITY = true; - private static final boolean LOG_PAIR_WITH_CACHED_MODEL_ID = true; - private static final boolean DIRECT_CONNECT_PROFILE_IF_MODEL_ID_IN_CACHE = false; - - public static Preferences.Builder getPreferencesBuilder() { - return Preferences.builder() - .setGattOperationTimeoutSeconds(GATT_OPERATION_TIME_OUT_SECOND) - .setGattConnectionTimeoutSeconds(GATT_CONNECTION_TIME_OUT_SECOND) - .setBluetoothToggleTimeoutSeconds(BLUETOOTH_TOGGLE_TIME_OUT_SECOND) - .setBluetoothToggleSleepSeconds(BLUETOOTH_TOGGLE_SLEEP_TIME_OUT_SECOND) - .setClassicDiscoveryTimeoutSeconds(CLASSIC_DISCOVERY_TIME_OUT_SECOND) - .setNumDiscoverAttempts(NUM_DISCOVER_ATTEMPTS) - .setDiscoveryRetrySleepSeconds(DISCOVERY_RETRY_SLEEP_SECONDS) - .setSdpTimeoutSeconds(SDP_TIME_OUT_SECONDS) - .setNumSdpAttempts(NUM_SDP_ATTEMPTS) - .setNumCreateBondAttempts(NUM_CREATED_BOND_ATTEMPTS) - .setNumConnectAttempts(NUM_CONNECT_ATTEMPT) - .setNumWriteAccountKeyAttempts(NUM_WRITE_ACCOUNT_KEY_ATTEMPT) - .setToggleBluetoothOnFailure(TOGGLE_BLUETOOTH_ON_FAILURE) - .setBluetoothStateUsesPolling(BLUETOOTH_STATE_POOLING) - .setBluetoothStatePollingMillis(BLUETOOTH_STATE_POOLING_MILLIS) - .setNumAttempts(NUM_ATTEMPTS) - .setBrHandoverDataCharacteristicId(BREDR_HANDOVER_DATA_CHARACTERISTIC_ID) - .setBluetoothSigDataCharacteristicId(BLUETOOTH_SIG_DATA_CHARACTERISTIC_ID) - .setBrTransportBlockDataDescriptorId(TRANSPORT_BLOCK_DATA_CHARACTERISTIC_ID) - .setWaitForUuidsAfterBonding(WAIT_FOR_UUID_AFTER_BONDING) - .setReceiveUuidsAndBondedEventBeforeClose( - RECEIVE_UUID_AND_BONDED_EVENT_BEFORE_CLOSE) - .setRemoveBondTimeoutSeconds(REMOVE_BOND_TIME_OUT_SECONDS) - .setRemoveBondSleepMillis(REMOVE_BOND_SLEEP_MILLIS) - .setCreateBondTimeoutSeconds(CREATE_BOND_TIME_OUT_SECONDS) - .setHidCreateBondTimeoutSeconds(HIDE_CREATED_BOND_TIME_OUT_SECONDS) - .setProxyTimeoutSeconds(PROXY_TIME_OUT_SECONDS) - .setRejectPhonebookAccess(REJECT_ACCESS) - .setRejectMessageAccess(REJECT_ACCESS) - .setRejectSimAccess(REJECT_ACCESS) - .setAcceptPasskey(ACCEPT_PASSKEY) - .setWriteAccountKeySleepMillis(WRITE_ACCOUNT_KEY_SLEEP_MILLIS) - .setProviderInitiatesBondingIfSupported(PROVIDER_INITIATE_BONDING) - .setAttemptDirectConnectionWhenPreviouslyBonded(true) - .setAutomaticallyReconnectGattWhenNeeded(true) - .setSkipDisconnectingGattBeforeWritingAccountKey(true) - .setIgnoreUuidTimeoutAfterBonded(true) - .setSpecifyCreateBondTransportType(SPECIFY_CREATE_BOND_TRANSPORT_TYPE) - .setCreateBondTransportType(CREATE_BOND_TRANSPORT_TYPE) - .setIncreaseIntentFilterPriority(true) - .setEvaluatePerformance(false) - .setKeepSameAccountKeyWrite(KEEP_SAME_ACCOUNT_KEY_WRITE) - .setEnableNamingCharacteristic(ENABLE_NAMING_CHARACTERISTIC) - .setEnableFirmwareVersionCharacteristic(CHECK_FIRMWARE_VERSION) - .setNumSdpAttemptsAfterBonded(SDP_ATTEMPTS_AFTER_BONDED) - .setSupportHidDevice(SUPPORT_HID) - .setEnablePairingWhileDirectlyConnecting( - ENABLE_PAIRING_WHILE_DIRECTLY_CONNECTING) - .setAcceptConsentForFastPairOne(ACCEPT_CONSENT_FOR_FP_ONE) - .setGattConnectRetryTimeoutMillis(GATT_CONNECT_RETRY_TIMEOUT_MILLIS) - .setEnable128BitCustomGattCharacteristicsId( - ENABLE_128BIT_CUSTOM_GATT_CHARACTERISTIC) - .setEnableSendExceptionStepToValidator(ENABLE_SEND_EXCEPTION_STEP_TO_VALIDATOR) - .setEnableAdditionalDataTypeWhenActionOverBle( - ENABLE_ADDITIONAL_DATA_TYPE_WHEN_ACTION_OVER_BLE) - .setCheckBondStateWhenSkipConnectingProfiles( - CHECK_BOND_STATE_WHEN_SKIP_CONNECTING_PROFILE) - .setMoreEventLogForQuality(MORE_LOG_FOR_QUALITY) - .setRetryGattConnectionAndSecretHandshake( - RETRY_GATT_CONNECTION_AND_SECRET_HANDSHAKE) - .setGattConnectShortTimeoutMs(GATT_CONNECT_SHORT_TIMEOUT_MS) - .setGattConnectLongTimeoutMs(GATT_CONNECTION_LONG_TIME_OUT_MS) - .setGattConnectShortTimeoutRetryMaxSpentTimeMs( - GATT_CONNECT_SHORT_TIMEOUT_RETRY_MAX_SPENT_TIME_MS) - .setAddressRotateRetryMaxSpentTimeMs(ADDRESS_ROTATE_RETRY_MAX_SPENT_TIME_MS) - .setPairingRetryDelayMs(PAIRING_RETRY_DELAY_MS) - .setSecretHandshakeShortTimeoutMs(HANDSHAKE_SHORT_TIMEOUT_MS) - .setSecretHandshakeLongTimeoutMs(HANDSHAKE_LONG_TIMEOUT_MS) - .setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs( - SECRET_HANDSHAKE_SHORT_TIMEOUT_RETRY_MAX_SPENT_TIME_MS) - .setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs( - SECRET_HANDSHAKE_LONG_TIMEOUT_RETRY_MAX_SPENT_TIME_MS) - .setSecretHandshakeRetryAttempts(SECRET_HANDSHAKE_RETRY_ATTEMPTS) - .setSecretHandshakeRetryGattConnectionMaxSpentTimeMs( - SECRET_HANDSHAKE_RETRY_GATT_CONNECTION_MAX_SPENT_TIME_MS) - .setSignalLostRetryMaxSpentTimeMs(SIGNAL_LOST_RETRY_MAX_SPENT_TIME_MS) - .setGattConnectionAndSecretHandshakeNoRetryGattError( - getGattConnectionAndSecretHandshakeNoRetryGattError()) - .setRetrySecretHandshakeTimeout(RETRY_SECRET_HANDSHAKE_TIMEOUT) - .setLogUserManualRetry(LOG_USER_MANUAL_RETRY) - .setEnablePairFlowShowUiWithoutProfileConnection( - ENABLE_PAIR_FLOW_SHOW_UI_WITHOUT_PROFILE_CONNECTION) - .setLogUserManualRetry(LOG_USER_MANUAL_CITY) - .setLogPairWithCachedModelId(LOG_PAIR_WITH_CACHED_MODEL_ID) - .setDirectConnectProfileIfModelIdInCache( - DIRECT_CONNECT_PROFILE_IF_MODEL_ID_IN_CACHE); - } - - private static ImmutableSet getGattConnectionAndSecretHandshakeNoRetryGattError() { - ImmutableSet.Builder noRetryGattErrorsBuilder = ImmutableSet.builder(); - // When GATT connection fail we will not retry on error code 257 - for (String errorCode : - Splitter.on(",").split("257,")) { - if (!TextUtils.isDigitsOnly(errorCode)) { - continue; - } - - try { - noRetryGattErrorsBuilder.add(Integer.parseInt(errorCode)); - } catch (NumberFormatException e) { - // Ignore - } - } - return noRetryGattErrorsBuilder.build(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java deleted file mode 100644 index 674633df4f7fef3a7cef68d01a295caedcb9e9ff..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair; - -import com.android.server.nearby.common.fastpair.service.UserActionHandlerBase; - -/** - * User action handler class. - */ -public class UserActionHandler extends UserActionHandlerBase { - - public static final String EXTRA_DISCOVERY_ITEM = PREFIX + "EXTRA_DISCOVERY_ITEM"; - public static final String EXTRA_FAST_PAIR_SECRET = PREFIX + "EXTRA_FAST_PAIR_SECRET"; - public static final String ACTION_FAST_PAIR = ACTION_PREFIX + "ACTION_FAST_PAIR"; - public static final String EXTRA_PRIVATE_BLE_ADDRESS = - ACTION_PREFIX + "EXTRA_PRIVATE_BLE_ADDRESS"; -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java deleted file mode 100644 index 6065f99fc5e6948f04a70567539747b97af1deb6..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.cache; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; -import static com.android.server.nearby.fastpair.UserActionHandler.EXTRA_FAST_PAIR_SECRET; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.text.TextUtils; -import android.util.Log; - -import com.android.server.nearby.common.ble.util.RangingUtils; -import com.android.server.nearby.common.fastpair.IconUtils; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.common.locator.LocatorContextWrapper; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.URISyntaxException; -import java.time.Clock; -import java.util.Objects; - -import service.proto.Cache; - -/** - * Wrapper class around StoredDiscoveryItem. A centralized place for methods related to - * updating/parsing StoredDiscoveryItem. - */ -public class DiscoveryItem implements Comparable { - - private static final String ACTION_FAST_PAIR = - "com.android.server.nearby:ACTION_FAST_PAIR"; - private static final int BEACON_STALENESS_MILLIS = 120000; - private static final int ITEM_EXPIRATION_MILLIS = 20000; - private static final int APP_INSTALL_EXPIRATION_MILLIS = 600000; - private static final int ITEM_DELETABLE_MILLIS = 15000; - - private final FastPairCacheManager mFastPairCacheManager; - private final Clock mClock; - - private Cache.StoredDiscoveryItem mStoredDiscoveryItem; - - /** IntDef for StoredDiscoveryItem.State */ - @IntDef({ - Cache.StoredDiscoveryItem.State.STATE_ENABLED_VALUE, - Cache.StoredDiscoveryItem.State.STATE_MUTED_VALUE, - Cache.StoredDiscoveryItem.State.STATE_DISABLED_BY_SYSTEM_VALUE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ItemState { - } - - public DiscoveryItem(LocatorContextWrapper locatorContextWrapper, - Cache.StoredDiscoveryItem mStoredDiscoveryItem) { - this.mFastPairCacheManager = - locatorContextWrapper.getLocator().get(FastPairCacheManager.class); - this.mClock = - locatorContextWrapper.getLocator().get(Clock.class); - this.mStoredDiscoveryItem = mStoredDiscoveryItem; - } - - public DiscoveryItem(Context context, Cache.StoredDiscoveryItem mStoredDiscoveryItem) { - this.mFastPairCacheManager = Locator.get(context, FastPairCacheManager.class); - this.mClock = Locator.get(context, Clock.class); - this.mStoredDiscoveryItem = mStoredDiscoveryItem; - } - - /** @return A new StoredDiscoveryItem with state fields set to their defaults. */ - public static Cache.StoredDiscoveryItem newStoredDiscoveryItem() { - Cache.StoredDiscoveryItem.Builder storedDiscoveryItem = - Cache.StoredDiscoveryItem.newBuilder(); - storedDiscoveryItem.setState(Cache.StoredDiscoveryItem.State.STATE_ENABLED); - return storedDiscoveryItem.build(); - } - - /** - * Checks if store discovery item support fast pair or not. - */ - public boolean isFastPair() { - Intent intent = parseIntentScheme(mStoredDiscoveryItem.getActionUrl()); - if (intent == null) { - Log.w("FastPairDiscovery", "FastPair: fail to parse action url" - + mStoredDiscoveryItem.getActionUrl()); - return false; - } - return ACTION_FAST_PAIR.equals(intent.getAction()); - } - - /** - * Sets the store discovery item mac address. - */ - public void setMacAddress(String address) { - mStoredDiscoveryItem = mStoredDiscoveryItem.toBuilder().setMacAddress(address).build(); - - mFastPairCacheManager.saveDiscoveryItem(this); - } - - /** - * Checks if the item is expired. Expired items are those over getItemExpirationMillis() eg. 2 - * minutes - */ - public static boolean isExpired( - long currentTimestampMillis, @Nullable Long lastObservationTimestampMillis) { - if (lastObservationTimestampMillis == null) { - return true; - } - return (currentTimestampMillis - lastObservationTimestampMillis) - >= ITEM_EXPIRATION_MILLIS; - } - - /** - * Checks if the item is deletable for saving disk space. Deletable items are those over - * getItemDeletableMillis eg. over 25 hrs. - */ - public static boolean isDeletable( - long currentTimestampMillis, @Nullable Long lastObservationTimestampMillis) { - if (lastObservationTimestampMillis == null) { - return true; - } - return currentTimestampMillis - lastObservationTimestampMillis - >= ITEM_DELETABLE_MILLIS; - } - - /** Checks if the item has a pending app install */ - public boolean isPendingAppInstallValid() { - return isPendingAppInstallValid(mClock.millis()); - } - - /** - * Checks if pending app valid. - */ - public boolean isPendingAppInstallValid(long appInstallMillis) { - return isPendingAppInstallValid(appInstallMillis, mStoredDiscoveryItem); - } - - /** - * Checks if the app install time expired. - */ - public static boolean isPendingAppInstallValid( - long currentMillis, Cache.StoredDiscoveryItem storedItem) { - return currentMillis - storedItem.getPendingAppInstallTimestampMillis() - < APP_INSTALL_EXPIRATION_MILLIS; - } - - - /** Checks if the item has enough data to be shown */ - public boolean isReadyForDisplay() { - boolean hasUrlOrPopularApp = !mStoredDiscoveryItem.getActionUrl().isEmpty(); - - return !TextUtils.isEmpty(mStoredDiscoveryItem.getTitle()) && hasUrlOrPopularApp; - } - - /** Checks if the action url is app install */ - public boolean isApp() { - return mStoredDiscoveryItem.getActionUrlType() == Cache.ResolvedUrlType.APP; - } - - /** Returns true if an item is muted, or if state is unavailable. */ - public boolean isMuted() { - return mStoredDiscoveryItem.getState() != Cache.StoredDiscoveryItem.State.STATE_ENABLED; - } - - /** - * Returns the state of store discovery item. - */ - public Cache.StoredDiscoveryItem.State getState() { - return mStoredDiscoveryItem.getState(); - } - - /** Checks if it's device item. e.g. Chromecast / Wear */ - public static boolean isDeviceType(Cache.NearbyType type) { - return type == Cache.NearbyType.NEARBY_CHROMECAST - || type == Cache.NearbyType.NEARBY_WEAR - || type == Cache.NearbyType.NEARBY_DEVICE; - } - - /** - * Check if the type is supported. - */ - public static boolean isTypeEnabled(Cache.NearbyType type) { - switch (type) { - case NEARBY_WEAR: - case NEARBY_CHROMECAST: - case NEARBY_DEVICE: - return true; - default: - Log.e("FastPairDiscoveryItem", "Invalid item type " + type.name()); - return false; - } - } - - /** Gets hash code of UI related data so we can collapse identical items. */ - public int getUiHashCode() { - return Objects.hash( - mStoredDiscoveryItem.getTitle(), - mStoredDiscoveryItem.getDescription(), - mStoredDiscoveryItem.getAppName(), - mStoredDiscoveryItem.getDisplayUrl(), - mStoredDiscoveryItem.getMacAddress()); - } - - // Getters below - - /** - * Returns the id of store discovery item. - */ - @Nullable - public String getId() { - return mStoredDiscoveryItem.getId(); - } - - /** - * Returns the title of discovery item. - */ - @Nullable - public String getTitle() { - return mStoredDiscoveryItem.getTitle(); - } - - /** - * Returns the description of discovery item. - */ - @Nullable - public String getDescription() { - return mStoredDiscoveryItem.getDescription(); - } - - /** - * Returns the mac address of discovery item. - */ - @Nullable - public String getMacAddress() { - return mStoredDiscoveryItem.getMacAddress(); - } - - /** - * Returns the display url of discovery item. - */ - @Nullable - public String getDisplayUrl() { - return mStoredDiscoveryItem.getDisplayUrl(); - } - - /** - * Returns the public key of discovery item. - */ - @Nullable - public byte[] getAuthenticationPublicKeySecp256R1() { - return mStoredDiscoveryItem.getAuthenticationPublicKeySecp256R1().toByteArray(); - } - - /** - * Returns the pairing secret. - */ - @Nullable - public String getFastPairSecretKey() { - Intent intent = parseIntentScheme(mStoredDiscoveryItem.getActionUrl()); - if (intent == null) { - Log.d("FastPairDiscoveryItem", "FastPair: fail to parse action url " - + mStoredDiscoveryItem.getActionUrl()); - return null; - } - return intent.getStringExtra(EXTRA_FAST_PAIR_SECRET); - } - - /** - * Returns the fast pair info of discovery item. - */ - @Nullable - public Cache.FastPairInformation getFastPairInformation() { - return mStoredDiscoveryItem.hasFastPairInformation() - ? mStoredDiscoveryItem.getFastPairInformation() : null; - } - - /** - * Returns the app name of discovery item. - */ - @Nullable - private String getAppName() { - return mStoredDiscoveryItem.getAppName(); - } - - /** - * Returns the package name of discovery item. - */ - @Nullable - public String getAppPackageName() { - return mStoredDiscoveryItem.getPackageName(); - } - - /** - * Returns the action url of discovery item. - */ - @Nullable - public String getActionUrl() { - return mStoredDiscoveryItem.getActionUrl(); - } - - /** - * Returns the rssi value of discovery item. - */ - @Nullable - public Integer getRssi() { - return mStoredDiscoveryItem.getRssi(); - } - - /** - * Returns the TX power of discovery item. - */ - @Nullable - public Integer getTxPower() { - return mStoredDiscoveryItem.getTxPower(); - } - - /** - * Returns the first observed time stamp of discovery item. - */ - @Nullable - public Long getFirstObservationTimestampMillis() { - return mStoredDiscoveryItem.getFirstObservationTimestampMillis(); - } - - /** - * Returns the last observed time stamp of discovery item. - */ - @Nullable - public Long getLastObservationTimestampMillis() { - return mStoredDiscoveryItem.getLastObservationTimestampMillis(); - } - - /** - * Calculates an estimated distance for the item, computed from the TX power (at 1m) and RSSI. - * - * @return estimated distance, or null if there is no RSSI or no TX power. - */ - @Nullable - public Double getEstimatedDistance() { - // In the future, we may want to do a foreground subscription to leverage onDistanceChanged. - return RangingUtils.distanceFromRssiAndTxPower(mStoredDiscoveryItem.getRssi(), - mStoredDiscoveryItem.getTxPower()); - } - - /** - * Gets icon Bitmap from icon store. - * - * @return null if no icon or icon size is incorrect. - */ - @Nullable - public Bitmap getIcon() { - Bitmap icon = - BitmapFactory.decodeByteArray( - mStoredDiscoveryItem.getIconPng().toByteArray(), - 0 /* offset */, mStoredDiscoveryItem.getIconPng().size()); - if (IconUtils.isIconSizeCorrect(icon)) { - return icon; - } else { - return null; - } - } - - /** Gets a FIFE URL of the icon. */ - @Nullable - public String getIconFifeUrl() { - return mStoredDiscoveryItem.getIconFifeUrl(); - } - - /** - * Compares this object to the specified object: 1. By device type. Device setups are 'greater - * than' beacons. 2. By relevance. More relevant items are 'greater than' less relevant items. - * 3.By distance. Nearer items are 'greater than' further items. - * - *

    In the list view, we sort in descending order, i.e. we put the most relevant items first. - */ - @Override - public int compareTo(DiscoveryItem another) { - // For items of the same relevance, compare distance. - Double distance1 = getEstimatedDistance(); - Double distance2 = another.getEstimatedDistance(); - distance1 = distance1 != null ? distance1 : Double.MAX_VALUE; - distance2 = distance2 != null ? distance2 : Double.MAX_VALUE; - // Negate because closer items are better ("greater than") further items. - return -distance1.compareTo(distance2); - } - - @Nullable - public String getTriggerId() { - return mStoredDiscoveryItem.getTriggerId(); - } - - @Override - public boolean equals(Object another) { - if (another instanceof DiscoveryItem) { - return ((DiscoveryItem) another).mStoredDiscoveryItem.equals(mStoredDiscoveryItem); - } - return false; - } - - @Override - public int hashCode() { - return mStoredDiscoveryItem.hashCode(); - } - - @Override - public String toString() { - return String.format( - "[triggerId=%s], [id=%s], [title=%s], [url=%s], [ready=%s], [macAddress=%s]", - getTriggerId(), - getId(), - getTitle(), - getActionUrl(), - isReadyForDisplay(), - maskBluetoothAddress(getMacAddress())); - } - - /** - * Gets a copy of the StoredDiscoveryItem proto backing this DiscoveryItem. Currently needed for - * Fast Pair 2.0: We store the item in the cloud associated with a user's account, to enable - * pairing with other devices owned by the user. - */ - public Cache.StoredDiscoveryItem getCopyOfStoredItem() { - return mStoredDiscoveryItem; - } - - /** - * Gets the StoredDiscoveryItem represented by this DiscoveryItem. This lets tests manipulate - * values that production code should not manipulate. - */ - - public Cache.StoredDiscoveryItem getStoredItemForTest() { - return mStoredDiscoveryItem; - } - - /** - * Sets the StoredDiscoveryItem represented by this DiscoveryItem. This lets tests manipulate - * values that production code should not manipulate. - */ - public void setStoredItemForTest(Cache.StoredDiscoveryItem s) { - mStoredDiscoveryItem = s; - } - - /** - * Parse the intent from item url. - */ - public static Intent parseIntentScheme(String uri) { - try { - return Intent.parseUri(uri, Intent.URI_INTENT_SCHEME); - } catch (URISyntaxException e) { - return null; - } - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItemContract.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItemContract.java deleted file mode 100644 index 61ca3fdd1e994b4dcd60e2c6ec8fc9f85785377b..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItemContract.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.cache; - -import android.provider.BaseColumns; - -/** - * Defines DiscoveryItem database schema. - */ -public class DiscoveryItemContract { - private DiscoveryItemContract() {} - - /** - * Discovery item entry related info. - */ - public static class DiscoveryItemEntry implements BaseColumns { - public static final String TABLE_NAME = "SCAN_RESULT"; - public static final String COLUMN_MODEL_ID = "MODEL_ID"; - public static final String COLUMN_SCAN_BYTE = "SCAN_RESULT_BYTE"; - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java deleted file mode 100644 index b8400913fc5fd080fb9af7109f043cd18ffde7cc..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.cache; - -import android.bluetooth.le.ScanResult; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import com.android.server.nearby.common.eventloop.Annotations; - -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.ArrayList; -import java.util.List; - -import service.proto.Cache; -import service.proto.Rpcs; - - -/** - * Save FastPair device info to database to avoid multiple requesting. - */ -public class FastPairCacheManager { - private final Context mContext; - private final FastPairDbHelper mFastPairDbHelper; - - public FastPairCacheManager(Context context) { - mContext = context; - mFastPairDbHelper = new FastPairDbHelper(context); - } - - /** - * Clean up function to release db - */ - public void cleanUp() { - mFastPairDbHelper.close(); - } - - /** - * Saves the response to the db - */ - private void saveDevice() { - } - - Cache.ServerResponseDbItem getDeviceFromScanResult(ScanResult scanResult) { - return Cache.ServerResponseDbItem.newBuilder().build(); - } - - /** - * Checks if the entry can be auto deleted from the cache - */ - public boolean isDeletable(Cache.ServerResponseDbItem entry) { - if (!entry.getExpirable()) { - return false; - } - return true; - } - - /** - * Save discovery item into database. Discovery item is item that discovered through Ble before - * pairing success. - */ - public boolean saveDiscoveryItem(DiscoveryItem item) { - - SQLiteDatabase db = mFastPairDbHelper.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(DiscoveryItemContract.DiscoveryItemEntry.COLUMN_MODEL_ID, item.getTriggerId()); - values.put(DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE, - item.getCopyOfStoredItem().toByteArray()); - db.insert(DiscoveryItemContract.DiscoveryItemEntry.TABLE_NAME, null, values); - return true; - } - - - @Annotations.EventThread - private Rpcs.GetObservedDeviceResponse getObservedDeviceInfo(ScanResult scanResult) { - return Rpcs.GetObservedDeviceResponse.getDefaultInstance(); - } - - /** - * Get discovery item from item id. - */ - public DiscoveryItem getDiscoveryItem(String itemId) { - return new DiscoveryItem(mContext, getStoredDiscoveryItem(itemId)); - } - - /** - * Get discovery item from item id. - */ - public Cache.StoredDiscoveryItem getStoredDiscoveryItem(String itemId) { - SQLiteDatabase db = mFastPairDbHelper.getReadableDatabase(); - String[] projection = { - DiscoveryItemContract.DiscoveryItemEntry.COLUMN_MODEL_ID, - DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE - }; - String selection = DiscoveryItemContract.DiscoveryItemEntry.COLUMN_MODEL_ID + " =? "; - String[] selectionArgs = {itemId}; - Cursor cursor = db.query( - DiscoveryItemContract.DiscoveryItemEntry.TABLE_NAME, - projection, - selection, - selectionArgs, - null, - null, - null - ); - - if (cursor.moveToNext()) { - byte[] res = cursor.getBlob(cursor.getColumnIndexOrThrow( - DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE)); - try { - Cache.StoredDiscoveryItem item = Cache.StoredDiscoveryItem.parseFrom(res); - return item; - } catch (InvalidProtocolBufferException e) { - Log.e("FastPairCacheManager", "storediscovery has error"); - } - } - cursor.close(); - return Cache.StoredDiscoveryItem.getDefaultInstance(); - } - - /** - * Get all of the discovery item related info in the cache. - */ - public List getAllSavedStoreDiscoveryItem() { - List storedDiscoveryItemList = new ArrayList<>(); - SQLiteDatabase db = mFastPairDbHelper.getReadableDatabase(); - String[] projection = { - DiscoveryItemContract.DiscoveryItemEntry.COLUMN_MODEL_ID, - DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE - }; - Cursor cursor = db.query( - DiscoveryItemContract.DiscoveryItemEntry.TABLE_NAME, - projection, - null, - null, - null, - null, - null - ); - - while (cursor.moveToNext()) { - byte[] res = cursor.getBlob(cursor.getColumnIndexOrThrow( - DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE)); - try { - Cache.StoredDiscoveryItem item = Cache.StoredDiscoveryItem.parseFrom(res); - storedDiscoveryItemList.add(item); - } catch (InvalidProtocolBufferException e) { - Log.e("FastPairCacheManager", "storediscovery has error"); - } - - } - cursor.close(); - return storedDiscoveryItemList; - } - - /** - * Get scan result from local database use model id - */ - public Cache.StoredScanResult getStoredScanResult(String modelId) { - return Cache.StoredScanResult.getDefaultInstance(); - } - - /** - * Gets the paired Fast Pair item that paired to the phone through mac address. - */ - public Cache.StoredFastPairItem getStoredFastPairItemFromMacAddress(String macAddress) { - SQLiteDatabase db = mFastPairDbHelper.getReadableDatabase(); - String[] projection = { - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_ACCOUNT_KEY, - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_MAC_ADDRESS, - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_STORED_FAST_PAIR_BYTE - }; - String selection = - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_MAC_ADDRESS + " =? "; - String[] selectionArgs = {macAddress}; - Cursor cursor = db.query( - StoredFastPairItemContract.StoredFastPairItemEntry.TABLE_NAME, - projection, - selection, - selectionArgs, - null, - null, - null - ); - - if (cursor.moveToNext()) { - byte[] res = cursor.getBlob(cursor.getColumnIndexOrThrow( - StoredFastPairItemContract.StoredFastPairItemEntry - .COLUMN_STORED_FAST_PAIR_BYTE)); - try { - Cache.StoredFastPairItem item = Cache.StoredFastPairItem.parseFrom(res); - return item; - } catch (InvalidProtocolBufferException e) { - Log.e("FastPairCacheManager", "storediscovery has error"); - } - } - cursor.close(); - return Cache.StoredFastPairItem.getDefaultInstance(); - } - - /** - * Save paired fast pair item into the database. - */ - public boolean putStoredFastPairItem(Cache.StoredFastPairItem storedFastPairItem) { - SQLiteDatabase db = mFastPairDbHelper.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_MAC_ADDRESS, - storedFastPairItem.getMacAddress()); - values.put(StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_ACCOUNT_KEY, - storedFastPairItem.getAccountKey().toString()); - values.put(StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_STORED_FAST_PAIR_BYTE, - storedFastPairItem.toByteArray()); - db.insert(StoredFastPairItemContract.StoredFastPairItemEntry.TABLE_NAME, null, values); - return true; - - } - - /** - * Removes certain storedFastPairItem so that it can update timely. - */ - public void removeStoredFastPairItem(String macAddress) { - SQLiteDatabase db = mFastPairDbHelper.getWritableDatabase(); - int res = db.delete(StoredFastPairItemContract.StoredFastPairItemEntry.TABLE_NAME, - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_MAC_ADDRESS + "=?", - new String[]{macAddress}); - - } - - /** - * Get all of the store fast pair item related info in the cache. - */ - public List getAllSavedStoredFastPairItem() { - List storedFastPairItemList = new ArrayList<>(); - SQLiteDatabase db = mFastPairDbHelper.getReadableDatabase(); - String[] projection = { - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_MAC_ADDRESS, - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_ACCOUNT_KEY, - StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_STORED_FAST_PAIR_BYTE - }; - Cursor cursor = db.query( - StoredFastPairItemContract.StoredFastPairItemEntry.TABLE_NAME, - projection, - null, - null, - null, - null, - null - ); - - while (cursor.moveToNext()) { - byte[] res = cursor.getBlob(cursor.getColumnIndexOrThrow(StoredFastPairItemContract - .StoredFastPairItemEntry.COLUMN_STORED_FAST_PAIR_BYTE)); - try { - Cache.StoredFastPairItem item = Cache.StoredFastPairItem.parseFrom(res); - storedFastPairItemList.add(item); - } catch (InvalidProtocolBufferException e) { - Log.e("FastPairCacheManager", "storediscovery has error"); - } - - } - cursor.close(); - return storedFastPairItemList; - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairDbHelper.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairDbHelper.java deleted file mode 100644 index d950d8d9a613df01c17855e8629f2f9ec5627c89..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairDbHelper.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.cache; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; - -/** - * Fast Pair db helper handle all of the db actions related Fast Pair. - */ -public class FastPairDbHelper extends SQLiteOpenHelper { - - public static final int DATABASE_VERSION = 1; - public static final String DATABASE_NAME = "FastPair.db"; - private static final String SQL_CREATE_DISCOVERY_ITEM_DB = - "CREATE TABLE IF NOT EXISTS " + DiscoveryItemContract.DiscoveryItemEntry.TABLE_NAME - + " (" + DiscoveryItemContract.DiscoveryItemEntry._ID - + "INTEGER PRIMARY KEY," - + DiscoveryItemContract.DiscoveryItemEntry.COLUMN_MODEL_ID - + " TEXT," + DiscoveryItemContract.DiscoveryItemEntry.COLUMN_SCAN_BYTE - + " BLOB)"; - private static final String SQL_DELETE_DISCOVERY_ITEM_DB = - "DROP TABLE IF EXISTS " + DiscoveryItemContract.DiscoveryItemEntry.TABLE_NAME; - private static final String SQL_CREATE_FAST_PAIR_ITEM_DB = - "CREATE TABLE IF NOT EXISTS " - + StoredFastPairItemContract.StoredFastPairItemEntry.TABLE_NAME - + " (" + StoredFastPairItemContract.StoredFastPairItemEntry._ID - + "INTEGER PRIMARY KEY," - + StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_MAC_ADDRESS - + " TEXT," - + StoredFastPairItemContract.StoredFastPairItemEntry.COLUMN_ACCOUNT_KEY - + " TEXT," - + StoredFastPairItemContract - .StoredFastPairItemEntry.COLUMN_STORED_FAST_PAIR_BYTE - + " BLOB)"; - private static final String SQL_DELETE_FAST_PAIR_ITEM_DB = - "DROP TABLE IF EXISTS " + StoredFastPairItemContract.StoredFastPairItemEntry.TABLE_NAME; - - public FastPairDbHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(SQL_CREATE_DISCOVERY_ITEM_DB); - db.execSQL(SQL_CREATE_FAST_PAIR_ITEM_DB); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // Since the outdated data has no value so just remove the data. - db.execSQL(SQL_DELETE_DISCOVERY_ITEM_DB); - db.execSQL(SQL_DELETE_FAST_PAIR_ITEM_DB); - onCreate(db); - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - super.onDowngrade(db, oldVersion, newVersion); - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/StoredFastPairItemContract.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/StoredFastPairItemContract.java deleted file mode 100644 index 99805654aec4243adae3d0dde9b1f29f414facac..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/cache/StoredFastPairItemContract.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.fastpair.cache; - -import android.provider.BaseColumns; - -/** - * Defines fast pair item database schema. - */ -public class StoredFastPairItemContract { - private StoredFastPairItemContract() {} - - /** - * StoredFastPairItem entry related info. - */ - public static class StoredFastPairItemEntry implements BaseColumns { - public static final String TABLE_NAME = "STORED_FAST_PAIR_ITEM"; - public static final String COLUMN_MAC_ADDRESS = "MAC_ADDRESS"; - public static final String COLUMN_ACCOUNT_KEY = "ACCOUNT_KEY"; - - public static final String COLUMN_STORED_FAST_PAIR_BYTE = "STORED_FAST_PAIR_BYTE"; - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/footprint/FastPairUploadInfo.java b/nearby/service/java/com/android/server/nearby/fastpair/footprint/FastPairUploadInfo.java deleted file mode 100644 index 6c9aff03460ee82b74e16ae974761ce24e1c228e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/footprint/FastPairUploadInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.fastpair.footprint; - - -import com.google.protobuf.ByteString; - -import service.proto.Cache; - -/** - * Wrapper class that upload the pair info to the footprint. - */ -public class FastPairUploadInfo { - - private Cache.StoredDiscoveryItem mStoredDiscoveryItem; - - private ByteString mAccountKey; - - private ByteString mSha256AccountKeyPublicAddress; - - - public FastPairUploadInfo(Cache.StoredDiscoveryItem storedDiscoveryItem, ByteString accountKey, - ByteString sha256AccountKeyPublicAddress) { - mStoredDiscoveryItem = storedDiscoveryItem; - mAccountKey = accountKey; - mSha256AccountKeyPublicAddress = sha256AccountKeyPublicAddress; - } - - public Cache.StoredDiscoveryItem getStoredDiscoveryItem() { - return mStoredDiscoveryItem; - } - - public ByteString getAccountKey() { - return mAccountKey; - } - - - public ByteString getSha256AccountKeyPublicAddress() { - return mSha256AccountKeyPublicAddress; - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/footprint/FootprintsDeviceManager.java b/nearby/service/java/com/android/server/nearby/fastpair/footprint/FootprintsDeviceManager.java deleted file mode 100644 index 68217c1edf5f45a868533a08d5a7bec90a1c5918..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/footprint/FootprintsDeviceManager.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.footprint; - -/** - * FootprintDeviceManager is responsible for all of the foot print operation. Footprint will - * store all of device info that already paired with certain account. This class will call AOSP - * api to let OEM save certain device. - */ -public class FootprintsDeviceManager { -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java deleted file mode 100644 index 553d5ce1805d6b47552d00a748ea4e4a3aa0aef3..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.halfsheet; - -import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE; -import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER; -import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE; -import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO; -import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_TYPE; -import static com.android.server.nearby.fastpair.FastPairManager.ACTION_RESOURCES_APK; - -import android.bluetooth.BluetoothDevice; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.nearby.FastPairDevice; -import android.nearby.FastPairStatusCallback; -import android.nearby.PairStatusMetadata; -import android.os.Bundle; -import android.os.UserHandle; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.nearby.common.locator.LocatorContextWrapper; -import com.android.server.nearby.fastpair.FastPairController; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.util.Environment; - -import java.util.List; -import java.util.stream.Collectors; - -import service.proto.Cache; - -/** - * Fast Pair ux manager for half sheet. - */ -public class FastPairHalfSheetManager { - private static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET"; - private static final String HALF_SHEET_CLASS_NAME = - "com.android.nearby.halfsheet.HalfSheetActivity"; - private static final String TAG = "FPHalfSheetManager"; - - private String mHalfSheetApkPkgName; - private final LocatorContextWrapper mLocatorContextWrapper; - - FastPairUiServiceImpl mFastPairUiService; - - public FastPairHalfSheetManager(Context context) { - this(new LocatorContextWrapper(context)); - } - - @VisibleForTesting - FastPairHalfSheetManager(LocatorContextWrapper locatorContextWrapper) { - mLocatorContextWrapper = locatorContextWrapper; - mFastPairUiService = new FastPairUiServiceImpl(); - } - - /** - * Invokes half sheet in the other apk. This function can only be called in Nearby because other - * app can't get the correct component name. - */ - public void showHalfSheet(Cache.ScanFastPairStoreItem scanFastPairStoreItem) { - try { - if (mLocatorContextWrapper != null) { - String packageName = getHalfSheetApkPkgName(); - if (packageName == null) { - Log.e(TAG, "package name is null"); - return; - } - mFastPairUiService.setFastPairController( - mLocatorContextWrapper.getLocator().get(FastPairController.class)); - Bundle bundle = new Bundle(); - bundle.putBinder(EXTRA_BINDER, mFastPairUiService); - mLocatorContextWrapper - .startActivityAsUser(new Intent(ACTIVITY_INTENT_ACTION) - .putExtra(EXTRA_HALF_SHEET_INFO, - scanFastPairStoreItem.toByteArray()) - .putExtra(EXTRA_HALF_SHEET_TYPE, - DEVICE_PAIRING_FRAGMENT_TYPE) - .putExtra(EXTRA_BUNDLE, bundle) - .setComponent(new ComponentName(packageName, - HALF_SHEET_CLASS_NAME)), - UserHandle.CURRENT); - } - } catch (IllegalStateException e) { - Log.e(TAG, "Can't resolve package that contains half sheet"); - } - } - - /** - * Shows pairing fail half sheet. - */ - public void showPairingFailed() { - FastPairStatusCallback pairStatusCallback = mFastPairUiService.getPairStatusCallback(); - if (pairStatusCallback != null) { - Log.v(TAG, "showPairingFailed: pairStatusCallback not NULL"); - pairStatusCallback.onPairUpdate(new FastPairDevice.Builder().build(), - new PairStatusMetadata(PairStatusMetadata.Status.FAIL)); - } else { - Log.w(TAG, "FastPairHalfSheetManager failed to show success half sheet because " - + "the pairStatusCallback is null"); - } - } - - /** - * Get the half sheet status whether it is foreground or dismissed - */ - public boolean getHalfSheetForegroundState() { - return true; - } - - /** - * Show passkey confirmation info on half sheet - */ - public void showPasskeyConfirmation(BluetoothDevice device, int passkey) { - } - - /** - * This function will handle pairing steps for half sheet. - */ - public void showPairingHalfSheet(DiscoveryItem item) { - Log.d(TAG, "show pairing half sheet"); - } - - /** - * Shows pairing success info. - */ - public void showPairingSuccessHalfSheet(String address) { - FastPairStatusCallback pairStatusCallback = mFastPairUiService.getPairStatusCallback(); - if (pairStatusCallback != null) { - pairStatusCallback.onPairUpdate( - new FastPairDevice.Builder().setBluetoothAddress(address).build(), - new PairStatusMetadata(PairStatusMetadata.Status.SUCCESS)); - } else { - Log.w(TAG, "FastPairHalfSheetManager failed to show success half sheet because " - + "the pairStatusCallback is null"); - } - } - - /** - * Removes dismiss runnable. - */ - public void disableDismissRunnable() { - } - - /** - * Destroys the bluetooth pairing controller. - */ - public void destroyBluetoothPairController() { - } - - /** - * Notify manager the pairing has finished. - */ - public void notifyPairingProcessDone(boolean success, String address, DiscoveryItem item) { - } - - /** - * Gets the package name of HalfSheet.apk - * getHalfSheetApkPkgName may invoke PackageManager multiple times and it does not have - * race condition check. Since there is no lock for mHalfSheetApkPkgName. - */ - String getHalfSheetApkPkgName() { - if (mHalfSheetApkPkgName != null) { - return mHalfSheetApkPkgName; - } - List resolveInfos = mLocatorContextWrapper - .getPackageManager().queryIntentActivities( - new Intent(ACTION_RESOURCES_APK), - PackageManager.MATCH_SYSTEM_ONLY); - - // remove apps that don't live in the nearby apex - resolveInfos.removeIf(info -> - !Environment.isAppInNearbyApex(info.activityInfo.applicationInfo)); - - if (resolveInfos.isEmpty()) { - // Resource APK not loaded yet, print a stack trace to see where this is called from - Log.e("FastPairManager", "Attempted to fetch resources before halfsheet " - + " APK is installed or package manager can't resolve correctly!", - new IllegalStateException()); - return null; - } - - if (resolveInfos.size() > 1) { - // multiple apps found, log a warning, but continue - Log.w("FastPairManager", "Found > 1 APK that can resolve halfsheet APK intent: " - + resolveInfos.stream() - .map(info -> info.activityInfo.applicationInfo.packageName) - .collect(Collectors.joining(", "))); - } - - // Assume the first ResolveInfo is the one we're looking for - ResolveInfo info = resolveInfos.get(0); - mHalfSheetApkPkgName = info.activityInfo.applicationInfo.packageName; - Log.i("FastPairManager", "Found halfsheet APK at: " + mHalfSheetApkPkgName); - return mHalfSheetApkPkgName; - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairUiServiceImpl.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairUiServiceImpl.java deleted file mode 100644 index 3bd273eb5000be8afcbdd13c78b68c34389d0389..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairUiServiceImpl.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.fastpair.halfsheet; - -import static com.android.server.nearby.fastpair.Constant.TAG; - -import android.nearby.FastPairDevice; -import android.nearby.FastPairStatusCallback; -import android.nearby.PairStatusMetadata; -import android.nearby.aidl.IFastPairStatusCallback; -import android.nearby.aidl.IFastPairUiService; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.server.nearby.fastpair.FastPairController; - -/** - * Service implementing Fast Pair functionality. - * - * @hide - */ -public class FastPairUiServiceImpl extends IFastPairUiService.Stub { - - private IBinder mStatusCallbackProxy; - private FastPairController mFastPairController; - private FastPairStatusCallback mFastPairStatusCallback; - - /** - * Registers the Binder call back in the server notifies the proxy when there is an update - * in the server. - */ - @Override - public void registerCallback(IFastPairStatusCallback iFastPairStatusCallback) { - mStatusCallbackProxy = iFastPairStatusCallback.asBinder(); - mFastPairStatusCallback = new FastPairStatusCallback() { - @Override - public void onPairUpdate(FastPairDevice fastPairDevice, - PairStatusMetadata pairStatusMetadata) { - try { - iFastPairStatusCallback.onPairUpdate(fastPairDevice, pairStatusMetadata); - } catch (RemoteException e) { - Log.w(TAG, "Failed to update pair status.", e); - } - } - }; - } - - /** - * Unregisters the Binder call back in the server. - */ - @Override - public void unregisterCallback(IFastPairStatusCallback iFastPairStatusCallback) { - mStatusCallbackProxy = null; - mFastPairStatusCallback = null; - } - - /** - * Asks the Fast Pair service to pair the device. initial pairing. - */ - @Override - public void connect(FastPairDevice fastPairDevice) { - if (mFastPairController != null) { - mFastPairController.pair(fastPairDevice); - } else { - Log.w(TAG, "Failed to connect because there is no FastPairController."); - } - } - - /** - * Cancels Fast Pair connection and dismisses half sheet. - */ - @Override - public void cancel(FastPairDevice fastPairDevice) { - } - - public FastPairStatusCallback getPairStatusCallback() { - return mFastPairStatusCallback; - } - - /** - * Sets function for Fast Pair controller. - */ - public void setFastPairController(FastPairController fastPairController) { - mFastPairController = fastPairController; - } -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/notification/FastPairNotificationManager.java b/nearby/service/java/com/android/server/nearby/fastpair/notification/FastPairNotificationManager.java deleted file mode 100644 index b1ae573bd3846a83c6b5ddf4ec0b7db3c891312b..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/notification/FastPairNotificationManager.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.notification; - - -import android.annotation.Nullable; -import android.content.Context; - -import com.android.server.nearby.fastpair.cache.DiscoveryItem; - -/** - * Responsible for show notification logic. - */ -public class FastPairNotificationManager { - - /** - * FastPair notification manager that handle notification ui for fast pair. - */ - public FastPairNotificationManager(Context context, DiscoveryItem item, boolean useLargeIcon, - int notificationId) { - } - /** - * FastPair notification manager that handle notification ui for fast pair. - */ - public FastPairNotificationManager(Context context, DiscoveryItem item, boolean useLargeIcon) { - - } - - /** - * Shows pairing in progress notification. - */ - public void showConnectingNotification() {} - - /** - * Shows success notification - */ - public void showPairingSucceededNotification( - @Nullable String companionApp, - int batteryLevel, - @Nullable String deviceName, - String address) { - - } - - /** - * Shows failed notification. - */ - public void showPairingFailedNotification(byte[] accountKey) { - - } - - /** - * Notify the pairing process is done. - */ - public void notifyPairingProcessDone(boolean success, boolean forceNotify, - String privateAddress, String publicAddress) {} -} diff --git a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/HalfSheetPairingProgressHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/HalfSheetPairingProgressHandler.java deleted file mode 100644 index c95f74f4e86b4b4421765438a5b6327de54ea3af..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/HalfSheetPairingProgressHandler.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.pairinghandler; - - -import android.annotation.Nullable; -import android.bluetooth.BluetoothDevice; -import android.content.Context; - -import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection; -import com.android.server.nearby.common.locator.Locator; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager; -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager; -import com.android.server.nearby.intdefs.NearbyEventIntDefs; - -/** Pairing progress handler that handle pairing come from half sheet. */ -public final class HalfSheetPairingProgressHandler extends PairingProgressHandlerBase { - - private final FastPairHalfSheetManager mFastPairHalfSheetManager; - private final boolean mIsSubsequentPair; - private final DiscoveryItem mItemResurface; - - HalfSheetPairingProgressHandler( - Context context, - DiscoveryItem item, - @Nullable String companionApp, - @Nullable byte[] accountKey) { - super(context, item); - this.mFastPairHalfSheetManager = Locator.get(context, FastPairHalfSheetManager.class); - this.mIsSubsequentPair = - item.getAuthenticationPublicKeySecp256R1() != null && accountKey != null; - this.mItemResurface = item; - } - - @Override - protected int getPairStartEventCode() { - return mIsSubsequentPair ? NearbyEventIntDefs.EventCode.SUBSEQUENT_PAIR_START - : NearbyEventIntDefs.EventCode.MAGIC_PAIR_START; - } - - @Override - protected int getPairEndEventCode() { - return mIsSubsequentPair ? NearbyEventIntDefs.EventCode.SUBSEQUENT_PAIR_END - : NearbyEventIntDefs.EventCode.MAGIC_PAIR_END; - } - - @Override - public void onPairingStarted() { - super.onPairingStarted(); - // Half sheet is not in the foreground reshow half sheet, also avoid showing HalfSheet on TV - if (!mFastPairHalfSheetManager.getHalfSheetForegroundState()) { - mFastPairHalfSheetManager.showPairingHalfSheet(mItemResurface); - } - mFastPairHalfSheetManager.disableDismissRunnable(); - } - - @Override - public void onHandlePasskeyConfirmation(BluetoothDevice device, int passkey) { - super.onHandlePasskeyConfirmation(device, passkey); - mFastPairHalfSheetManager.showPasskeyConfirmation(device, passkey); - } - - @Nullable - @Override - public String onPairedCallbackCalled( - FastPairConnection connection, - byte[] accountKey, - FootprintsDeviceManager footprints, - String address) { - String deviceName = super.onPairedCallbackCalled(connection, accountKey, - footprints, address); - mFastPairHalfSheetManager.showPairingSuccessHalfSheet(address); - mFastPairHalfSheetManager.disableDismissRunnable(); - return deviceName; - } - - @Override - public void onPairingFailed(Throwable throwable) { - super.onPairingFailed(throwable); - mFastPairHalfSheetManager.disableDismissRunnable(); - mFastPairHalfSheetManager.showPairingFailed(); - mFastPairHalfSheetManager.notifyPairingProcessDone( - /* success= */ false, /* publicAddress= */ null, mItem); - // fix auto rebond issue - mFastPairHalfSheetManager.destroyBluetoothPairController(); - } - - @Override - public void onPairingSuccess(String address) { - super.onPairingSuccess(address); - mFastPairHalfSheetManager.disableDismissRunnable(); - mFastPairHalfSheetManager - .notifyPairingProcessDone(/* success= */ true, address, mItem); - mFastPairHalfSheetManager.destroyBluetoothPairController(); - } -} - diff --git a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/NotificationPairingProgressHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/NotificationPairingProgressHandler.java deleted file mode 100644 index d469c4572f141a1b753fef2117c72f071c21585c..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/NotificationPairingProgressHandler.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.pairinghandler; - -import android.annotation.Nullable; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; - -import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager; -import com.android.server.nearby.fastpair.notification.FastPairNotificationManager; -import com.android.server.nearby.intdefs.NearbyEventIntDefs; - -/** Pairing progress handler for pairing coming from notifications. */ -@SuppressWarnings("nullness") -public class NotificationPairingProgressHandler extends PairingProgressHandlerBase { - private final FastPairNotificationManager mFastPairNotificationManager; - @Nullable - private final String mCompanionApp; - @Nullable - private final byte[] mAccountKey; - private final boolean mIsSubsequentPair; - - NotificationPairingProgressHandler( - Context context, - DiscoveryItem item, - @Nullable String companionApp, - @Nullable byte[] accountKey, - FastPairNotificationManager mFastPairNotificationManager) { - super(context, item); - this.mFastPairNotificationManager = mFastPairNotificationManager; - this.mCompanionApp = companionApp; - this.mAccountKey = accountKey; - this.mIsSubsequentPair = - item.getAuthenticationPublicKeySecp256R1() != null && accountKey != null; - } - - @Override - public int getPairStartEventCode() { - return mIsSubsequentPair ? NearbyEventIntDefs.EventCode.SUBSEQUENT_PAIR_START - : NearbyEventIntDefs.EventCode.MAGIC_PAIR_START; - } - - @Override - public int getPairEndEventCode() { - return mIsSubsequentPair ? NearbyEventIntDefs.EventCode.SUBSEQUENT_PAIR_END - : NearbyEventIntDefs.EventCode.MAGIC_PAIR_END; - } - - @Override - public void onReadyToPair() { - super.onReadyToPair(); - mFastPairNotificationManager.showConnectingNotification(); - } - - @Override - public String onPairedCallbackCalled( - FastPairConnection connection, - byte[] accountKey, - FootprintsDeviceManager footprints, - String address) { - String deviceName = super.onPairedCallbackCalled(connection, accountKey, footprints, - address); - - int batteryLevel = -1; - - BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class); - BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); - if (bluetoothAdapter != null) { - // Need to check battery level here set that to -1 for now - batteryLevel = -1; - } else { - Log.v( - "NotificationPairingProgressHandler", - "onPairedCallbackCalled getBatteryLevel failed," - + " adapter is null"); - } - mFastPairNotificationManager.showPairingSucceededNotification( - !TextUtils.isEmpty(mCompanionApp) ? mCompanionApp : null, - batteryLevel, - deviceName, - address); - return deviceName; - } - - @Override - public void onPairingFailed(Throwable throwable) { - super.onPairingFailed(throwable); - mFastPairNotificationManager.showPairingFailedNotification(mAccountKey); - mFastPairNotificationManager.notifyPairingProcessDone( - /* success= */ false, - /* forceNotify= */ false, - /* privateAddress= */ mItem.getMacAddress(), - /* publicAddress= */ null); - } - - @Override - public void onPairingSuccess(String address) { - super.onPairingSuccess(address); - mFastPairNotificationManager.notifyPairingProcessDone( - /* success= */ true, - /* forceNotify= */ false, - /* privateAddress= */ mItem.getMacAddress(), - /* publicAddress= */ address); - } -} - diff --git a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java deleted file mode 100644 index ccd7e5e610e00de1ae086c41287baf53d1a3237f..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.fastpair.pairinghandler; - -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothAddress.maskBluetoothAddress; -import static com.android.server.nearby.fastpair.FastPairManager.isThroughFastPair2InitialPairing; - -import android.annotation.Nullable; -import android.bluetooth.BluetoothDevice; -import android.content.Context; -import android.util.Log; - -import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection; -import com.android.server.nearby.common.bluetooth.fastpair.Preferences; -import com.android.server.nearby.fastpair.cache.DiscoveryItem; -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager; -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager; -import com.android.server.nearby.fastpair.notification.FastPairNotificationManager; -import com.android.server.nearby.intdefs.FastPairEventIntDefs; - -/** Base class for pairing progress handler. */ -public abstract class PairingProgressHandlerBase { - protected final Context mContext; - protected final DiscoveryItem mItem; - @Nullable - private FastPairEventIntDefs.ErrorCode mRescueFromError; - - protected abstract int getPairStartEventCode(); - - protected abstract int getPairEndEventCode(); - - protected PairingProgressHandlerBase(Context context, DiscoveryItem item) { - this.mContext = context; - this.mItem = item; - } - - - /** - * Pairing progress init function. - */ - public static PairingProgressHandlerBase create( - Context context, - DiscoveryItem item, - @Nullable String companionApp, - @Nullable byte[] accountKey, - FootprintsDeviceManager footprints, - FastPairNotificationManager notificationManager, - FastPairHalfSheetManager fastPairHalfSheetManager, - boolean isRetroactivePair) { - PairingProgressHandlerBase pairingProgressHandlerBase; - // Disable half sheet on subsequent pairing - if (item.getAuthenticationPublicKeySecp256R1() != null - && accountKey != null) { - // Subsequent pairing - pairingProgressHandlerBase = - new NotificationPairingProgressHandler( - context, item, companionApp, accountKey, notificationManager); - } else { - pairingProgressHandlerBase = - new HalfSheetPairingProgressHandler(context, item, companionApp, accountKey); - } - - - Log.v("PairingHandler", - "PairingProgressHandler:Create " - + item.getMacAddress() + " for pairing"); - return pairingProgressHandlerBase; - } - - - /** - * Function calls when pairing start. - */ - public void onPairingStarted() { - Log.v("PairingHandler", "PairingProgressHandler:onPairingStarted"); - } - - /** - * Waits for screen to unlock. - */ - public void onWaitForScreenUnlock() { - Log.v("PairingHandler", "PairingProgressHandler:onWaitForScreenUnlock"); - } - - /** - * Function calls when screen unlock. - */ - public void onScreenUnlocked() { - Log.v("PairingHandler", "PairingProgressHandler:onScreenUnlocked"); - } - - /** - * Calls when the handler is ready to pair. - */ - public void onReadyToPair() { - Log.v("PairingHandler", "PairingProgressHandler:onReadyToPair"); - } - - /** - * Helps to set up pairing preference. - */ - public void onSetupPreferencesBuilder(Preferences.Builder builder) { - Log.v("PairingHandler", "PairingProgressHandler:onSetupPreferencesBuilder"); - } - - /** - * Calls when pairing setup complete. - */ - public void onPairingSetupCompleted() { - Log.v("PairingHandler", "PairingProgressHandler:onPairingSetupCompleted"); - } - - /** Called while pairing if needs to handle the passkey confirmation by Ui. */ - public void onHandlePasskeyConfirmation(BluetoothDevice device, int passkey) { - Log.v("PairingHandler", "PairingProgressHandler:onHandlePasskeyConfirmation"); - } - - /** - * In this callback, we know if it is a real initial pairing by existing account key, and do - * following things: - *

  • 1, optIn footprint for initial pairing. - *
  • 2, write the device name to provider - *
  • 2.1, generate default personalized name for initial pairing or get the personalized name - * from footprint for subsequent pairing. - *
  • 2.2, set alias name for the bluetooth device. - *
  • 2.3, update the device name for connection to write into provider for initial pair. - *
  • 3, suppress battery notifications until oobe finishes. - * - * @return display name of the pairing device - */ - @Nullable - public String onPairedCallbackCalled( - FastPairConnection connection, - byte[] accountKey, - FootprintsDeviceManager footprints, - String address) { - Log.v("PairingHandler", - "PairingProgressHandler:onPairedCallbackCalled with address: " - + address); - - byte[] existingAccountKey = connection.getExistingAccountKey(); - optInFootprintsForInitialPairing(footprints, mItem, accountKey, existingAccountKey); - // Add support for naming the device - return null; - } - - /** - * Gets the related info from db use account key. - */ - @Nullable - public byte[] getKeyForLocalCache( - byte[] accountKey, FastPairConnection connection, - FastPairConnection.SharedSecret sharedSecret) { - Log.v("PairingHandler", "PairingProgressHandler:getKeyForLocalCache"); - return accountKey != null ? accountKey : connection.getExistingAccountKey(); - } - - /** - * Function handles pairing fail. - */ - public void onPairingFailed(Throwable throwable) { - Log.w("PairingHandler", "PairingProgressHandler:onPairingFailed"); - } - - /** - * Function handles pairing success. - */ - public void onPairingSuccess(String address) { - Log.v("PairingHandler", "PairingProgressHandler:onPairingSuccess with address: " - + maskBluetoothAddress(address)); - } - - private static void optInFootprintsForInitialPairing( - FootprintsDeviceManager footprints, - DiscoveryItem item, - byte[] accountKey, - @Nullable byte[] existingAccountKey) { - if (isThroughFastPair2InitialPairing(item, accountKey) && existingAccountKey == null) { - // enable the save to footprint - Log.v("PairingHandler", "footprint should call opt in here"); - } - } - - /** - * Returns {@code true} if the PairingProgressHandler is running at the background. - * - *

    In order to keep the following status notification shows as a heads up, we must wait for - * the screen unlocked to continue. - */ - public boolean skipWaitingScreenUnlock() { - return false; - } -} - diff --git a/nearby/service/java/com/android/server/nearby/intdefs/FastPairEventIntDefs.java b/nearby/service/java/com/android/server/nearby/intdefs/FastPairEventIntDefs.java deleted file mode 100644 index 8bb7980dd20546646b95a79892525d3cf33c21fe..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/intdefs/FastPairEventIntDefs.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.intdefs; - -import androidx.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Holds integer definitions for FastPair. */ -public class FastPairEventIntDefs { - - /** Fast Pair Bond State. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - BondState.UNKNOWN_BOND_STATE, - BondState.NONE, - BondState.BONDING, - BondState.BONDED, - }) - public @interface BondState { - int UNKNOWN_BOND_STATE = 0; - int NONE = 10; - int BONDING = 11; - int BONDED = 12; - } - - /** Fast Pair error code. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - ErrorCode.UNKNOWN_ERROR_CODE, - ErrorCode.OTHER_ERROR, - ErrorCode.TIMEOUT, - ErrorCode.INTERRUPTED, - ErrorCode.REFLECTIVE_OPERATION_EXCEPTION, - ErrorCode.EXECUTION_EXCEPTION, - ErrorCode.PARSE_EXCEPTION, - ErrorCode.MDH_REMOTE_EXCEPTION, - ErrorCode.SUCCESS_RETRY_GATT_ERROR, - ErrorCode.SUCCESS_RETRY_GATT_TIMEOUT, - ErrorCode.SUCCESS_RETRY_SECRET_HANDSHAKE_ERROR, - ErrorCode.SUCCESS_RETRY_SECRET_HANDSHAKE_TIMEOUT, - ErrorCode.SUCCESS_SECRET_HANDSHAKE_RECONNECT, - ErrorCode.SUCCESS_ADDRESS_ROTATE, - ErrorCode.SUCCESS_SIGNAL_LOST, - }) - public @interface ErrorCode { - int UNKNOWN_ERROR_CODE = 0; - - // Check the other fields for a more specific error code. - int OTHER_ERROR = 1; - - // The operation timed out. - int TIMEOUT = 2; - - // The thread was interrupted. - int INTERRUPTED = 3; - - // Some reflective call failed (should never happen). - int REFLECTIVE_OPERATION_EXCEPTION = 4; - - // A Future threw an exception (should never happen). - int EXECUTION_EXCEPTION = 5; - - // Parsing something (e.g. BR/EDR Handover data) failed. - int PARSE_EXCEPTION = 6; - - // A failure at MDH. - int MDH_REMOTE_EXCEPTION = 7; - - // For errors on GATT connection and retry success - int SUCCESS_RETRY_GATT_ERROR = 8; - - // For timeout on GATT connection and retry success - int SUCCESS_RETRY_GATT_TIMEOUT = 9; - - // For errors on secret handshake and retry success - int SUCCESS_RETRY_SECRET_HANDSHAKE_ERROR = 10; - - // For timeout on secret handshake and retry success - int SUCCESS_RETRY_SECRET_HANDSHAKE_TIMEOUT = 11; - - // For secret handshake fail and restart GATT connection success - int SUCCESS_SECRET_HANDSHAKE_RECONNECT = 12; - - // For address rotate and retry with new address success - int SUCCESS_ADDRESS_ROTATE = 13; - - // For signal lost and retry with old address still success - int SUCCESS_SIGNAL_LOST = 14; - } - - /** Fast Pair BrEdrHandover Error Code. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - BrEdrHandoverErrorCode.UNKNOWN_BR_EDR_HANDOVER_ERROR_CODE, - BrEdrHandoverErrorCode.CONTROL_POINT_RESULT_CODE_NOT_SUCCESS, - BrEdrHandoverErrorCode.BLUETOOTH_MAC_INVALID, - BrEdrHandoverErrorCode.TRANSPORT_BLOCK_INVALID, - }) - public @interface BrEdrHandoverErrorCode { - int UNKNOWN_BR_EDR_HANDOVER_ERROR_CODE = 0; - int CONTROL_POINT_RESULT_CODE_NOT_SUCCESS = 1; - int BLUETOOTH_MAC_INVALID = 2; - int TRANSPORT_BLOCK_INVALID = 3; - } - - /** Fast Pair CreateBound Error Code. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - CreateBondErrorCode.UNKNOWN_BOND_ERROR_CODE, - CreateBondErrorCode.BOND_BROKEN, - CreateBondErrorCode.POSSIBLE_MITM, - CreateBondErrorCode.NO_PERMISSION, - CreateBondErrorCode.INCORRECT_VARIANT, - CreateBondErrorCode.FAILED_BUT_ALREADY_RECEIVE_PASS_KEY, - }) - public @interface CreateBondErrorCode { - int UNKNOWN_BOND_ERROR_CODE = 0; - int BOND_BROKEN = 1; - int POSSIBLE_MITM = 2; - int NO_PERMISSION = 3; - int INCORRECT_VARIANT = 4; - int FAILED_BUT_ALREADY_RECEIVE_PASS_KEY = 5; - } - - /** Fast Pair Connect Error Code. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - ConnectErrorCode.UNKNOWN_CONNECT_ERROR_CODE, - ConnectErrorCode.UNSUPPORTED_PROFILE, - ConnectErrorCode.GET_PROFILE_PROXY_FAILED, - ConnectErrorCode.DISCONNECTED, - ConnectErrorCode.LINK_KEY_CLEARED, - ConnectErrorCode.FAIL_TO_DISCOVERY, - ConnectErrorCode.DISCOVERY_NOT_FINISHED, - }) - public @interface ConnectErrorCode { - int UNKNOWN_CONNECT_ERROR_CODE = 0; - int UNSUPPORTED_PROFILE = 1; - int GET_PROFILE_PROXY_FAILED = 2; - int DISCONNECTED = 3; - int LINK_KEY_CLEARED = 4; - int FAIL_TO_DISCOVERY = 5; - int DISCOVERY_NOT_FINISHED = 6; - } - - private FastPairEventIntDefs() {} -} diff --git a/nearby/service/java/com/android/server/nearby/intdefs/NearbyEventIntDefs.java b/nearby/service/java/com/android/server/nearby/intdefs/NearbyEventIntDefs.java deleted file mode 100644 index 91bf49a6a9f4f72f88f3c19e57c829423e785ac1..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/intdefs/NearbyEventIntDefs.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2021 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 com.android.server.nearby.intdefs; - -import androidx.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Holds integer definitions for NearbyEvent. */ -public class NearbyEventIntDefs { - - /** NearbyEvent Code. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - value = { - EventCode.UNKNOWN_EVENT_TYPE, - EventCode.MAGIC_PAIR_START, - EventCode.WAIT_FOR_SCREEN_UNLOCK, - EventCode.GATT_CONNECT, - EventCode.BR_EDR_HANDOVER_WRITE_CONTROL_POINT_REQUEST, - EventCode.BR_EDR_HANDOVER_READ_BLUETOOTH_MAC, - EventCode.BR_EDR_HANDOVER_READ_TRANSPORT_BLOCK, - EventCode.GET_PROFILES_VIA_SDP, - EventCode.DISCOVER_DEVICE, - EventCode.CANCEL_DISCOVERY, - EventCode.REMOVE_BOND, - EventCode.CANCEL_BOND, - EventCode.CREATE_BOND, - EventCode.CONNECT_PROFILE, - EventCode.DISABLE_BLUETOOTH, - EventCode.ENABLE_BLUETOOTH, - EventCode.MAGIC_PAIR_END, - EventCode.SECRET_HANDSHAKE, - EventCode.WRITE_ACCOUNT_KEY, - EventCode.WRITE_TO_FOOTPRINTS, - EventCode.PASSKEY_EXCHANGE, - EventCode.DEVICE_RECOGNIZED, - EventCode.GET_LOCAL_PUBLIC_ADDRESS, - EventCode.DIRECTLY_CONNECTED_TO_PROFILE, - EventCode.DEVICE_ALIAS_CHANGED, - EventCode.WRITE_DEVICE_NAME, - EventCode.UPDATE_PROVIDER_NAME_START, - EventCode.UPDATE_PROVIDER_NAME_END, - EventCode.READ_FIRMWARE_VERSION, - EventCode.RETROACTIVE_PAIR_START, - EventCode.RETROACTIVE_PAIR_END, - EventCode.SUBSEQUENT_PAIR_START, - EventCode.SUBSEQUENT_PAIR_END, - EventCode.BISTO_PAIR_START, - EventCode.BISTO_PAIR_END, - EventCode.REMOTE_PAIR_START, - EventCode.REMOTE_PAIR_END, - EventCode.BEFORE_CREATE_BOND, - EventCode.BEFORE_CREATE_BOND_BONDING, - EventCode.BEFORE_CREATE_BOND_BONDED, - EventCode.BEFORE_CONNECT_PROFILE, - EventCode.HANDLE_PAIRING_REQUEST, - EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION, - EventCode.GATT_CONNECTION_AND_SECRET_HANDSHAKE, - EventCode.CHECK_SIGNAL_AFTER_HANDSHAKE, - EventCode.RECOVER_BY_RETRY_GATT, - EventCode.RECOVER_BY_RETRY_HANDSHAKE, - EventCode.RECOVER_BY_RETRY_HANDSHAKE_RECONNECT, - EventCode.GATT_HANDSHAKE_MANUAL_RETRY_ATTEMPTS, - EventCode.PAIR_WITH_CACHED_MODEL_ID, - EventCode.DIRECTLY_CONNECT_PROFILE_WITH_CACHED_ADDRESS, - EventCode.PAIR_WITH_NEW_MODEL, - }) - public @interface EventCode { - int UNKNOWN_EVENT_TYPE = 0; - - // Codes for Magic Pair. - // Starting at 1000 to not conflict with other existing codes (e.g. - // DiscoveryEvent) that may be migrated to become official Event Codes. - int MAGIC_PAIR_START = 1010; - int WAIT_FOR_SCREEN_UNLOCK = 1020; - int GATT_CONNECT = 1030; - int BR_EDR_HANDOVER_WRITE_CONTROL_POINT_REQUEST = 1040; - int BR_EDR_HANDOVER_READ_BLUETOOTH_MAC = 1050; - int BR_EDR_HANDOVER_READ_TRANSPORT_BLOCK = 1060; - int GET_PROFILES_VIA_SDP = 1070; - int DISCOVER_DEVICE = 1080; - int CANCEL_DISCOVERY = 1090; - int REMOVE_BOND = 1100; - int CANCEL_BOND = 1110; - int CREATE_BOND = 1120; - int CONNECT_PROFILE = 1130; - int DISABLE_BLUETOOTH = 1140; - int ENABLE_BLUETOOTH = 1150; - int MAGIC_PAIR_END = 1160; - int SECRET_HANDSHAKE = 1170; - int WRITE_ACCOUNT_KEY = 1180; - int WRITE_TO_FOOTPRINTS = 1190; - int PASSKEY_EXCHANGE = 1200; - int DEVICE_RECOGNIZED = 1210; - int GET_LOCAL_PUBLIC_ADDRESS = 1220; - int DIRECTLY_CONNECTED_TO_PROFILE = 1230; - int DEVICE_ALIAS_CHANGED = 1240; - int WRITE_DEVICE_NAME = 1250; - int UPDATE_PROVIDER_NAME_START = 1260; - int UPDATE_PROVIDER_NAME_END = 1270; - int READ_FIRMWARE_VERSION = 1280; - int RETROACTIVE_PAIR_START = 1290; - int RETROACTIVE_PAIR_END = 1300; - int SUBSEQUENT_PAIR_START = 1310; - int SUBSEQUENT_PAIR_END = 1320; - int BISTO_PAIR_START = 1330; - int BISTO_PAIR_END = 1340; - int REMOTE_PAIR_START = 1350; - int REMOTE_PAIR_END = 1360; - int BEFORE_CREATE_BOND = 1370; - int BEFORE_CREATE_BOND_BONDING = 1380; - int BEFORE_CREATE_BOND_BONDED = 1390; - int BEFORE_CONNECT_PROFILE = 1400; - int HANDLE_PAIRING_REQUEST = 1410; - int SECRET_HANDSHAKE_GATT_COMMUNICATION = 1420; - int GATT_CONNECTION_AND_SECRET_HANDSHAKE = 1430; - int CHECK_SIGNAL_AFTER_HANDSHAKE = 1440; - int RECOVER_BY_RETRY_GATT = 1450; - int RECOVER_BY_RETRY_HANDSHAKE = 1460; - int RECOVER_BY_RETRY_HANDSHAKE_RECONNECT = 1470; - int GATT_HANDSHAKE_MANUAL_RETRY_ATTEMPTS = 1480; - int PAIR_WITH_CACHED_MODEL_ID = 1490; - int DIRECTLY_CONNECT_PROFILE_WITH_CACHED_ADDRESS = 1500; - int PAIR_WITH_NEW_MODEL = 1510; - } - - private NearbyEventIntDefs() {} -} diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceConstants.java b/nearby/service/java/com/android/server/nearby/presence/PresenceConstants.java index c355df2a063fb9e17c9ad755375cc07816878f14..39bc60b136c13e7b7a699364bdecd30853ab25b8 100644 --- a/nearby/service/java/com/android/server/nearby/presence/PresenceConstants.java +++ b/nearby/service/java/com/android/server/nearby/presence/PresenceConstants.java @@ -16,9 +16,7 @@ package com.android.server.nearby.presence; -import static com.android.server.nearby.common.bluetooth.fastpair.BluetoothUuids.to128BitUuid; - -import java.util.UUID; +import android.os.ParcelUuid; /** * Constants for Nearby Presence operations. @@ -26,5 +24,6 @@ import java.util.UUID; public class PresenceConstants { /** Presence advertisement service data uuid. */ - public static final UUID PRESENCE_UUID = to128BitUuid((short) 0xFCF1); + public static final ParcelUuid PRESENCE_UUID = + ParcelUuid.fromString("0000fcf1-0000-1000-8000-00805f9b34fb"); } diff --git a/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java index e8aea7955b8f6dff928a4707015a67708e6f4e92..e2fbe7764d19d9684b0452c8c4ddc9f18eb46a38 100644 --- a/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java +++ b/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java @@ -17,6 +17,7 @@ package com.android.server.nearby.provider; import static com.android.server.nearby.NearbyService.TAG; +import static com.android.server.nearby.presence.PresenceConstants.PRESENCE_UUID; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; @@ -33,9 +34,7 @@ import android.nearby.ScanRequest; import android.os.ParcelUuid; import android.util.Log; -import com.android.server.nearby.common.bluetooth.fastpair.Constants; import com.android.server.nearby.injector.Injector; -import com.android.server.nearby.presence.PresenceConstants; import com.android.server.nearby.util.ForegroundThread; import com.google.common.annotations.VisibleForTesting; @@ -50,10 +49,6 @@ import java.util.concurrent.Executor; */ public class BleDiscoveryProvider extends AbstractDiscoveryProvider { - @VisibleForTesting - static final ParcelUuid FAST_PAIR_UUID = new ParcelUuid(Constants.FastPairService.ID); - private static final ParcelUuid PRESENCE_UUID = new ParcelUuid(PresenceConstants.PRESENCE_UUID); - // Don't block the thread as it may be used by other services. private static final Executor NEARBY_EXECUTOR = ForegroundThread.getExecutor(); private final Injector mInjector; @@ -75,14 +70,9 @@ public class BleDiscoveryProvider extends AbstractDiscoveryProvider { } Map serviceDataMap = record.getServiceData(); if (serviceDataMap != null) { - byte[] fastPairData = serviceDataMap.get(FAST_PAIR_UUID); - if (fastPairData != null) { - builder.setData(serviceDataMap.get(FAST_PAIR_UUID)); - } else { - byte[] presenceData = serviceDataMap.get(PRESENCE_UUID); - if (presenceData != null) { - builder.setData(serviceDataMap.get(PRESENCE_UUID)); - } + byte[] presenceData = serviceDataMap.get(PRESENCE_UUID); + if (presenceData != null) { + builder.setData(serviceDataMap.get(PRESENCE_UUID)); } } } @@ -104,7 +94,7 @@ public class BleDiscoveryProvider extends AbstractDiscoveryProvider { List scanFilterList = new ArrayList<>(); scanFilterList.add( new ScanFilter.Builder() - .setServiceData(FAST_PAIR_UUID, new byte[]{0}, new byte[]{0}) + .setServiceData(PRESENCE_UUID, new byte[]{0}, new byte[]{0}) .build()); return scanFilterList; } diff --git a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java deleted file mode 100644 index 0f99a2f6149cdec57e04659ff7aefffd08ab33d6..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.provider; - -import android.accounts.Account; -import android.annotation.Nullable; -import android.content.Context; -import android.nearby.FastPairDataProviderService; -import android.nearby.aidl.ByteArrayParcel; -import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel; -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel; -import android.nearby.aidl.FastPairEligibleAccountsRequestParcel; -import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel; -import android.nearby.aidl.FastPairManageAccountRequestParcel; -import android.util.Log; - -import androidx.annotation.WorkerThread; - -import com.android.server.nearby.common.bloomfilter.BloomFilter; -import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo; - -import java.util.ArrayList; -import java.util.List; - -import service.proto.Data; -import service.proto.Rpcs; - -/** - * FastPairDataProvider is a singleton that implements APIs to get FastPair data. - */ -public class FastPairDataProvider { - - private static final String TAG = "FastPairDataProvider"; - - private static FastPairDataProvider sInstance; - - private ProxyFastPairDataProvider mProxyFastPairDataProvider; - - /** - * Initializes FastPairDataProvider singleton. - */ - public static synchronized FastPairDataProvider init(Context context) { - if (sInstance == null) { - sInstance = new FastPairDataProvider(context); - } - if (sInstance.mProxyFastPairDataProvider == null) { - Log.w(TAG, "no proxy fast pair data provider found"); - } else { - sInstance.mProxyFastPairDataProvider.register(); - } - return sInstance; - } - - @Nullable - public static synchronized FastPairDataProvider getInstance() { - return sInstance; - } - - private FastPairDataProvider(Context context) { - mProxyFastPairDataProvider = ProxyFastPairDataProvider.create( - context, FastPairDataProviderService.ACTION_FAST_PAIR_DATA_PROVIDER); - if (mProxyFastPairDataProvider == null) { - Log.d("FastPairService", "fail to initiate the fast pair proxy provider"); - } else { - Log.d("FastPairService", "the fast pair proxy provider initiated"); - } - } - - /** - * Loads FastPairAntispoofKeyDeviceMetadata. - * - * @throws IllegalStateException If ProxyFastPairDataProvider is not available. - */ - @WorkerThread - @Nullable - public Rpcs.GetObservedDeviceResponse loadFastPairAntispoofKeyDeviceMetadata(byte[] modelId) { - if (mProxyFastPairDataProvider != null) { - FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel = - new FastPairAntispoofKeyDeviceMetadataRequestParcel(); - requestParcel.modelId = modelId; - return Utils.convertToGetObservedDeviceResponse( - mProxyFastPairDataProvider - .loadFastPairAntispoofKeyDeviceMetadata(requestParcel)); - } - throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed"); - } - - /** - * Enrolls an account to Fast Pair. - * - * @throws IllegalStateException If ProxyFastPairDataProvider is not available. - */ - public void optIn(Account account) { - if (mProxyFastPairDataProvider != null) { - FastPairManageAccountRequestParcel requestParcel = - new FastPairManageAccountRequestParcel(); - requestParcel.account = account; - requestParcel.requestType = FastPairDataProviderService.MANAGE_REQUEST_ADD; - mProxyFastPairDataProvider.manageFastPairAccount(requestParcel); - return; - } - throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed"); - } - - /** - * Uploads the device info to Fast Pair account. - * - * @throws IllegalStateException If ProxyFastPairDataProvider is not available. - */ - public void upload(Account account, FastPairUploadInfo uploadInfo) { - if (mProxyFastPairDataProvider != null) { - FastPairManageAccountDeviceRequestParcel requestParcel = - new FastPairManageAccountDeviceRequestParcel(); - requestParcel.account = account; - requestParcel.requestType = FastPairDataProviderService.MANAGE_REQUEST_ADD; - requestParcel.accountKeyDeviceMetadata = - Utils.convertToFastPairAccountKeyDeviceMetadata(uploadInfo); - mProxyFastPairDataProvider.manageFastPairAccountDevice(requestParcel); - return; - } - throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed"); - } - - /** - * Get recognized device from bloom filter. - */ - public Data.FastPairDeviceWithAccountKey getRecognizedDevice(BloomFilter bloomFilter, - byte[] salt) { - return Data.FastPairDeviceWithAccountKey.newBuilder().build(); - } - - /** - * Loads FastPair device accountKeys for a given account, but not other detailed fields. - * - * @throws IllegalStateException If ProxyFastPairDataProvider is not available. - */ - public List loadFastPairDeviceWithAccountKey( - Account account) { - return loadFastPairDeviceWithAccountKey(account, new ArrayList(0)); - } - - /** - * Loads FastPair devices for a list of accountKeys of a given account. - * - * @param account The account of the FastPair devices. - * @param deviceAccountKeys The allow list of FastPair devices if it is not empty. Otherwise, - * the function returns accountKeys of all FastPair devices under the - * account, without detailed fields. - * - * @throws IllegalStateException If ProxyFastPairDataProvider is not available. - */ - public List loadFastPairDeviceWithAccountKey( - Account account, List deviceAccountKeys) { - if (mProxyFastPairDataProvider != null) { - FastPairAccountDevicesMetadataRequestParcel requestParcel = - new FastPairAccountDevicesMetadataRequestParcel(); - requestParcel.account = account; - requestParcel.deviceAccountKeys = new ByteArrayParcel[deviceAccountKeys.size()]; - int i = 0; - for (byte[] deviceAccountKey : deviceAccountKeys) { - requestParcel.deviceAccountKeys[i] = new ByteArrayParcel(); - requestParcel.deviceAccountKeys[i].byteArray = deviceAccountKey; - i = i + 1; - } - return Utils.convertToFastPairDevicesWithAccountKey( - mProxyFastPairDataProvider.loadFastPairAccountDevicesMetadata(requestParcel)); - } - throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed"); - } - - /** - * Loads FastPair Eligible Accounts. - * - * @throws IllegalStateException If ProxyFastPairDataProvider is not available. - */ - public List loadFastPairEligibleAccounts() { - if (mProxyFastPairDataProvider != null) { - FastPairEligibleAccountsRequestParcel requestParcel = - new FastPairEligibleAccountsRequestParcel(); - return Utils.convertToAccountList( - mProxyFastPairDataProvider.loadFastPairEligibleAccounts(requestParcel)); - } - throw new IllegalStateException("No ProxyFastPairDataProvider yet constructed"); - } -} diff --git a/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java deleted file mode 100644 index f0ade6cb49ae53fa2239fe27e090aad23e8b1a3d..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/provider/ProxyFastPairDataProvider.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.provider; - -import android.annotation.Nullable; -import android.content.Context; -import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel; -import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel; -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel; -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel; -import android.nearby.aidl.FastPairEligibleAccountParcel; -import android.nearby.aidl.FastPairEligibleAccountsRequestParcel; -import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel; -import android.nearby.aidl.FastPairManageAccountRequestParcel; -import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback; -import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback; -import android.nearby.aidl.IFastPairDataProvider; -import android.nearby.aidl.IFastPairEligibleAccountsCallback; -import android.nearby.aidl.IFastPairManageAccountCallback; -import android.nearby.aidl.IFastPairManageAccountDeviceCallback; -import android.os.IBinder; -import android.os.RemoteException; - -import androidx.annotation.WorkerThread; - -import com.android.server.nearby.common.servicemonitor.CurrentUserServiceProvider; -import com.android.server.nearby.common.servicemonitor.CurrentUserServiceProvider.BoundServiceInfo; -import com.android.server.nearby.common.servicemonitor.ServiceMonitor; -import com.android.server.nearby.common.servicemonitor.ServiceMonitor.ServiceListener; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Proxy for IFastPairDataProvider implementations. - */ -public class ProxyFastPairDataProvider implements ServiceListener { - - private static final int TIME_OUT_MILLIS = 10000; - - /** - * Creates and registers this proxy. If no suitable service is available for the proxy, returns - * null. - */ - @Nullable - public static ProxyFastPairDataProvider create(Context context, String action) { - ProxyFastPairDataProvider proxy = new ProxyFastPairDataProvider(context, action); - if (proxy.checkServiceResolves()) { - return proxy; - } else { - return null; - } - } - - private final ServiceMonitor mServiceMonitor; - - private ProxyFastPairDataProvider(Context context, String action) { - // safe to use direct executor since our locks are not acquired in a code path invoked by - // our owning provider - - mServiceMonitor = ServiceMonitor.create(context, "FAST_PAIR_DATA_PROVIDER", - CurrentUserServiceProvider.create(context, action), this); - } - - private boolean checkServiceResolves() { - return mServiceMonitor.checkServiceResolves(); - } - - /** - * User service watch to connect to actually services implemented by OEMs. - */ - public void register() { - mServiceMonitor.register(); - } - - // Fast Pair Data Provider doesn't maintain a long running state. - // Therefore, it doesn't need setup at bind time. - @Override - public void onBind(IBinder binder, BoundServiceInfo boundServiceInfo) throws RemoteException { - } - - // Fast Pair Data Provider doesn't maintain a long running state. - // Therefore, it doesn't need tear down at unbind time. - @Override - public void onUnbind() { - } - - /** - * Invokes system api loadFastPairEligibleAccounts. - * - * @return an array of acccounts and their opt in status. - */ - @WorkerThread - @Nullable - public FastPairEligibleAccountParcel[] loadFastPairEligibleAccounts( - FastPairEligibleAccountsRequestParcel requestParcel) { - final CountDownLatch waitForCompletionLatch = new CountDownLatch(1); - final AtomicReference response = new AtomicReference<>(); - mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder); - IFastPairEligibleAccountsCallback callback = - new IFastPairEligibleAccountsCallback.Stub() { - public void onFastPairEligibleAccountsReceived( - FastPairEligibleAccountParcel[] accountParcels) { - response.set(accountParcels); - waitForCompletionLatch.countDown(); - } - - public void onError(int code, String message) { - waitForCompletionLatch.countDown(); - } - }; - provider.loadFastPairEligibleAccounts(requestParcel, callback); - } - - @Override - public void onError() { - waitForCompletionLatch.countDown(); - } - }); - try { - waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // skip. - } - return response.get(); - } - - /** - * Invokes system api manageFastPairAccount to opt in account, or opt out account. - */ - @WorkerThread - public void manageFastPairAccount(FastPairManageAccountRequestParcel requestParcel) { - final CountDownLatch waitForCompletionLatch = new CountDownLatch(1); - mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder); - IFastPairManageAccountCallback callback = - new IFastPairManageAccountCallback.Stub() { - public void onSuccess() { - waitForCompletionLatch.countDown(); - } - - public void onError(int code, String message) { - waitForCompletionLatch.countDown(); - } - }; - provider.manageFastPairAccount(requestParcel, callback); - } - - @Override - public void onError() { - waitForCompletionLatch.countDown(); - } - }); - try { - waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // skip. - } - return; - } - - /** - * Invokes system api manageFastPairAccountDevice to add or remove a device from a Fast Pair - * account. - */ - @WorkerThread - public void manageFastPairAccountDevice( - FastPairManageAccountDeviceRequestParcel requestParcel) { - final CountDownLatch waitForCompletionLatch = new CountDownLatch(1); - mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder); - IFastPairManageAccountDeviceCallback callback = - new IFastPairManageAccountDeviceCallback.Stub() { - public void onSuccess() { - waitForCompletionLatch.countDown(); - } - - public void onError(int code, String message) { - waitForCompletionLatch.countDown(); - } - }; - provider.manageFastPairAccountDevice(requestParcel, callback); - } - - @Override - public void onError() { - waitForCompletionLatch.countDown(); - } - }); - try { - waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // skip. - } - return; - } - - /** - * Invokes system api loadFastPairAntispoofKeyDeviceMetadata. - * - * @return the Fast Pair AntispoofKeyDeviceMetadata of a given device. - */ - @WorkerThread - @Nullable - FastPairAntispoofKeyDeviceMetadataParcel loadFastPairAntispoofKeyDeviceMetadata( - FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel) { - final CountDownLatch waitForCompletionLatch = new CountDownLatch(1); - final AtomicReference response = - new AtomicReference<>(); - mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder); - IFastPairAntispoofKeyDeviceMetadataCallback callback = - new IFastPairAntispoofKeyDeviceMetadataCallback.Stub() { - public void onFastPairAntispoofKeyDeviceMetadataReceived( - FastPairAntispoofKeyDeviceMetadataParcel metadata) { - response.set(metadata); - waitForCompletionLatch.countDown(); - } - - public void onError(int code, String message) { - waitForCompletionLatch.countDown(); - } - }; - provider.loadFastPairAntispoofKeyDeviceMetadata(requestParcel, callback); - } - - @Override - public void onError() { - waitForCompletionLatch.countDown(); - } - }); - try { - waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // skip. - } - return response.get(); - } - - /** - * Invokes loadFastPairAccountDevicesMetadata. - * - * @return the metadata of Fast Pair devices that are associated with a given account. - */ - @WorkerThread - @Nullable - FastPairAccountKeyDeviceMetadataParcel[] loadFastPairAccountDevicesMetadata( - FastPairAccountDevicesMetadataRequestParcel requestParcel) { - final CountDownLatch waitForCompletionLatch = new CountDownLatch(1); - final AtomicReference response = - new AtomicReference<>(); - mServiceMonitor.runOnBinder(new ServiceMonitor.BinderOperation() { - @Override - public void run(IBinder binder) throws RemoteException { - IFastPairDataProvider provider = IFastPairDataProvider.Stub.asInterface(binder); - IFastPairAccountDevicesMetadataCallback callback = - new IFastPairAccountDevicesMetadataCallback.Stub() { - public void onFastPairAccountDevicesMetadataReceived( - FastPairAccountKeyDeviceMetadataParcel[] metadatas) { - response.set(metadatas); - waitForCompletionLatch.countDown(); - } - - public void onError(int code, String message) { - waitForCompletionLatch.countDown(); - } - }; - provider.loadFastPairAccountDevicesMetadata(requestParcel, callback); - } - - @Override - public void onError() { - waitForCompletionLatch.countDown(); - } - }); - try { - waitForCompletionLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // skip. - } - return response.get(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/provider/Utils.java b/nearby/service/java/com/android/server/nearby/provider/Utils.java deleted file mode 100644 index 0f1c5673747b979f032413b29ce946cfd8b5d117..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/provider/Utils.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.provider; - -import android.accounts.Account; -import android.annotation.Nullable; -import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel; -import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel; -import android.nearby.aidl.FastPairDeviceMetadataParcel; -import android.nearby.aidl.FastPairDiscoveryItemParcel; -import android.nearby.aidl.FastPairEligibleAccountParcel; - -import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo; - -import com.google.protobuf.ByteString; - -import java.util.ArrayList; -import java.util.List; - -import service.proto.Cache; -import service.proto.Data; -import service.proto.FastPairString.FastPairStrings; -import service.proto.Rpcs; - -/** - * Utility functions to convert between different data classes. - */ -class Utils { - - static List convertToFastPairDevicesWithAccountKey( - @Nullable FastPairAccountKeyDeviceMetadataParcel[] metadataParcels) { - if (metadataParcels == null) { - return new ArrayList(0); - } - - List fpDeviceList = - new ArrayList<>(metadataParcels.length); - for (FastPairAccountKeyDeviceMetadataParcel metadataParcel : metadataParcels) { - if (metadataParcel == null) { - continue; - } - Data.FastPairDeviceWithAccountKey.Builder fpDeviceBuilder = - Data.FastPairDeviceWithAccountKey.newBuilder(); - if (metadataParcel.deviceAccountKey != null) { - fpDeviceBuilder.setAccountKey( - ByteString.copyFrom(metadataParcel.deviceAccountKey)); - } - if (metadataParcel.sha256DeviceAccountKeyPublicAddress != null) { - fpDeviceBuilder.setSha256AccountKeyPublicAddress( - ByteString.copyFrom(metadataParcel.sha256DeviceAccountKeyPublicAddress)); - } - - Cache.StoredDiscoveryItem.Builder storedDiscoveryItemBuilder = - Cache.StoredDiscoveryItem.newBuilder(); - - if (metadataParcel.discoveryItem != null) { - if (metadataParcel.discoveryItem.actionUrl != null) { - storedDiscoveryItemBuilder.setActionUrl(metadataParcel.discoveryItem.actionUrl); - } - Cache.ResolvedUrlType urlType = Cache.ResolvedUrlType.forNumber( - metadataParcel.discoveryItem.actionUrlType); - if (urlType != null) { - storedDiscoveryItemBuilder.setActionUrlType(urlType); - } - if (metadataParcel.discoveryItem.appName != null) { - storedDiscoveryItemBuilder.setAppName(metadataParcel.discoveryItem.appName); - } - if (metadataParcel.discoveryItem.authenticationPublicKeySecp256r1 != null) { - storedDiscoveryItemBuilder.setAuthenticationPublicKeySecp256R1( - ByteString.copyFrom( - metadataParcel.discoveryItem.authenticationPublicKeySecp256r1)); - } - if (metadataParcel.discoveryItem.description != null) { - storedDiscoveryItemBuilder.setDescription( - metadataParcel.discoveryItem.description); - } - if (metadataParcel.discoveryItem.deviceName != null) { - storedDiscoveryItemBuilder.setDeviceName( - metadataParcel.discoveryItem.deviceName); - } - if (metadataParcel.discoveryItem.displayUrl != null) { - storedDiscoveryItemBuilder.setDisplayUrl( - metadataParcel.discoveryItem.displayUrl); - } - storedDiscoveryItemBuilder.setFirstObservationTimestampMillis( - metadataParcel.discoveryItem.firstObservationTimestampMillis); - if (metadataParcel.discoveryItem.iconFifeUrl != null) { - storedDiscoveryItemBuilder.setIconFifeUrl( - metadataParcel.discoveryItem.iconFifeUrl); - } - if (metadataParcel.discoveryItem.iconPng != null) { - storedDiscoveryItemBuilder.setIconPng( - ByteString.copyFrom(metadataParcel.discoveryItem.iconPng)); - } - if (metadataParcel.discoveryItem.id != null) { - storedDiscoveryItemBuilder.setId(metadataParcel.discoveryItem.id); - } - storedDiscoveryItemBuilder.setLastObservationTimestampMillis( - metadataParcel.discoveryItem.lastObservationTimestampMillis); - if (metadataParcel.discoveryItem.macAddress != null) { - storedDiscoveryItemBuilder.setMacAddress( - metadataParcel.discoveryItem.macAddress); - } - if (metadataParcel.discoveryItem.packageName != null) { - storedDiscoveryItemBuilder.setPackageName( - metadataParcel.discoveryItem.packageName); - } - storedDiscoveryItemBuilder.setPendingAppInstallTimestampMillis( - metadataParcel.discoveryItem.pendingAppInstallTimestampMillis); - storedDiscoveryItemBuilder.setRssi(metadataParcel.discoveryItem.rssi); - Cache.StoredDiscoveryItem.State state = - Cache.StoredDiscoveryItem.State.forNumber( - metadataParcel.discoveryItem.state); - if (state != null) { - storedDiscoveryItemBuilder.setState(state); - } - if (metadataParcel.discoveryItem.title != null) { - storedDiscoveryItemBuilder.setTitle(metadataParcel.discoveryItem.title); - } - if (metadataParcel.discoveryItem.triggerId != null) { - storedDiscoveryItemBuilder.setTriggerId(metadataParcel.discoveryItem.triggerId); - } - storedDiscoveryItemBuilder.setTxPower(metadataParcel.discoveryItem.txPower); - } - if (metadataParcel.metadata != null) { - FastPairStrings.Builder stringsBuilder = FastPairStrings.newBuilder(); - if (metadataParcel.metadata.connectSuccessCompanionAppInstalled != null) { - stringsBuilder.setPairingFinishedCompanionAppInstalled( - metadataParcel.metadata.connectSuccessCompanionAppInstalled); - } - if (metadataParcel.metadata.connectSuccessCompanionAppNotInstalled != null) { - stringsBuilder.setPairingFinishedCompanionAppNotInstalled( - metadataParcel.metadata.connectSuccessCompanionAppNotInstalled); - } - if (metadataParcel.metadata.failConnectGoToSettingsDescription != null) { - stringsBuilder.setPairingFailDescription( - metadataParcel.metadata.failConnectGoToSettingsDescription); - } - if (metadataParcel.metadata.initialNotificationDescription != null) { - stringsBuilder.setTapToPairWithAccount( - metadataParcel.metadata.initialNotificationDescription); - } - if (metadataParcel.metadata.initialNotificationDescriptionNoAccount != null) { - stringsBuilder.setTapToPairWithoutAccount( - metadataParcel.metadata.initialNotificationDescriptionNoAccount); - } - if (metadataParcel.metadata.initialPairingDescription != null) { - stringsBuilder.setInitialPairingDescription( - metadataParcel.metadata.initialPairingDescription); - } - if (metadataParcel.metadata.retroactivePairingDescription != null) { - stringsBuilder.setRetroactivePairingDescription( - metadataParcel.metadata.retroactivePairingDescription); - } - if (metadataParcel.metadata.subsequentPairingDescription != null) { - stringsBuilder.setSubsequentPairingDescription( - metadataParcel.metadata.subsequentPairingDescription); - } - if (metadataParcel.metadata.waitLaunchCompanionAppDescription != null) { - stringsBuilder.setWaitAppLaunchDescription( - metadataParcel.metadata.waitLaunchCompanionAppDescription); - } - storedDiscoveryItemBuilder.setFastPairStrings(stringsBuilder.build()); - - Cache.FastPairInformation.Builder fpInformationBuilder = - Cache.FastPairInformation.newBuilder(); - Rpcs.TrueWirelessHeadsetImages.Builder imagesBuilder = - Rpcs.TrueWirelessHeadsetImages.newBuilder(); - if (metadataParcel.metadata.trueWirelessImageUrlCase != null) { - imagesBuilder.setCaseUrl(metadataParcel.metadata.trueWirelessImageUrlCase); - } - if (metadataParcel.metadata.trueWirelessImageUrlLeftBud != null) { - imagesBuilder.setLeftBudUrl( - metadataParcel.metadata.trueWirelessImageUrlLeftBud); - } - if (metadataParcel.metadata.trueWirelessImageUrlRightBud != null) { - imagesBuilder.setRightBudUrl( - metadataParcel.metadata.trueWirelessImageUrlRightBud); - } - fpInformationBuilder.setTrueWirelessImages(imagesBuilder.build()); - Rpcs.DeviceType deviceType = - Rpcs.DeviceType.forNumber(metadataParcel.metadata.deviceType); - if (deviceType != null) { - fpInformationBuilder.setDeviceType(deviceType); - } - - storedDiscoveryItemBuilder.setFastPairInformation(fpInformationBuilder.build()); - } - fpDeviceBuilder.setDiscoveryItem(storedDiscoveryItemBuilder.build()); - fpDeviceList.add(fpDeviceBuilder.build()); - } - return fpDeviceList; - } - - static List convertToAccountList( - @Nullable FastPairEligibleAccountParcel[] accountParcels) { - if (accountParcels == null) { - return new ArrayList(0); - } - List accounts = new ArrayList(accountParcels.length); - for (FastPairEligibleAccountParcel parcel : accountParcels) { - if (parcel != null && parcel.account != null) { - accounts.add(parcel.account); - } - } - return accounts; - } - - private static @Nullable Rpcs.Device convertToDevice( - FastPairAntispoofKeyDeviceMetadataParcel metadata) { - - Rpcs.Device.Builder deviceBuilder = Rpcs.Device.newBuilder(); - if (metadata.antispoofPublicKey != null) { - deviceBuilder.setAntiSpoofingKeyPair(Rpcs.AntiSpoofingKeyPair.newBuilder() - .setPublicKey(ByteString.copyFrom(metadata.antispoofPublicKey)) - .build()); - } - if (metadata.deviceMetadata != null) { - Rpcs.TrueWirelessHeadsetImages.Builder imagesBuilder = - Rpcs.TrueWirelessHeadsetImages.newBuilder(); - if (metadata.deviceMetadata.trueWirelessImageUrlLeftBud != null) { - imagesBuilder.setLeftBudUrl(metadata.deviceMetadata.trueWirelessImageUrlLeftBud); - } - if (metadata.deviceMetadata.trueWirelessImageUrlRightBud != null) { - imagesBuilder.setRightBudUrl(metadata.deviceMetadata.trueWirelessImageUrlRightBud); - } - if (metadata.deviceMetadata.trueWirelessImageUrlCase != null) { - imagesBuilder.setCaseUrl(metadata.deviceMetadata.trueWirelessImageUrlCase); - } - deviceBuilder.setTrueWirelessImages(imagesBuilder.build()); - if (metadata.deviceMetadata.imageUrl != null) { - deviceBuilder.setImageUrl(metadata.deviceMetadata.imageUrl); - } - if (metadata.deviceMetadata.intentUri != null) { - deviceBuilder.setIntentUri(metadata.deviceMetadata.intentUri); - } - if (metadata.deviceMetadata.name != null) { - deviceBuilder.setName(metadata.deviceMetadata.name); - } - Rpcs.DeviceType deviceType = - Rpcs.DeviceType.forNumber(metadata.deviceMetadata.deviceType); - if (deviceType != null) { - deviceBuilder.setDeviceType(deviceType); - } - deviceBuilder.setBleTxPower(metadata.deviceMetadata.bleTxPower) - .setTriggerDistance(metadata.deviceMetadata.triggerDistance); - } - - return deviceBuilder.build(); - } - - private static @Nullable ByteString convertToImage( - FastPairAntispoofKeyDeviceMetadataParcel metadata) { - if (metadata.deviceMetadata == null || metadata.deviceMetadata.image == null) { - return null; - } - - return ByteString.copyFrom(metadata.deviceMetadata.image); - } - - private static @Nullable Rpcs.ObservedDeviceStrings - convertToObservedDeviceStrings(FastPairAntispoofKeyDeviceMetadataParcel metadata) { - if (metadata.deviceMetadata == null) { - return null; - } - - Rpcs.ObservedDeviceStrings.Builder stringsBuilder = Rpcs.ObservedDeviceStrings.newBuilder(); - if (metadata.deviceMetadata.connectSuccessCompanionAppInstalled != null) { - stringsBuilder.setConnectSuccessCompanionAppInstalled( - metadata.deviceMetadata.connectSuccessCompanionAppInstalled); - } - if (metadata.deviceMetadata.connectSuccessCompanionAppNotInstalled != null) { - stringsBuilder.setConnectSuccessCompanionAppNotInstalled( - metadata.deviceMetadata.connectSuccessCompanionAppNotInstalled); - } - if (metadata.deviceMetadata.downloadCompanionAppDescription != null) { - stringsBuilder.setDownloadCompanionAppDescription( - metadata.deviceMetadata.downloadCompanionAppDescription); - } - if (metadata.deviceMetadata.failConnectGoToSettingsDescription != null) { - stringsBuilder.setFailConnectGoToSettingsDescription( - metadata.deviceMetadata.failConnectGoToSettingsDescription); - } - if (metadata.deviceMetadata.initialNotificationDescription != null) { - stringsBuilder.setInitialNotificationDescription( - metadata.deviceMetadata.initialNotificationDescription); - } - if (metadata.deviceMetadata.initialNotificationDescriptionNoAccount != null) { - stringsBuilder.setInitialNotificationDescriptionNoAccount( - metadata.deviceMetadata.initialNotificationDescriptionNoAccount); - } - if (metadata.deviceMetadata.initialPairingDescription != null) { - stringsBuilder.setInitialPairingDescription( - metadata.deviceMetadata.initialPairingDescription); - } - if (metadata.deviceMetadata.openCompanionAppDescription != null) { - stringsBuilder.setOpenCompanionAppDescription( - metadata.deviceMetadata.openCompanionAppDescription); - } - if (metadata.deviceMetadata.retroactivePairingDescription != null) { - stringsBuilder.setRetroactivePairingDescription( - metadata.deviceMetadata.retroactivePairingDescription); - } - if (metadata.deviceMetadata.subsequentPairingDescription != null) { - stringsBuilder.setSubsequentPairingDescription( - metadata.deviceMetadata.subsequentPairingDescription); - } - if (metadata.deviceMetadata.unableToConnectDescription != null) { - stringsBuilder.setUnableToConnectDescription( - metadata.deviceMetadata.unableToConnectDescription); - } - if (metadata.deviceMetadata.unableToConnectTitle != null) { - stringsBuilder.setUnableToConnectTitle( - metadata.deviceMetadata.unableToConnectTitle); - } - if (metadata.deviceMetadata.updateCompanionAppDescription != null) { - stringsBuilder.setUpdateCompanionAppDescription( - metadata.deviceMetadata.updateCompanionAppDescription); - } - if (metadata.deviceMetadata.waitLaunchCompanionAppDescription != null) { - stringsBuilder.setWaitLaunchCompanionAppDescription( - metadata.deviceMetadata.waitLaunchCompanionAppDescription); - } - - return stringsBuilder.build(); - } - - static @Nullable Rpcs.GetObservedDeviceResponse - convertToGetObservedDeviceResponse( - @Nullable FastPairAntispoofKeyDeviceMetadataParcel metadata) { - if (metadata == null) { - return null; - } - - Rpcs.GetObservedDeviceResponse.Builder responseBuilder = - Rpcs.GetObservedDeviceResponse.newBuilder(); - - Rpcs.Device device = convertToDevice(metadata); - if (device != null) { - responseBuilder.setDevice(device); - } - ByteString image = convertToImage(metadata); - if (image != null) { - responseBuilder.setImage(image); - } - Rpcs.ObservedDeviceStrings strings = convertToObservedDeviceStrings(metadata); - if (strings != null) { - responseBuilder.setStrings(strings); - } - - return responseBuilder.build(); - } - - static @Nullable FastPairAccountKeyDeviceMetadataParcel - convertToFastPairAccountKeyDeviceMetadata( - @Nullable FastPairUploadInfo uploadInfo) { - if (uploadInfo == null) { - return null; - } - - FastPairAccountKeyDeviceMetadataParcel accountKeyDeviceMetadataParcel = - new FastPairAccountKeyDeviceMetadataParcel(); - if (uploadInfo.getAccountKey() != null) { - accountKeyDeviceMetadataParcel.deviceAccountKey = - uploadInfo.getAccountKey().toByteArray(); - } - if (uploadInfo.getSha256AccountKeyPublicAddress() != null) { - accountKeyDeviceMetadataParcel.sha256DeviceAccountKeyPublicAddress = - uploadInfo.getSha256AccountKeyPublicAddress().toByteArray(); - } - if (uploadInfo.getStoredDiscoveryItem() != null) { - accountKeyDeviceMetadataParcel.metadata = - convertToFastPairDeviceMetadata(uploadInfo.getStoredDiscoveryItem()); - accountKeyDeviceMetadataParcel.discoveryItem = - convertToFastPairDiscoveryItem(uploadInfo.getStoredDiscoveryItem()); - } - - return accountKeyDeviceMetadataParcel; - } - - private static @Nullable FastPairDiscoveryItemParcel - convertToFastPairDiscoveryItem(Cache.StoredDiscoveryItem storedDiscoveryItem) { - FastPairDiscoveryItemParcel discoveryItemParcel = new FastPairDiscoveryItemParcel(); - discoveryItemParcel.actionUrl = storedDiscoveryItem.getActionUrl(); - discoveryItemParcel.actionUrlType = storedDiscoveryItem.getActionUrlType().getNumber(); - discoveryItemParcel.appName = storedDiscoveryItem.getAppName(); - discoveryItemParcel.authenticationPublicKeySecp256r1 = - storedDiscoveryItem.getAuthenticationPublicKeySecp256R1().toByteArray(); - discoveryItemParcel.description = storedDiscoveryItem.getDescription(); - discoveryItemParcel.deviceName = storedDiscoveryItem.getDeviceName(); - discoveryItemParcel.displayUrl = storedDiscoveryItem.getDisplayUrl(); - discoveryItemParcel.firstObservationTimestampMillis = - storedDiscoveryItem.getFirstObservationTimestampMillis(); - discoveryItemParcel.iconFifeUrl = storedDiscoveryItem.getIconFifeUrl(); - discoveryItemParcel.iconPng = storedDiscoveryItem.getIconPng().toByteArray(); - discoveryItemParcel.id = storedDiscoveryItem.getId(); - discoveryItemParcel.lastObservationTimestampMillis = - storedDiscoveryItem.getLastObservationTimestampMillis(); - discoveryItemParcel.macAddress = storedDiscoveryItem.getMacAddress(); - discoveryItemParcel.packageName = storedDiscoveryItem.getPackageName(); - discoveryItemParcel.pendingAppInstallTimestampMillis = - storedDiscoveryItem.getPendingAppInstallTimestampMillis(); - discoveryItemParcel.rssi = storedDiscoveryItem.getRssi(); - discoveryItemParcel.state = storedDiscoveryItem.getState().getNumber(); - discoveryItemParcel.title = storedDiscoveryItem.getTitle(); - discoveryItemParcel.triggerId = storedDiscoveryItem.getTriggerId(); - discoveryItemParcel.txPower = storedDiscoveryItem.getTxPower(); - - return discoveryItemParcel; - } - - /* Do we upload these? - String downloadCompanionAppDescription = - bundle.getString("downloadCompanionAppDescription"); - String locale = bundle.getString("locale"); - String openCompanionAppDescription = bundle.getString("openCompanionAppDescription"); - float triggerDistance = bundle.getFloat("triggerDistance"); - String unableToConnectDescription = bundle.getString("unableToConnectDescription"); - String unableToConnectTitle = bundle.getString("unableToConnectTitle"); - String updateCompanionAppDescription = bundle.getString("updateCompanionAppDescription"); - */ - private static @Nullable FastPairDeviceMetadataParcel - convertToFastPairDeviceMetadata(Cache.StoredDiscoveryItem storedDiscoveryItem) { - FastPairStrings fpStrings = storedDiscoveryItem.getFastPairStrings(); - - FastPairDeviceMetadataParcel metadataParcel = new FastPairDeviceMetadataParcel(); - metadataParcel.connectSuccessCompanionAppInstalled = - fpStrings.getPairingFinishedCompanionAppInstalled(); - metadataParcel.connectSuccessCompanionAppNotInstalled = - fpStrings.getPairingFinishedCompanionAppNotInstalled(); - metadataParcel.failConnectGoToSettingsDescription = fpStrings.getPairingFailDescription(); - metadataParcel.initialNotificationDescription = fpStrings.getTapToPairWithAccount(); - metadataParcel.initialNotificationDescriptionNoAccount = - fpStrings.getTapToPairWithoutAccount(); - metadataParcel.initialPairingDescription = fpStrings.getInitialPairingDescription(); - metadataParcel.retroactivePairingDescription = fpStrings.getRetroactivePairingDescription(); - metadataParcel.subsequentPairingDescription = fpStrings.getSubsequentPairingDescription(); - metadataParcel.waitLaunchCompanionAppDescription = fpStrings.getWaitAppLaunchDescription(); - - Cache.FastPairInformation fpInformation = storedDiscoveryItem.getFastPairInformation(); - metadataParcel.trueWirelessImageUrlCase = - fpInformation.getTrueWirelessImages().getCaseUrl(); - metadataParcel.trueWirelessImageUrlLeftBud = - fpInformation.getTrueWirelessImages().getLeftBudUrl(); - metadataParcel.trueWirelessImageUrlRightBud = - fpInformation.getTrueWirelessImages().getRightBudUrl(); - metadataParcel.deviceType = fpInformation.getDeviceType().getNumber(); - - return metadataParcel; - } -} diff --git a/nearby/service/java/com/android/server/nearby/util/DataUtils.java b/nearby/service/java/com/android/server/nearby/util/DataUtils.java deleted file mode 100644 index 8bb83e947b217c5747ead0daa6dd2668a968e15e..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/util/DataUtils.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.util; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import service.proto.Cache.ScanFastPairStoreItem; -import service.proto.Cache.StoredDiscoveryItem; -import service.proto.FastPairString.FastPairStrings; -import service.proto.Rpcs.Device; -import service.proto.Rpcs.GetObservedDeviceResponse; -import service.proto.Rpcs.ObservedDeviceStrings; - -/** - * Utils class converts different data types {@link ScanFastPairStoreItem}, - * {@link StoredDiscoveryItem} and {@link GetObservedDeviceResponse}, - * - */ -public final class DataUtils { - - /** - * Converts a {@link GetObservedDeviceResponse} to a {@link ScanFastPairStoreItem}. - */ - public static ScanFastPairStoreItem toScanFastPairStoreItem( - GetObservedDeviceResponse observedDeviceResponse, - @NonNull String bleAddress, @Nullable String account) { - Device device = observedDeviceResponse.getDevice(); - String deviceName = device.getName(); - return ScanFastPairStoreItem.newBuilder() - .setAddress(bleAddress) - .setActionUrl(device.getIntentUri()) - .setDeviceName(deviceName) - .setIconPng(observedDeviceResponse.getImage()) - .setIconFifeUrl(device.getImageUrl()) - .setAntiSpoofingPublicKey(device.getAntiSpoofingKeyPair().getPublicKey()) - .setFastPairStrings(getFastPairStrings(observedDeviceResponse, deviceName, account)) - .build(); - } - - /** - * Prints readable string for a {@link ScanFastPairStoreItem}. - */ - public static String toString(ScanFastPairStoreItem item) { - return "ScanFastPairStoreItem=[address:" + item.getAddress() - + ", actionUr:" + item.getActionUrl() - + ", deviceName:" + item.getDeviceName() - + ", iconPng:" + item.getIconPng() - + ", iconFifeUrl:" + item.getIconFifeUrl() - + ", antiSpoofingKeyPair:" + item.getAntiSpoofingPublicKey() - + ", fastPairStrings:" + toString(item.getFastPairStrings()) - + "]"; - } - - /** - * Prints readable string for a {@link FastPairStrings} - */ - public static String toString(FastPairStrings fastPairStrings) { - return "FastPairStrings[" - + "tapToPairWithAccount=" + fastPairStrings.getTapToPairWithAccount() - + ", tapToPairWithoutAccount=" + fastPairStrings.getTapToPairWithoutAccount() - + ", initialPairingDescription=" + fastPairStrings.getInitialPairingDescription() - + ", pairingFinishedCompanionAppInstalled=" - + fastPairStrings.getPairingFinishedCompanionAppInstalled() - + ", pairingFinishedCompanionAppNotInstalled=" - + fastPairStrings.getPairingFinishedCompanionAppNotInstalled() - + ", subsequentPairingDescription=" - + fastPairStrings.getSubsequentPairingDescription() - + ", retroactivePairingDescription=" - + fastPairStrings.getRetroactivePairingDescription() - + ", waitAppLaunchDescription=" + fastPairStrings.getWaitAppLaunchDescription() - + ", pairingFailDescription=" + fastPairStrings.getPairingFailDescription() - + "]"; - } - - private static FastPairStrings getFastPairStrings(GetObservedDeviceResponse response, - String deviceName, @Nullable String account) { - ObservedDeviceStrings strings = response.getStrings(); - return FastPairStrings.newBuilder() - .setTapToPairWithAccount(strings.getInitialNotificationDescription()) - .setTapToPairWithoutAccount( - strings.getInitialNotificationDescriptionNoAccount()) - .setInitialPairingDescription(account == null - ? strings.getInitialNotificationDescriptionNoAccount() - : String.format(strings.getInitialPairingDescription(), - deviceName, account)) - .setPairingFinishedCompanionAppInstalled( - strings.getConnectSuccessCompanionAppInstalled()) - .setPairingFinishedCompanionAppNotInstalled( - strings.getConnectSuccessCompanionAppNotInstalled()) - .setSubsequentPairingDescription(strings.getSubsequentPairingDescription()) - .setRetroactivePairingDescription(strings.getRetroactivePairingDescription()) - .setWaitAppLaunchDescription(strings.getWaitLaunchCompanionAppDescription()) - .setPairingFailDescription(strings.getFailConnectGoToSettingsDescription()) - .build(); - } -} diff --git a/nearby/service/java/com/android/server/nearby/util/Environment.java b/nearby/service/java/com/android/server/nearby/util/Environment.java deleted file mode 100644 index d397862b77e8235189887b6f53e5332294a9622f..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/util/Environment.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 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 com.android.server.nearby.util; - -import android.content.ApexEnvironment; -import android.content.pm.ApplicationInfo; -import android.os.UserHandle; - -import java.io.File; - -/** - * Provides function to make sure the function caller is from the same apex. - */ -public class Environment { - /** - * NEARBY apex name. - */ - private static final String NEARBY_APEX_NAME = "com.android.tethering"; - - /** - * The path where the Nearby apex is mounted. - * Current value = "/apex/com.android.tethering" - */ - private static final String NEARBY_APEX_PATH = - new File("/apex", NEARBY_APEX_NAME).getAbsolutePath(); - - /** - * Nearby shared folder. - */ - public static File getNearbyDirectory() { - return ApexEnvironment.getApexEnvironment(NEARBY_APEX_NAME).getDeviceProtectedDataDir(); - } - - /** - * Nearby user specific folder. - */ - public static File getNearbyDirectory(int userId) { - return ApexEnvironment.getApexEnvironment(NEARBY_APEX_NAME) - .getCredentialProtectedDataDirForUser(UserHandle.of(userId)); - } - - /** - * Returns true if the app is in the nearby apex, false otherwise. - * Checks if the app's path starts with "/apex/com.android.tethering". - */ - public static boolean isAppInNearbyApex(ApplicationInfo appInfo) { - return appInfo.sourceDir.startsWith(NEARBY_APEX_PATH); - } -} diff --git a/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java b/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java deleted file mode 100644 index 6021ff6986d9a559561e4ea9226a0f8b52053cc3..0000000000000000000000000000000000000000 --- a/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2022 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 com.android.server.nearby.util; - -import android.annotation.Nullable; -import android.bluetooth.le.ScanRecord; -import android.os.ParcelUuid; -import android.util.SparseArray; - -import com.android.server.nearby.common.ble.BleFilter; -import com.android.server.nearby.common.ble.BleRecord; - -import java.util.Arrays; - -/** - * Parses Fast Pair information out of {@link BleRecord}s. - * - *

    There are 2 different packet formats that are supported, which is used can be determined by - * packet length: - * - *

    For 3-byte packets, the full packet is the model ID. - * - *

    For all other packets, the first byte is the header, followed by the model ID, followed by - * zero or more extra fields. Each field has its own header byte followed by the field value. The - * packet header is formatted as 0bVVVLLLLR (V = version, L = model ID length, R = reserved) and - * each extra field header is 0bLLLLTTTT (L = field length, T = field type). - */ -public class FastPairDecoder { - - private static final int FIELD_TYPE_BLOOM_FILTER = 0; - private static final int FIELD_TYPE_BLOOM_FILTER_SALT = 1; - private static final int FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION = 2; - private static final int FIELD_TYPE_BATTERY = 3; - private static final int FIELD_TYPE_BATTERY_NO_NOTIFICATION = 4; - public static final int FIELD_TYPE_CONNECTION_STATE = 5; - private static final int FIELD_TYPE_RANDOM_RESOLVABLE_DATA = 6; - - - /** FE2C is the 16-bit Service UUID. The rest is the base UUID. See BluetoothUuid (hidden). */ - private static final ParcelUuid FAST_PAIR_SERVICE_PARCEL_UUID = - ParcelUuid.fromString("0000FE2C-0000-1000-8000-00805F9B34FB"); - - /** The filter you use to scan for Fast Pair BLE advertisements. */ - public static final BleFilter FILTER = - new BleFilter.Builder().setServiceData(FAST_PAIR_SERVICE_PARCEL_UUID, - new byte[0]).build(); - - // NOTE: Ensure that all bitmasks are always ints, not bytes so that bitshifting works correctly - // without needing worry about signing errors. - private static final int HEADER_VERSION_BITMASK = 0b11100000; - private static final int HEADER_LENGTH_BITMASK = 0b00011110; - private static final int HEADER_VERSION_OFFSET = 5; - private static final int HEADER_LENGTH_OFFSET = 1; - - private static final int EXTRA_FIELD_LENGTH_BITMASK = 0b11110000; - private static final int EXTRA_FIELD_TYPE_BITMASK = 0b00001111; - private static final int EXTRA_FIELD_LENGTH_OFFSET = 4; - private static final int EXTRA_FIELD_TYPE_OFFSET = 0; - - private static final int MIN_ID_LENGTH = 3; - private static final int MAX_ID_LENGTH = 14; - private static final int HEADER_INDEX = 0; - private static final int HEADER_LENGTH = 1; - private static final int FIELD_HEADER_LENGTH = 1; - - // Not using java.util.IllegalFormatException because it is unchecked. - private static class IllegalFormatException extends Exception { - private IllegalFormatException(String message) { - super(message); - } - } - - /** - * Gets model id data from broadcast - */ - @Nullable - public static byte[] getModelId(@Nullable byte[] serviceData) { - if (serviceData == null) { - return null; - } - - if (serviceData.length >= MIN_ID_LENGTH) { - if (serviceData.length == MIN_ID_LENGTH) { - // If the length == 3, all bytes are the ID. See flag docs for more about - // endianness. - return serviceData; - } else { - // Otherwise, the first byte is a header which contains the length of the big-endian - // model ID that follows. The model ID will be trimmed if it contains leading zeros. - int idIndex = 1; - int end = idIndex + getIdLength(serviceData); - while (serviceData[idIndex] == 0 && end - idIndex > MIN_ID_LENGTH) { - idIndex++; - } - return Arrays.copyOfRange(serviceData, idIndex, end); - } - } - return null; - } - - /** Gets the FastPair service data array if available, otherwise returns null. */ - @Nullable - public static byte[] getServiceDataArray(BleRecord bleRecord) { - return bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID); - } - - /** Gets the FastPair service data array if available, otherwise returns null. */ - @Nullable - public static byte[] getServiceDataArray(ScanRecord scanRecord) { - return scanRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID); - } - - /** Gets the bloom filter from the extra fields if available, otherwise returns null. */ - @Nullable - public static byte[] getBloomFilter(@Nullable byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER); - } - - /** Gets the bloom filter salt from the extra fields if available, otherwise returns null. */ - @Nullable - public static byte[] getBloomFilterSalt(byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_SALT); - } - - /** - * Gets the suppress notification with bloom filter from the extra fields if available, - * otherwise returns null. - */ - @Nullable - public static byte[] getBloomFilterNoNotification(@Nullable byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION); - } - - /** - * Get random resolvableData - */ - @Nullable - public static byte[] getRandomResolvableData(byte[] serviceData) { - return getExtraField(serviceData, FIELD_TYPE_RANDOM_RESOLVABLE_DATA); - } - - @Nullable - private static byte[] getExtraField(@Nullable byte[] serviceData, int fieldId) { - if (serviceData == null || serviceData.length < HEADER_INDEX + HEADER_LENGTH) { - return null; - } - try { - return getExtraFields(serviceData).get(fieldId); - } catch (IllegalFormatException e) { - return null; - } - } - - /** Gets extra field data at the end of the packet, defined by the extra field header. */ - private static SparseArray getExtraFields(byte[] serviceData) - throws IllegalFormatException { - SparseArray extraFields = new SparseArray<>(); - if (getVersion(serviceData) != 0) { - return extraFields; - } - int headerIndex = getFirstExtraFieldHeaderIndex(serviceData); - while (headerIndex < serviceData.length) { - int length = getExtraFieldLength(serviceData, headerIndex); - int index = headerIndex + FIELD_HEADER_LENGTH; - int type = getExtraFieldType(serviceData, headerIndex); - int end = index + length; - if (extraFields.get(type) == null) { - if (end <= serviceData.length) { - extraFields.put(type, Arrays.copyOfRange(serviceData, index, end)); - } else { - throw new IllegalFormatException( - "Invalid length, " + end + " is longer than service data size " - + serviceData.length); - } - } - headerIndex = end; - } - return extraFields; - } - - /** Checks whether or not a valid ID is included in the service data packet. */ - public static boolean hasBeaconIdBytes(BleRecord bleRecord) { - byte[] serviceData = bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID); - return checkModelId(serviceData); - } - - /** Check whether byte array is FastPair model id or not. */ - public static boolean checkModelId(@Nullable byte[] scanResult) { - return scanResult != null - // The 3-byte format has no header byte (all bytes are the ID). - && (scanResult.length == MIN_ID_LENGTH - // Header byte exists. We support only format version 0. (A different version - // indicates - // a breaking change in the format.) - || (scanResult.length > MIN_ID_LENGTH - && getVersion(scanResult) == 0 - && isIdLengthValid(scanResult))); - } - - /** Checks whether or not bloom filter is included in the service data packet. */ - public static boolean hasBloomFilter(BleRecord bleRecord) { - return (getBloomFilter(getServiceDataArray(bleRecord)) != null - || getBloomFilterNoNotification(getServiceDataArray(bleRecord)) != null); - } - - /** Checks whether or not bloom filter is included in the service data packet. */ - public static boolean hasBloomFilter(ScanRecord scanRecord) { - return (getBloomFilter(getServiceDataArray(scanRecord)) != null - || getBloomFilterNoNotification(getServiceDataArray(scanRecord)) != null); - } - - private static int getVersion(byte[] serviceData) { - return serviceData.length == MIN_ID_LENGTH - ? 0 - : (serviceData[HEADER_INDEX] & HEADER_VERSION_BITMASK) >> HEADER_VERSION_OFFSET; - } - - private static int getIdLength(byte[] serviceData) { - return serviceData.length == MIN_ID_LENGTH - ? MIN_ID_LENGTH - : (serviceData[HEADER_INDEX] & HEADER_LENGTH_BITMASK) >> HEADER_LENGTH_OFFSET; - } - - private static int getFirstExtraFieldHeaderIndex(byte[] serviceData) { - return HEADER_INDEX + HEADER_LENGTH + getIdLength(serviceData); - } - - private static int getExtraFieldLength(byte[] serviceData, int extraFieldIndex) { - return (serviceData[extraFieldIndex] & EXTRA_FIELD_LENGTH_BITMASK) - >> EXTRA_FIELD_LENGTH_OFFSET; - } - - private static int getExtraFieldType(byte[] serviceData, int extraFieldIndex) { - return (serviceData[extraFieldIndex] & EXTRA_FIELD_TYPE_BITMASK) >> EXTRA_FIELD_TYPE_OFFSET; - } - - private static boolean isIdLengthValid(byte[] serviceData) { - int idLength = getIdLength(serviceData); - return MIN_ID_LENGTH <= idLength - && idLength <= MAX_ID_LENGTH - && idLength + HEADER_LENGTH <= serviceData.length; - } -} - diff --git a/nearby/service/proto/src/fastpair/cache.proto b/nearby/service/proto/src/fastpair/cache.proto deleted file mode 100644 index d4c7c3d02805154d2444a1ae311222c3b7e670fe..0000000000000000000000000000000000000000 --- a/nearby/service/proto/src/fastpair/cache.proto +++ /dev/null @@ -1,427 +0,0 @@ -syntax = "proto3"; -package service.proto; -import "src/fastpair/rpcs.proto"; -import "src/fastpair/fast_pair_string.proto"; - -// db information for Fast Pair that gets from server. -message ServerResponseDbItem { - // Device's model id. - string model_id = 1; - - // Response was received from the server. Contains data needed to display - // FastPair notification such as device name, txPower of device, image used - // in the notification, etc. - GetObservedDeviceResponse get_observed_device_response = 2; - - // The timestamp that make the server fetch. - int64 last_fetch_info_timestamp_millis = 3; - - // Whether the item in the cache is expirable or not (when offline mode this - // will be false). - bool expirable = 4; -} - - -// Client side scan result. -message StoredScanResult { - // REQUIRED - // Unique ID generated based on scan result - string id = 1; - - // REQUIRED - NearbyType type = 2; - - // REQUIRED - // The most recent all upper case mac associated with this item. - // (Mac-to-DiscoveryItem is a many-to-many relationship) - string mac_address = 4; - - // Beacon's RSSI value - int32 rssi = 10; - - // Beacon's tx power - int32 tx_power = 11; - - // The mac address encoded in beacon advertisement. Currently only used by - // chromecast. - string device_setup_mac = 12; - - // Uptime of the device in minutes. Stops incrementing at 255. - int32 uptime_minutes = 13; - - // REQUIRED - // Client timestamp when the beacon was first observed in BLE scan. - int64 first_observation_timestamp_millis = 14; - - // REQUIRED - // Client timestamp when the beacon was last observed in BLE scan. - int64 last_observation_timestamp_millis = 15; - - // Deprecated fields. - reserved 3, 5, 6, 7, 8, 9; -} - - -// Data for a DiscoveryItem created from server response and client scan result. -// Only caching original data from scan result, server response, timestamps -// and user actions. Do not save generated data in this object. -// Next ID: 50 -message StoredDiscoveryItem { - enum State { - // Default unknown state. - STATE_UNKNOWN = 0; - - // The item is normal. - STATE_ENABLED = 1; - - // The item has been muted by user. - STATE_MUTED = 2; - - // The item has been disabled by us (likely temporarily). - STATE_DISABLED_BY_SYSTEM = 3; - } - - // The status of the item. - // TODO(b/204409421) remove enum - enum DebugMessageCategory { - // Default unknown state. - STATUS_UNKNOWN = 0; - - // The item is valid and visible in notification. - STATUS_VALID_NOTIFICATION = 1; - - // The item made it to list but not to notification. - STATUS_VALID_LIST_VIEW = 2; - - // The item is filtered out on client. Never made it to list view. - STATUS_DISABLED_BY_CLIENT = 3; - - // The item is filtered out by server. Never made it to client. - STATUS_DISABLED_BY_SERVER = 4; - } - - enum ExperienceType { - EXPERIENCE_UNKNOWN = 0; - EXPERIENCE_GOOD = 1; - EXPERIENCE_BAD = 2; - } - - // REQUIRED - // Offline item: unique ID generated on client. - // Online item: unique ID generated on server. - string id = 1; - - // REQUIRED - // The most recent all upper case mac associated with this item. - // (Mac-to-DiscoveryItem is a many-to-many relationship) - string mac_address = 4; - - // REQUIRED - string action_url = 5; - - // The bluetooth device name from advertisment - string device_name = 6; - - // REQUIRED - // Item's title - string title = 7; - - // Item's description. - string description = 8; - - // The URL for display - string display_url = 9; - - // REQUIRED - // Client timestamp when the beacon was last observed in BLE scan. - int64 last_observation_timestamp_millis = 10; - - // REQUIRED - // Client timestamp when the beacon was first observed in BLE scan. - int64 first_observation_timestamp_millis = 11; - - // REQUIRED - // Item's current state. e.g. if the item is blocked. - State state = 17; - - // The resolved url type for the action_url. - ResolvedUrlType action_url_type = 19; - - // The timestamp when the user is redirected to Play Store after clicking on - // the item. - int64 pending_app_install_timestamp_millis = 20; - - // Beacon's RSSI value - int32 rssi = 22; - - // Beacon's tx power - int32 tx_power = 23; - - // Human readable name of the app designated to open the uri - // Used in the second line of the notification, "Open in {} app" - string app_name = 25; - - // The timestamp when the attachment was created on PBS server. In case there - // are duplicate - // items with the same scanId/groupID, only show the one with the latest - // timestamp. - int64 attachment_creation_sec = 28; - - // Package name of the App that owns this item. - string package_name = 30; - - // The average star rating of the app. - float star_rating = 31; - - // TriggerId identifies the trigger/beacon that is attached with a message. - // It's generated from server for online messages to synchronize formatting - // across client versions. - // Example: - // * BLE_UID: 3||deadbeef - // * BLE_URL: http://trigger.id - // See go/discovery-store-message-and-trigger-id for more details. - string trigger_id = 34; - - // Bytes of item icon in PNG format displayed in Discovery item list. - bytes icon_png = 36; - - // A FIFE URL of the item icon displayed in Discovery item list. - string icon_fife_url = 49; - - // See equivalent field in NearbyItem. - bytes authentication_public_key_secp256r1 = 45; - - // See equivalent field in NearbyItem. - FastPairInformation fast_pair_information = 46; - - // Companion app detail. - CompanionAppDetails companion_detail = 47; - - // Fast pair strings - FastPairStrings fast_pair_strings = 48; - - // Deprecated fields. - reserved 2, 3, 12, 13, 14, 15, 16, 18, 21, 24, 26, 27, 29, 32, 33, 35, 37, 38, 39, 40, 41, 42, 43, 44; -} -enum ResolvedUrlType { - RESOLVED_URL_TYPE_UNKNOWN = 0; - - // The url is resolved to a web page that is not a play store app. - // This can be considered as the default resolved type when it's - // not the other specific types. - WEBPAGE = 1; - - // The url is resolved to the Google Play store app - // ie. play.google.com/store - APP = 2; -} -enum DiscoveryAttachmentType { - DISCOVERY_ATTACHMENT_TYPE_UNKNOWN = 0; - - // The attachment is posted in the prod namespace (without "-debug") - DISCOVERY_ATTACHMENT_TYPE_NORMAL = 1; - - // The attachment is posted in the debug namespace (with "-debug") - DISCOVERY_ATTACHMENT_TYPE_DEBUG = 2; -} -// Additional information relevant only for Fast Pair devices. -message FastPairInformation { - // When true, Fast Pair will only create a bond with the device and not - // attempt to connect any profiles (for example, A2DP or HFP). - bool data_only_connection = 1; - - // Additional images that are attached specifically for true wireless Fast - // Pair devices. - TrueWirelessHeadsetImages true_wireless_images = 3; - - // When true, this device can support assistant function. - bool assistant_supported = 4; - - // Features supported by the Fast Pair device. - repeated FastPairFeature features = 5; - - // Optional, the name of the company producing this Fast Pair device. - string company_name = 6; - - // Optional, the type of device. - DeviceType device_type = 7; - - reserved 2; -} - - -enum NearbyType { - NEARBY_TYPE_UNKNOWN = 0; - // Proximity Beacon Service (PBS). This is the only type of nearbyItems which - // can be customized by 3p and therefore the intents passed should not be - // completely trusted. Deprecated already. - NEARBY_PROXIMITY_BEACON = 1; - // Physical Web URL beacon. Deprecated already. - NEARBY_PHYSICAL_WEB = 2; - // Chromecast beacon. Used on client-side only. - NEARBY_CHROMECAST = 3; - // Wear beacon. Used on client-side only. - NEARBY_WEAR = 4; - // A device (e.g. a Magic Pair device that needs to be set up). The special- - // case devices above (e.g. ChromeCast, Wear) might migrate to this type. - NEARBY_DEVICE = 6; - // Popular apps/urls based on user's current geo-location. - NEARBY_POPULAR_HERE = 7; - - reserved 5; -} - -// A locally cached Fast Pair device associating an account key with the -// bluetooth address of the device. -message StoredFastPairItem { - // The device's public mac address. - string mac_address = 1; - - // The account key written to the device. - bytes account_key = 2; - - // When user need to update provider name, enable this value to trigger - // writing new name to provider. - bool need_to_update_provider_name = 3; - - // The retry times to update name into provider. - int32 update_name_retries = 4; - - // Latest firmware version from the server. - string latest_firmware_version = 5; - - // The firmware version that is on the device. - string device_firmware_version = 6; - - // The timestamp from the last time we fetched the firmware version from the - // device. - int64 last_check_firmware_timestamp_millis = 7; - - // The timestamp from the last time we fetched the firmware version from - // server. - int64 last_server_query_timestamp_millis = 8; - - // Only allows one bloom filter check process to create gatt connection and - // try to read the firmware version value. - bool can_read_firmware = 9; - - // Device's model id. - string model_id = 10; - - // Features that this Fast Pair device supports. - repeated FastPairFeature features = 11; - - // Keeps the stored discovery item in local cache, we can have most - // information of fast pair device locally without through footprints, i.e. we - // can have most fast pair features locally. - StoredDiscoveryItem discovery_item = 12; - - // When true, the latest uploaded event to FMA is connected. We use - // it as the previous ACL state when getting the BluetoothAdapter STATE_OFF to - // determine if need to upload the disconnect event to FMA. - bool fma_state_is_connected = 13; - - // Device's buffer size range. - repeated BufferSizeRange buffer_size_range = 18; - - // The additional account key if this device could be associated with multiple - // accounts. Notes that for this device, the account_key field is the basic - // one which will not be associated with the accounts. - repeated bytes additional_account_key = 19; - - // Deprecated fields. - reserved 14, 15, 16, 17; -} - -// Contains information about Fast Pair devices stored through our scanner. -// Next ID: 29 -message ScanFastPairStoreItem { - // Device's model id. - string model_id = 1; - - // Device's RSSI value - int32 rssi = 2; - - // Device's tx power - int32 tx_power = 3; - - // Bytes of item icon in PNG format displayed in Discovery item list. - bytes icon_png = 4; - - // A FIFE URL of the item icon displayed in Discovery item list. - string icon_fife_url = 28; - - // Device name like "Bose QC 35". - string device_name = 5; - - // Client timestamp when user last saw Fast Pair device. - int64 last_observation_timestamp_millis = 6; - - // Action url after user click the notification. - string action_url = 7; - - // Device's bluetooth address. - string address = 8; - - // The computed threshold rssi value that would trigger FastPair notifications - int32 threshold_rssi = 9; - - // Populated with the contents of the bloom filter in the event that - // the scanned device is advertising a bloom filter instead of a model id - bytes bloom_filter = 10; - - // Device name from the BLE scan record - string ble_device_name = 11; - - // Strings used for the FastPair UI - FastPairStrings fast_pair_strings = 12; - - // A key used to authenticate advertising device. - // See NearbyItem.authentication_public_key_secp256r1 for more information. - bytes anti_spoofing_public_key = 13; - - // When true, Fast Pair will only create a bond with the device and not - // attempt to connect any profiles (for example, A2DP or HFP). - bool data_only_connection = 14; - - // The type of the manufacturer (first party, third party, etc). - int32 manufacturer_type_num = 15; - - // Additional images that are attached specifically for true wireless Fast - // Pair devices. - TrueWirelessHeadsetImages true_wireless_images = 16; - - // When true, this device can support assistant function. - bool assistant_supported = 17; - - // Optional, the name of the company producing this Fast Pair device. - string company_name = 18; - - // Features supported by the Fast Pair device. - FastPairFeature features = 19; - - // The interaction type that this scan should trigger - InteractionType interaction_type = 20; - - // The copy of the advertisement bytes, used to pass along to other - // apps that use Fast Pair as the discovery vehicle. - bytes full_ble_record = 21; - - // Companion app related information - CompanionAppDetails companion_detail = 22; - - // Client timestamp when user first saw Fast Pair device. - int64 first_observation_timestamp_millis = 23; - - // The type of the device (wearable, headphones, etc). - int32 device_type_num = 24; - - // The type of notification (app launch smart setup, etc). - NotificationType notification_type = 25; - - // The customized title. - string customized_title = 26; - - // The customized description. - string customized_description = 27; -} diff --git a/nearby/service/proto/src/fastpair/data.proto b/nearby/service/proto/src/fastpair/data.proto deleted file mode 100644 index 6f4faddd1f189eddcd1694b3a0052b29bddb9677..0000000000000000000000000000000000000000 --- a/nearby/service/proto/src/fastpair/data.proto +++ /dev/null @@ -1,26 +0,0 @@ -syntax = "proto3"; - -package service.proto; -import "src/fastpair/cache.proto"; - -// A device that has been Fast Paired with. -message FastPairDeviceWithAccountKey { - // The account key which was written to the device after pairing completed. - bytes account_key = 1; - - // The stored discovery item which represents the notification that should be - // associated with the device. Note, this is stored as a raw byte array - // instead of StoredDiscoveryItem because icing only supports proto lite and - // StoredDiscoveryItem is handed around as a nano proto in implementation, - // which are not compatible with each other. - StoredDiscoveryItem discovery_item = 3; - - // SHA256 of "account key + headset's public address", this is used to - // identify the paired headset. Because of adding account key to generate the - // hash value, it makes the information anonymous, even for the same headset, - // different accounts have different values. - bytes sha256_account_key_public_address = 4; - - // Deprecated fields. - reserved 2; -} diff --git a/nearby/service/proto/src/fastpair/fast_pair_string.proto b/nearby/service/proto/src/fastpair/fast_pair_string.proto deleted file mode 100644 index f318c1aec3e079dcbadb3e2d9d8565310e645525..0000000000000000000000000000000000000000 --- a/nearby/service/proto/src/fastpair/fast_pair_string.proto +++ /dev/null @@ -1,40 +0,0 @@ -syntax = "proto2"; - -package service.proto; - -message FastPairStrings { - // Required for initial pairing, used when there is a Google account on the - // device - optional string tap_to_pair_with_account = 1; - - // Required for initial pairing, used when there is no Google account on the - // device - optional string tap_to_pair_without_account = 2; - - // Description for initial pairing - optional string initial_pairing_description = 3; - - // Description after successfully paired the device with companion app - // installed - optional string pairing_finished_companion_app_installed = 4; - - // Description after successfully paired the device with companion app not - // installed - optional string pairing_finished_companion_app_not_installed = 5; - - // Description when phone found the device that associates with user's account - // before remind user to pair with new device. - optional string subsequent_pairing_description = 6; - - // Description when fast pair finds the user paired with device manually - // reminds user to opt the device into cloud. - optional string retroactive_pairing_description = 7; - - // Description when user click setup device while device is still pairing - optional string wait_app_launch_description = 8; - - // Description when user fail to pair with device - optional string pairing_fail_description = 9; - - reserved 10, 11, 12, 13, 14,15, 16, 17, 18; -} diff --git a/nearby/service/proto/src/fastpair/rpcs.proto b/nearby/service/proto/src/fastpair/rpcs.proto deleted file mode 100644 index bce4378f77455235c012b60486977b2da9d9ea7c..0000000000000000000000000000000000000000 --- a/nearby/service/proto/src/fastpair/rpcs.proto +++ /dev/null @@ -1,301 +0,0 @@ -// RPCs for the Nearby Console service. -syntax = "proto3"; - -package service.proto; -// Response containing an observed device. -message GetObservedDeviceResponse { - // The device from the request. - Device device = 1; - - // The image icon that shows in the notification - bytes image = 3; - - // Strings to be displayed on notifications during the pairing process. - ObservedDeviceStrings strings = 4; - - reserved 2; -} - -message Device { - // Output only. The server-generated ID of the device. - int64 id = 1; - - // The pantheon project number the device is created under. Only Nearby admins - // can change this. - int64 project_number = 2; - - // How the notification will be displayed to the user - NotificationType notification_type = 3; - - // The image to show on the notification. - string image_url = 4; - - // The name of the device. - string name = 5; - - // The intent that will be launched via the notification. - string intent_uri = 6; - - // The transmit power of the device's BLE chip. - int32 ble_tx_power = 7; - - // The distance that the device must be within to show a notification. - // If no distance is set, we default to 0.6 meters. Only Nearby admins can - // change this. - float trigger_distance = 8; - - // Output only. Fast Pair only - The anti-spoofing key pair for the device. - AntiSpoofingKeyPair anti_spoofing_key_pair = 9; - - // Output only. The current status of the device. - Status status = 10; - - - // DEPRECATED - check for published_version instead. - // Output only. - // Whether the device has a different, already published version. - bool has_published_version = 12; - - // Fast Pair only - The type of device being registered. - DeviceType device_type = 13; - - - // Fast Pair only - Additional images for true wireless headsets. - TrueWirelessHeadsetImages true_wireless_images = 15; - - // Fast Pair only - When true, this device can support assistant function. - bool assistant_supported = 16; - - // Output only. - // The published version of a device that has been approved to be displayed - // as a notification - only populated if the device has a different published - // version. (A device that only has a published version would not have this - // populated). - Device published_version = 17; - - // Fast Pair only - When true, Fast Pair will only create a bond with the - // device and not attempt to connect any profiles (for example, A2DP or HFP). - bool data_only_connection = 18; - - // Name of the company/brand that will be selling the product. - string company_name = 19; - - repeated FastPairFeature features = 20; - - // Name of the device that is displayed on the console. - string display_name = 21; - - // How the device will be interacted with by the user when the scan record - // is detected. - InteractionType interaction_type = 22; - - // Companion app information. - CompanionAppDetails companion_detail = 23; - - reserved 11, 14; -} - - -// Represents the format of the final device notification (which is directly -// correlated to the action taken by the notification). -enum NotificationType { - // Unspecified notification type. - NOTIFICATION_TYPE_UNSPECIFIED = 0; - // Notification launches the fast pair intent. - // Example Notification Title: "Bose SoundLink II" - // Notification Description: "Tap to pair with this device" - FAST_PAIR = 1; - // Notification launches an app. - // Notification Title: "[X]" where X is type/name of the device. - // Notification Description: "Tap to setup this device" - APP_LAUNCH = 2; - // Notification launches for Nearby Setup. The notification title and - // description is the same as APP_LAUNCH. - NEARBY_SETUP = 3; - // Notification launches the fast pair intent, but doesn't include an anti- - // spoofing key. The notification title and description is the same as - // FAST_PAIR. - FAST_PAIR_ONE = 4; - // Notification launches Smart Setup on devices. - // These notifications are identical to APP_LAUNCH except that they always - // launch Smart Setup intents within GMSCore. - SMART_SETUP = 5; -} - -// How the device will be interacted with when it is seen. -enum InteractionType { - INTERACTION_TYPE_UNKNOWN = 0; - AUTO_LAUNCH = 1; - NOTIFICATION = 2; -} - -// Features that can be enabled for a Fast Pair device. -enum FastPairFeature { - FAST_PAIR_FEATURE_UNKNOWN = 0; - SILENCE_MODE = 1; - WIRELESS_CHARGING = 2; - DYNAMIC_BUFFER_SIZE = 3; - NO_PERSONALIZED_NAME = 4; - EDDYSTONE_TRACKING = 5; -} - -message CompanionAppDetails { - // Companion app slice provider's authority. - string authority = 1; - - // Companion app certificate value. - string certificate_hash = 2; - - // Deprecated fields. - reserved 3; -} - -// Additional images for True Wireless Fast Pair devices. -message TrueWirelessHeadsetImages { - // Image URL for the left bud. - string left_bud_url = 1; - - // Image URL for the right bud. - string right_bud_url = 2; - - // Image URL for the case. - string case_url = 3; -} - -// Represents the type of device that is being registered. -enum DeviceType { - DEVICE_TYPE_UNSPECIFIED = 0; - HEADPHONES = 1; - SPEAKER = 2; - WEARABLE = 3; - INPUT_DEVICE = 4; - AUTOMOTIVE = 5; - OTHER = 6; - TRUE_WIRELESS_HEADPHONES = 7; - WEAR_OS = 8; - ANDROID_AUTO = 9; -} - -// An anti-spoofing key pair for a device that allows us to verify the device is -// broadcasting legitimately. -message AntiSpoofingKeyPair { - // The private key (restricted to only be viewable by trusted clients). - bytes private_key = 1; - - // The public key. - bytes public_key = 2; -} - -// Various states that a customer-configured device notification can be in. -// PUBLISHED is the only state that shows notifications to the public. -message Status { - // Status types available for each device. - enum StatusType { - // Unknown status. - TYPE_UNSPECIFIED = 0; - // Drafted device. - DRAFT = 1; - // Submitted and waiting for approval. - SUBMITTED = 2; - // Fully approved and available for end users. - PUBLISHED = 3; - // Rejected and not available for end users. - REJECTED = 4; - } - - // Details about a device that has been rejected. - message RejectionDetails { - // The reason for the rejection. - enum RejectionReason { - // Unspecified reason. - REASON_UNSPECIFIED = 0; - // Name is not valid. - NAME = 1; - // Image is not valid. - IMAGE = 2; - // Tests have failed. - TESTS = 3; - // Other reason. - OTHER = 4; - } - - // A list of reasons the device was rejected. - repeated RejectionReason reasons = 1; - // Comment about an OTHER rejection reason. - string additional_comment = 2; - } - - // The status of the device. - StatusType status_type = 1; - - // Accompanies Status.REJECTED. - RejectionDetails rejection_details = 2; -} - -// Strings to be displayed in notifications surfaced for a device. -message ObservedDeviceStrings { - // The notification description for when the device is initially discovered. - string initial_notification_description = 2; - - // The notification description for when the device is initially discovered - // and no account is logged in. - string initial_notification_description_no_account = 3; - - // The notification description for once we have finished pairing and the - // companion app has been opened. For google assistant devices, this string will point - // users to setting up the assistant. - string open_companion_app_description = 4; - - // The notification description for once we have finished pairing and the - // companion app needs to be updated before use. - string update_companion_app_description = 5; - - // The notification description for once we have finished pairing and the - // companion app needs to be installed. - string download_companion_app_description = 6; - - // The notification title when a pairing fails. - string unable_to_connect_title = 7; - - // The notification summary when a pairing fails. - string unable_to_connect_description = 8; - - // The description that helps user initially paired with device. - string initial_pairing_description = 9; - - // The description that let user open the companion app. - string connect_success_companion_app_installed = 10; - - // The description that let user download the companion app. - string connect_success_companion_app_not_installed = 11; - - // The description that reminds user there is a paired device nearby. - string subsequent_pairing_description = 12; - - // The description that reminds users opt in their device. - string retroactive_pairing_description = 13; - - // The description that indicates companion app is about to launch. - string wait_launch_companion_app_description = 14; - - // The description that indicates go to bluetooth settings when connection - // fail. - string fail_connect_go_to_settings_description = 15; - - reserved 1, 16, 17, 18, 19, 20, 21, 22, 23, 24; -} - -// The buffer size range of a Fast Pair devices support dynamic buffer size. -message BufferSizeRange { - // The max buffer size in ms. - int32 max_size = 1; - - // The min buffer size in ms. - int32 min_size = 2; - - // The default buffer size in ms. - int32 default_size = 3; - - // The codec of this buffer size range. - int32 codec = 4; -} diff --git a/nearby/tests/integration/privileged/src/android/nearby/integration/privileged/FastPairSettingsProviderTest.kt b/nearby/tests/integration/privileged/src/android/nearby/integration/privileged/FastPairSettingsProviderTest.kt deleted file mode 100644 index af3f75f4f7caa7c631e98b3e165775b0de475863..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/privileged/src/android/nearby/integration/privileged/FastPairSettingsProviderTest.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.privileged - -import android.content.Context -import android.provider.Settings -import androidx.test.core.app.ApplicationProvider -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters - -data class FastPairSettingsFlag(val name: String, val value: Int) { - override fun toString() = name -} - -@RunWith(Parameterized::class) -class FastPairSettingsProviderTest(private val flag: FastPairSettingsFlag) { - - /** Verify privileged app can enable/disable Fast Pair scan. */ - @Test - fun testSettingsFastPairScan_fromPrivilegedApp() { - val appContext = ApplicationProvider.getApplicationContext() - val contentResolver = appContext.contentResolver - - Settings.Secure.putInt(contentResolver, "fast_pair_scan_enabled", flag.value) - - val actualValue = Settings.Secure.getInt( - contentResolver, "fast_pair_scan_enabled", /* default value */ -1) - assertThat(actualValue).isEqualTo(flag.value) - } - - companion object { - @JvmStatic - @Parameters(name = "{0}Succeed") - fun fastPairScanFlags() = listOf( - FastPairSettingsFlag(name = "disable", value = 0), - FastPairSettingsFlag(name = "enable", value = 1), - ) - } -} diff --git a/nearby/tests/integration/ui/Android.bp b/nearby/tests/integration/ui/Android.bp deleted file mode 100644 index 524c838ade6639d2de3330fe9f57a8441452418f..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/Android.bp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_test { - name: "NearbyIntegrationUiTests", - defaults: ["mts-target-sdk-version-current"], - sdk_version: "test_current", - static_libs: ["NearbyIntegrationUiTestsLib"], - test_suites: ["device-tests"], -} - -android_library { - name: "NearbyIntegrationUiTestsLib", - srcs: ["src/**/*.kt"], - sdk_version: "test_current", - static_libs: [ - "androidx.test.ext.junit", - "androidx.test.rules", - "androidx.test.uiautomator_uiautomator", - "junit", - "platform-test-rules", - "service-nearby-pre-jarjar", - "truth-prebuilt", - ], -} diff --git a/nearby/tests/integration/ui/AndroidManifest.xml b/nearby/tests/integration/ui/AndroidManifest.xml deleted file mode 100644 index 9aea0c1cba09d62436a7d66b6e6a4a6fe6dddc98..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - diff --git a/nearby/tests/integration/ui/AndroidTest.xml b/nearby/tests/integration/ui/AndroidTest.xml deleted file mode 100644 index 9dfcf7b1885e550ddfa0a0be50eb54ce547e348a..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/AndroidTest.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - diff --git a/nearby/tests/integration/ui/src/android/nearby/integration/ui/BaseUiTest.kt b/nearby/tests/integration/ui/src/android/nearby/integration/ui/BaseUiTest.kt deleted file mode 100644 index 658775b7db9fe2bf662f5ed606036c15f4b4a8b7..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/src/android/nearby/integration/ui/BaseUiTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.ui - -import android.platform.test.rule.ArtifactSaver -import android.platform.test.rule.ScreenRecordRule -import android.platform.test.rule.TestWatcher -import org.junit.Rule -import org.junit.rules.TestRule -import org.junit.rules.Timeout -import org.junit.runner.Description - -abstract class BaseUiTest { - @get:Rule - var mGlobalTimeout: Timeout = Timeout.seconds(100) // Test times out in 1.67 minutes - - @get:Rule - val mTestWatcherRule: TestRule = object : TestWatcher() { - override fun failed(throwable: Throwable?, description: Description?) { - super.failed(throwable, description) - ArtifactSaver.onError(description, throwable) - } - } - - @get:Rule - val mScreenRecordRule: TestRule = ScreenRecordRule() -} \ No newline at end of file diff --git a/nearby/tests/integration/ui/src/android/nearby/integration/ui/CheckNearbyHalfSheetUiTest.kt b/nearby/tests/integration/ui/src/android/nearby/integration/ui/CheckNearbyHalfSheetUiTest.kt deleted file mode 100644 index 5a3538e15e8d1b1ba90e9c4f2df18c50ba8bd538..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/src/android/nearby/integration/ui/CheckNearbyHalfSheetUiTest.kt +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.ui - -import android.content.Context -import android.os.Bundle -import android.platform.test.rule.ScreenRecordRule.ScreenRecord -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import androidx.test.uiautomator.Until -import com.android.server.nearby.common.eventloop.EventLoop -import com.android.server.nearby.common.locator.Locator -import com.android.server.nearby.common.locator.LocatorContextWrapper -import com.android.server.nearby.fastpair.FastPairController -import com.android.server.nearby.fastpair.cache.FastPairCacheManager -import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager -import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager -import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth.assertWithMessage -import org.junit.AfterClass -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import service.proto.Cache -import service.proto.FastPairString.FastPairStrings -import java.time.Clock - -/** An instrumented test to check Nearby half sheet UI showed correctly. - * - * To run this test directly: - * am instrument -w -r \ - * -e class android.nearby.integration.ui.CheckNearbyHalfSheetUiTest \ - * android.nearby.integration.ui/androidx.test.runner.AndroidJUnitRunner - */ -@RunWith(AndroidJUnit4::class) -class CheckNearbyHalfSheetUiTest : BaseUiTest() { - private var waitHalfSheetPopupTimeoutMs: Long - private var halfSheetTitleText: String - private var halfSheetSubtitleText: String - - init { - val arguments: Bundle = InstrumentationRegistry.getArguments() - waitHalfSheetPopupTimeoutMs = arguments.getLong( - WAIT_HALF_SHEET_POPUP_TIMEOUT_KEY, - DEFAULT_WAIT_HALF_SHEET_POPUP_TIMEOUT_MS - ) - halfSheetTitleText = - arguments.getString(HALF_SHEET_TITLE_KEY, DEFAULT_HALF_SHEET_TITLE_TEXT) - halfSheetSubtitleText = - arguments.getString(HALF_SHEET_SUBTITLE_KEY, DEFAULT_HALF_SHEET_SUBTITLE_TEXT) - - device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - } - - /** For multidevice test snippet only. Force overwrites the test arguments. */ - fun updateTestArguments( - waitHalfSheetPopupTimeoutSeconds: Int, - halfSheetTitleText: String, - halfSheetSubtitleText: String - ) { - this.waitHalfSheetPopupTimeoutMs = waitHalfSheetPopupTimeoutSeconds * 1000L - this.halfSheetTitleText = halfSheetTitleText - this.halfSheetSubtitleText = halfSheetSubtitleText - } - - @Before - fun setUp() { - val appContext = ApplicationProvider.getApplicationContext() - val locator = Locator(appContext).apply { - overrideBindingForTest(EventLoop::class.java, EventLoop.newInstance("test")) - overrideBindingForTest( - FastPairCacheManager::class.java, - FastPairCacheManager(appContext) - ) - overrideBindingForTest(FootprintsDeviceManager::class.java, FootprintsDeviceManager()) - overrideBindingForTest(Clock::class.java, Clock.systemDefaultZone()) - } - val locatorContextWrapper = LocatorContextWrapper(appContext, locator) - locator.overrideBindingForTest( - FastPairController::class.java, - FastPairController(locatorContextWrapper) - ) - val scanFastPairStoreItem = Cache.ScanFastPairStoreItem.newBuilder() - .setDeviceName(DEFAULT_HALF_SHEET_TITLE_TEXT) - .setFastPairStrings( - FastPairStrings.newBuilder() - .setInitialPairingDescription(DEFAULT_HALF_SHEET_SUBTITLE_TEXT).build() - ) - .build() - FastPairHalfSheetManager(locatorContextWrapper).showHalfSheet(scanFastPairStoreItem) - } - - @Test - @ScreenRecord - fun checkNearbyHalfSheetUi() { - // Check Nearby half sheet showed by checking button "Connect" on the DevicePairingFragment. - val isConnectButtonShowed = device.wait( - Until.hasObject(NearbyHalfSheetUiMap.DevicePairingFragment.connectButton), - waitHalfSheetPopupTimeoutMs - ) - assertWithMessage("Nearby half sheet didn't show within $waitHalfSheetPopupTimeoutMs ms.") - .that(isConnectButtonShowed).isTrue() - - val halfSheetTitle = - device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.halfSheetTitle) - assertThat(halfSheetTitle).isNotNull() - assertThat(halfSheetTitle.text).isEqualTo(halfSheetTitleText) - - val halfSheetSubtitle = - device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.halfSheetSubtitle) - assertThat(halfSheetSubtitle).isNotNull() - assertThat(halfSheetSubtitle.text).isEqualTo(halfSheetSubtitleText) - - val deviceImage = device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.deviceImage) - assertThat(deviceImage).isNotNull() - - val infoButton = device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.infoButton) - assertThat(infoButton).isNotNull() - } - - companion object { - private const val DEFAULT_WAIT_HALF_SHEET_POPUP_TIMEOUT_MS = 30 * 1000L - private const val DEFAULT_HALF_SHEET_TITLE_TEXT = "Fast Pair Provider Simulator" - private const val DEFAULT_HALF_SHEET_SUBTITLE_TEXT = "Fast Pair Provider Simulator will " + - "appear on devices linked with nearby-mainline-fpseeker@google.com" - private const val WAIT_HALF_SHEET_POPUP_TIMEOUT_KEY = "WAIT_HALF_SHEET_POPUP_TIMEOUT_MS" - private const val HALF_SHEET_TITLE_KEY = "HALF_SHEET_TITLE" - private const val HALF_SHEET_SUBTITLE_KEY = "HALF_SHEET_SUBTITLE" - private lateinit var device: UiDevice - - @AfterClass - @JvmStatic - fun teardownClass() { - // Cleans up after saving screenshot in TestWatcher, leaves nothing dirty behind. - DismissNearbyHalfSheetUiTest().dismissHalfSheet() - } - } -} \ No newline at end of file diff --git a/nearby/tests/integration/ui/src/android/nearby/integration/ui/DismissNearbyHalfSheetUiTest.kt b/nearby/tests/integration/ui/src/android/nearby/integration/ui/DismissNearbyHalfSheetUiTest.kt deleted file mode 100644 index 52d202aa44c0c18753fc6729d65e2fbae3bed480..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/src/android/nearby/integration/ui/DismissNearbyHalfSheetUiTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.ui - -import android.platform.test.rule.ScreenRecordRule.ScreenRecord -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import com.google.common.truth.Truth.assertWithMessage -import org.junit.Test -import org.junit.runner.RunWith - -/** An instrumented test to dismiss Nearby half sheet UI. - * - * To run this test directly: - * am instrument -w -r \ - * -e class android.nearby.integration.ui.DismissNearbyHalfSheetUiTest \ - * android.nearby.integration.ui/androidx.test.runner.AndroidJUnitRunner - */ -@RunWith(AndroidJUnit4::class) -class DismissNearbyHalfSheetUiTest : BaseUiTest() { - private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - - @Test - @ScreenRecord - fun dismissHalfSheet() { - device.pressHome() - device.waitForIdle() - - assertWithMessage("Fail to dismiss Nearby half sheet.").that( - device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.connectButton) - ).isNull() - } -} \ No newline at end of file diff --git a/nearby/tests/integration/ui/src/android/nearby/integration/ui/NearbyHalfSheetUiMap.kt b/nearby/tests/integration/ui/src/android/nearby/integration/ui/NearbyHalfSheetUiMap.kt deleted file mode 100644 index 8b19d5c595887e9997d722dd337962faebb3e562..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/src/android/nearby/integration/ui/NearbyHalfSheetUiMap.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.ui - -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY -import android.content.pm.PackageManager.ResolveInfoFlags -import android.content.pm.ResolveInfo -import android.util.Log -import androidx.test.core.app.ApplicationProvider -import androidx.test.uiautomator.By -import androidx.test.uiautomator.BySelector -import com.android.server.nearby.fastpair.FastPairManager -import com.android.server.nearby.util.Environment -import com.google.common.truth.Truth.assertThat - -/** UiMap for Nearby Mainline Half Sheet. */ -object NearbyHalfSheetUiMap { - private val PACKAGE_NAME: String = getHalfSheetApkPkgName() - private const val ANDROID_WIDGET_BUTTON = "android.widget.Button" - private const val ANDROID_WIDGET_IMAGE_VIEW = "android.widget.ImageView" - private const val ANDROID_WIDGET_TEXT_VIEW = "android.widget.TextView" - - object DevicePairingFragment { - val halfSheetTitle: BySelector = - By.res(PACKAGE_NAME, "toolbar_title").clazz(ANDROID_WIDGET_TEXT_VIEW) - val halfSheetSubtitle: BySelector = - By.res(PACKAGE_NAME, "header_subtitle").clazz(ANDROID_WIDGET_TEXT_VIEW) - val deviceImage: BySelector = - By.res(PACKAGE_NAME, "pairing_pic").clazz(ANDROID_WIDGET_IMAGE_VIEW) - val connectButton: BySelector = - By.res(PACKAGE_NAME, "connect_btn").clazz(ANDROID_WIDGET_BUTTON).text("Connect") - val infoButton: BySelector = - By.res(PACKAGE_NAME, "info_icon").clazz(ANDROID_WIDGET_IMAGE_VIEW) - } - - // Vendors might override HalfSheetUX in their vendor partition, query the package name - // instead of hard coding. ex: Google overrides it in vendor/google/modules/TetheringGoogle. - fun getHalfSheetApkPkgName(): String { - val appContext = ApplicationProvider.getApplicationContext() - val resolveInfos: MutableList = - appContext.packageManager.queryIntentActivities( - Intent(FastPairManager.ACTION_RESOURCES_APK), - ResolveInfoFlags.of(MATCH_SYSTEM_ONLY.toLong()) - ) - - // remove apps that don't live in the nearby apex - resolveInfos.removeIf { !Environment.isAppInNearbyApex(it.activityInfo.applicationInfo) } - - assertThat(resolveInfos).hasSize(1) - - val halfSheetApkPkgName: String = resolveInfos[0].activityInfo.applicationInfo.packageName - Log.i("NearbyHalfSheetUiMap", "Found half-sheet APK at: $halfSheetApkPkgName") - return halfSheetApkPkgName - } -} \ No newline at end of file diff --git a/nearby/tests/integration/ui/src/android/nearby/integration/ui/PairByNearbyHalfSheetUiTest.kt b/nearby/tests/integration/ui/src/android/nearby/integration/ui/PairByNearbyHalfSheetUiTest.kt deleted file mode 100644 index 27264b519aaf8d09d866f1307dbb8d3dda06b30c..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/ui/src/android/nearby/integration/ui/PairByNearbyHalfSheetUiTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.ui - -import android.platform.test.rule.ScreenRecordRule.ScreenRecord -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import androidx.test.uiautomator.Until -import org.junit.AfterClass -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -/** An instrumented test to start pairing by interacting with Nearby half sheet UI. - * - * To run this test directly: - * am instrument -w -r \ - * -e class android.nearby.integration.ui.PairByNearbyHalfSheetUiTest \ - * android.nearby.integration.ui/androidx.test.runner.AndroidJUnitRunner - */ -@RunWith(AndroidJUnit4::class) -class PairByNearbyHalfSheetUiTest : BaseUiTest() { - init { - device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - } - - @Before - fun setUp() { - CheckNearbyHalfSheetUiTest().apply { - setUp() - checkNearbyHalfSheetUi() - } - } - - @Test - @ScreenRecord - fun clickConnectButton() { - val connectButton = NearbyHalfSheetUiMap.DevicePairingFragment.connectButton - device.findObject(connectButton).click() - device.wait(Until.gone(connectButton), CONNECT_BUTTON_TIMEOUT_MILLS) - } - - companion object { - private const val CONNECT_BUTTON_TIMEOUT_MILLS = 3000L - private lateinit var device: UiDevice - - @AfterClass - @JvmStatic - fun teardownClass() { - // Cleans up after saving screenshot in TestWatcher, leaves nothing dirty behind. - device.pressBack() - DismissNearbyHalfSheetUiTest().dismissHalfSheet() - } - } -} \ No newline at end of file diff --git a/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/FastPairSettingsProviderTest.kt b/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/FastPairSettingsProviderTest.kt deleted file mode 100644 index c549073edba9f0c999b93dd52a601ba494273611..0000000000000000000000000000000000000000 --- a/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/FastPairSettingsProviderTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.integration.untrusted - -import android.content.Context -import android.content.ContentResolver -import android.provider.Settings -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertFailsWith - - -@RunWith(AndroidJUnit4::class) -class FastPairSettingsProviderTest { - private lateinit var contentResolver: ContentResolver - - @Before - fun setUp() { - contentResolver = ApplicationProvider.getApplicationContext().contentResolver - } - - /** Verify untrusted app can read Fast Pair scan enabled setting. */ - @Test - fun testSettingsFastPairScan_fromUnTrustedApp_readsSucceed() { - Settings.Secure.getInt(contentResolver, - "fast_pair_scan_enabled", /* default value */ -1) - } - - /** Verify untrusted app can't write Fast Pair scan enabled setting. */ - @Test - fun testSettingsFastPairScan_fromUnTrustedApp_writesFailed() { - assertFailsWith { - Settings.Secure.putInt(contentResolver, "fast_pair_scan_enabled", 1) - } - } -} diff --git a/nearby/tests/multidevices/OWNERS b/nearby/tests/multidevices/OWNERS deleted file mode 100644 index f4dbde2621b1d9258a6f1da90b0a9ed6a0ab3e38..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Bug component: 1092133 - -ericth@google.com -ryancllin@google.com \ No newline at end of file diff --git a/nearby/tests/multidevices/README.md b/nearby/tests/multidevices/README.md deleted file mode 100644 index 9d086de74d70ca7dae584e97243d3543833dfea2..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/README.md +++ /dev/null @@ -1,155 +0,0 @@ -# Nearby Mainline Fast Pair end-to-end tests - -This document refers to the Mainline Fast Pair project source code in the -packages/modules/Connectivity/nearby. This is not an officially supported Google -product. - -## About the Fast Pair Project - -The Connectivity Nearby mainline module is created in the Android T to host -Better Together related functionality. Fast Pair is one of the main -functionalities to provide seamless onboarding and integrated experiences for -peripheral devices (for example, headsets like Google Pixel Buds) in the Nearby -component. - -## Fully automated test - -### Prerequisites - -The fully automated end-to-end (e2e) tests are host-driven tests (which means -test logics are in the host test scripts) using Mobly runner in Python. The two -phones are installed with the test snippet -`NearbyMultiDevicesClientsSnippets.apk` in the test time to let the host scripts -control both sides for testing. Here's the overview of the test environment. - -Workstation (runs Python test scripts and controls Android devices through USB -ADB) \ -├── Phone 1: As Fast Pair seeker role, to scan, pair Fast Pair devices nearby \ -└── Phone 2: As Fast Pair provider role, to simulate a Fast Pair device (for -example, a Bluetooth headset) - -Note: These two phones need to be physically within 0.3 m of each other. - -### Prepare Phone 1 (Fast Pair seeker role) - -This is the phone to scan/pair Fast Pair devices nearby using the Nearby -Mainline module. Test it by flashing with the Android T ROM. - -### Prepare Phone 2 (Fast Pair provider role) - -This is the phone to simulate a Fast Pair device (for example, a Bluetooth -headset). Flash it with a customized ROM with the following changes: - -* Adjust Bluetooth profile configurations. \ - The Fast Pair provider simulator is an opposite role to the seeker. It needs - to enable/disable the following Bluetooth profile: - * Disable A2DP source (bluetooth.profile.a2dp.source.enabled) - * Enable A2DP sink (bluetooth.profile.a2dp.sink.enabled) - * Disable the AVRCP controller (bluetooth.profile.avrcp.controller.enabled) - * Enable the AVRCP target (bluetooth.profile.avrcp.target.enabled) - * Enable the HFP service (bluetooth.profile.hfp.ag.enabled, bluetooth.profile.hfp.hf.enabled) - -```makefile -# The Bluetooth profiles that Fast Pair provider simulator expect to have enabled. -PRODUCT_PRODUCT_PROPERTIES += \ - bluetooth.device.default_name=FastPairProviderSimulator \ - bluetooth.profile.a2dp.source.enabled=false \ - bluetooth.profile.a2dp.sink.enabled=true \ - bluetooth.profile.avrcp.controller.enabled=false \ - bluetooth.profile.avrcp.target.enabled=true \ - bluetooth.profile.hfp.ag.enabled=true \ - bluetooth.profile.hfp.hf.enabled=true -``` - -* Adjust Bluetooth TX power limitation in Bluetooth module and disable the - Fast Pair in Google Play service (aka GMS) - -```shell -adb root -adb shell am broadcast \ - -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' \ - --es package "com.google.android.gms.nearby" \ - --es user "\*" \ - --esa flags "enabled" \ - --esa types "boolean" \ - --esa values "false" \ - com.google.android.gms -``` - -### Running tests - -To run the tests, enter: - -```shell -atest -v CtsNearbyMultiDevicesTestSuite -``` - -## Manual testing the seeker side with headsets - -Use this testing with headsets such as Google Pixel buds. - -The `FastPairTestDataProviderService.apk` is a run-time configurable Fast Pair -data provider service (`FastPairDataProviderService`): - -`packages/modules/Connectivity/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider` - -It has a test data manager(`FastPairTestDataManager`) to receive intent -broadcasts to add or clear the test data cache (`FastPairTestDataCache`). This -cache provides the data to return to the Fast Pair module for onXXX calls (for -example, `onLoadFastPairAntispoofKeyDeviceMetadata`) so you can feed the -metadata for your device. - -Here are some sample uses: - -* Send FastPairAntispoofKeyDeviceMetadata for PixelBuds-A to - FastPairTestDataCache \ - `./fast_pair_data_provider_shell.sh -m=718c17 - -a=../test_data/fastpair/pixelbuds-a_antispoofkey_devicemeta_json.txt` -* Send FastPairAccountDevicesMetadata for PixelBuds-A to FastPairTestDataCache - \ - `./fast_pair_data_provider_shell.sh - -d=../test_data/fastpair/pixelbuds-a_account_devicemeta_json.txt` -* Send FastPairAntispoofKeyDeviceMetadata for Provider Simulator to - FastPairTestDataCache \ - `./fast_pair_data_provider_shell.sh -m=00000c - -a=../test_data/fastpair/simulator_antispoofkey_devicemeta_json.txt` -* Send FastPairAccountDevicesMetadata for Provider Simulator to - FastPairTestDataCache \ - `./fast_pair_data_provider_shell.sh - -d=../test_data/fastpair/simulator_account_devicemeta_json.txt` -* Clear FastPairTestDataCache \ - `./fast_pair_data_provider_shell.sh -c` - -See -[host/tool/fast_pair_data_provider_shell.sh](host/tool/fast_pair_data_provider_shell.sh) -for more documentation. - -To install the data provider as system private app, consider remounting the -system partition: - -``` -adb root && adb remount -``` - -Push it in: - -``` -adb push ${ANDROID_PRODUCT_OUT}/system/app/NearbyFastPairSeekerDataProvider -/system/priv-app/ -``` - -Then reboot: - -``` -adb reboot -``` - -## Manual testing the seeker side with provider simulator app - -The `NearbyFastPairProviderSimulatorApp.apk` is a simple Android app to let you -control the state of the Fast Pair provider simulator. Install this app on phone -2 (Fast Pair provider role) to work correctly. - -See -[clients/test_support/fastpair_provider/simulator_app/Android.bp](clients/test_support/fastpair_provider/simulator_app/Android.bp) -for more documentation. diff --git a/nearby/tests/multidevices/clients/Android.bp b/nearby/tests/multidevices/clients/Android.bp deleted file mode 100644 index db6d191114dcf5148a5c694f6d15b01883bdce2a..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/Android.bp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_library { - name: "NearbyMultiDevicesClientsLib", - srcs: ["src/**/*.kt"], - sdk_version: "test_current", - static_libs: [ - "MoblySnippetHelperLib", - "NearbyFastPairProviderLib", - "NearbyFastPairSeekerSharedLib", - "NearbyIntegrationUiTestsLib", - "androidx.test.core", - "androidx.test.ext.junit", - "kotlin-stdlib", - "mobly-snippet-lib", - "truth-prebuilt", - ], -} - -android_app { - name: "NearbyMultiDevicesClientsSnippets", - sdk_version: "test_current", - certificate: "platform", - static_libs: ["NearbyMultiDevicesClientsLib"], - optimize: { - enabled: true, - shrink: false, - // Required to avoid class collisions from static and shared linking - // of MessageNano. - proguard_compatibility: true, - proguard_flags_files: ["proguard.flags"], - }, -} diff --git a/nearby/tests/multidevices/clients/AndroidManifest.xml b/nearby/tests/multidevices/clients/AndroidManifest.xml deleted file mode 100644 index 86c10b2fb5d27113ac0e22a015ce9ea942581a55..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/AndroidManifest.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nearby/tests/multidevices/clients/proguard.flags b/nearby/tests/multidevices/clients/proguard.flags deleted file mode 100644 index 11938cdf86e8f764d40e389a6e2d468ecc14c133..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/proguard.flags +++ /dev/null @@ -1,29 +0,0 @@ -# Keep all snippet classes. --keep class android.nearby.multidevices.** { - *; -} - -# Keep AdvertisingSetCallback#onOwnAddressRead callback. --keep class * extends android.bluetooth.le.AdvertisingSetCallback { - *; -} - -# Do not touch Mobly. --keep class com.google.android.mobly.** { - *; -} - -# Keep names for easy debugging. --dontobfuscate - -# Necessary to allow debugging. --keepattributes * - -# By default, proguard leaves all classes in their original package, which -# needlessly repeats com.google.android.apps.etc. --repackageclasses "" - -# Allows proguard to make private and protected methods and fields public as -# part of optimization. This lets proguard inline trivial getter/setter -# methods. --allowaccessmodification \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt deleted file mode 100644 index 922e950cc52ecb6f1443201cc2b88f7c9a4d3956..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.provider - -import android.annotation.TargetApi -import android.content.Context -import android.nearby.multidevices.fastpair.provider.controller.FastPairProviderSimulatorController -import android.nearby.multidevices.fastpair.provider.events.ProviderStatusEvents -import android.os.Build -import androidx.test.platform.app.InstrumentationRegistry -import com.google.android.mobly.snippet.Snippet -import com.google.android.mobly.snippet.rpc.AsyncRpc -import com.google.android.mobly.snippet.rpc.Rpc - -/** Expose Mobly RPC methods for Python side to simulate fast pair provider role. */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -class FastPairProviderSimulatorSnippet : Snippet { - private val context: Context = InstrumentationRegistry.getInstrumentation().context - private val fastPairProviderSimulatorController = FastPairProviderSimulatorController(context) - - /** Sets up the Fast Pair provider simulator. */ - @AsyncRpc(description = "Sets up FP provider simulator.") - fun setupProviderSimulator(callbackId: String) { - fastPairProviderSimulatorController.setupProviderSimulator(ProviderStatusEvents(callbackId)) - } - - /** - * Starts model id advertising for scanning and initial pairing. - * - * @param callbackId the callback ID corresponding to the - * [FastPairProviderSimulatorSnippet#startProviderSimulator] call that started the scanning. - * @param modelId a 3-byte hex string for seeker side to recognize the device (ex: 0x00000C). - * @param antiSpoofingKeyString a public key for registered headsets. - */ - @AsyncRpc(description = "Starts model id advertising for scanning and initial pairing.") - fun startModelIdAdvertising( - callbackId: String, - modelId: String, - antiSpoofingKeyString: String - ) { - fastPairProviderSimulatorController.startModelIdAdvertising( - modelId, - antiSpoofingKeyString, - ProviderStatusEvents(callbackId) - ) - } - - /** Tears down the Fast Pair provider simulator. */ - @Rpc(description = "Tears down FP provider simulator.") - fun teardownProviderSimulator() { - fastPairProviderSimulatorController.teardownProviderSimulator() - } - - /** Gets BLE mac address of the Fast Pair provider simulator. */ - @Rpc(description = "Gets BLE mac address of the Fast Pair provider simulator.") - fun getBluetoothLeAddress(): String { - return fastPairProviderSimulatorController.getProviderSimulatorBleAddress() - } - - /** Gets the latest account key received on the Fast Pair provider simulator */ - @Rpc(description = "Gets the latest account key received on the Fast Pair provider simulator.") - fun getLatestReceivedAccountKey(): String? { - return fastPairProviderSimulatorController.getLatestReceivedAccountKey() - } -} diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt deleted file mode 100644 index a2d2659abc9819494930911492b79b9f193b65bb..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.provider.controller - -import android.bluetooth.le.AdvertiseSettings -import android.content.Context -import android.nearby.fastpair.provider.FastPairSimulator -import android.nearby.fastpair.provider.bluetooth.BluetoothController -import com.google.android.mobly.snippet.util.Log -import com.google.common.io.BaseEncoding.base64 - -class FastPairProviderSimulatorController(private val context: Context) : - FastPairSimulator.AdvertisingChangedCallback, BluetoothController.EventListener { - private lateinit var bluetoothController: BluetoothController - private lateinit var eventListener: EventListener - private var simulator: FastPairSimulator? = null - - fun setupProviderSimulator(listener: EventListener) { - eventListener = listener - - bluetoothController = BluetoothController(context, this) - bluetoothController.registerBluetoothStateReceiver() - bluetoothController.enableBluetooth() - bluetoothController.connectA2DPSinkProfile() - } - - fun teardownProviderSimulator() { - simulator?.destroy() - bluetoothController.unregisterBluetoothStateReceiver() - } - - fun startModelIdAdvertising( - modelId: String, - antiSpoofingKeyString: String, - listener: EventListener - ) { - eventListener = listener - - val antiSpoofingKey = base64().decode(antiSpoofingKeyString) - simulator = FastPairSimulator( - context, FastPairSimulator.Options.builder(modelId) - .setAdvertisingModelId(modelId) - .setBluetoothAddress(null) - .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) - .setAdvertisingChangedCallback(this) - .setAntiSpoofingPrivateKey(antiSpoofingKey) - .setUseRandomSaltForAccountKeyRotation(false) - .setDataOnlyConnection(false) - .setShowsPasskeyConfirmation(false) - .setRemoveAllDevicesDuringPairing(true) - .build() - ) - } - - fun getProviderSimulatorBleAddress() = simulator!!.bleAddress!! - - fun getLatestReceivedAccountKey() = - simulator!!.accountKey?.let { base64().encode(it.toByteArray()) } - - /** - * Called when we change our BLE advertisement. - * - * @param isAdvertising the advertising status. - */ - override fun onAdvertisingChanged(isAdvertising: Boolean) { - Log.i("FastPairSimulator onAdvertisingChanged(isAdvertising: $isAdvertising)") - eventListener.onAdvertisingChange(isAdvertising) - } - - /** The callback for the first onServiceConnected of A2DP sink profile. */ - override fun onA2DPSinkProfileConnected() { - eventListener.onA2DPSinkProfileConnected() - } - - /** - * Reports the current bond state of the remote device. - * - * @param bondState the bond state of the remote device. - */ - override fun onBondStateChanged(bondState: Int) { - } - - /** - * Reports the current connection state of the remote device. - * - * @param connectionState the bond state of the remote device. - */ - override fun onConnectionStateChanged(connectionState: Int) { - } - - /** - * Reports the current scan mode of the local Adapter. - * - * @param mode the current scan mode of the local Adapter. - */ - override fun onScanModeChange(mode: Int) { - eventListener.onScanModeChange(FastPairSimulator.scanModeToString(mode)) - } - - /** Interface for listening the events from Fast Pair Provider Simulator. */ - interface EventListener { - /** Reports the first onServiceConnected of A2DP sink profile. */ - fun onA2DPSinkProfileConnected() - - /** - * Reports the current scan mode of the local Adapter. - * - * @param mode the current scan mode in string. - */ - fun onScanModeChange(mode: String) - - /** - * Indicates the advertising state of the Fast Pair provider simulator has changed. - * - * @param isAdvertising the current advertising state, true if advertising otherwise false. - */ - fun onAdvertisingChange(isAdvertising: Boolean) - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/events/ProviderStatusEvents.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/events/ProviderStatusEvents.kt deleted file mode 100644 index 2addd7765fa2fec6321dc0175c0c41560c78296b..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/events/ProviderStatusEvents.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.provider.events - -import android.nearby.multidevices.fastpair.provider.controller.FastPairProviderSimulatorController -import com.google.android.mobly.snippet.util.postSnippetEvent - -/** The Mobly snippet events to report to the Python side. */ -class ProviderStatusEvents(private val callbackId: String) : - FastPairProviderSimulatorController.EventListener { - - /** Reports the first onServiceConnected of A2DP sink profile. */ - override fun onA2DPSinkProfileConnected() { - postSnippetEvent(callbackId, "onA2DPSinkProfileConnected") {} - } - - /** - * Indicates the Bluetooth scan mode of the Fast Pair provider simulator has changed. - * - * @param mode the current scan mode in String mapping by [FastPairSimulator#scanModeToString]. - */ - override fun onScanModeChange(mode: String) { - postSnippetEvent(callbackId, "onScanModeChange") { putString("mode", mode) } - } - - /** - * Indicates the advertising state of the Fast Pair provider simulator has changed. - * - * @param isAdvertising the current advertising state, true if advertising otherwise false. - */ - override fun onAdvertisingChange(isAdvertising: Boolean) { - postSnippetEvent(callbackId, "onAdvertisingChange") { - putBoolean("isAdvertising", isAdvertising) - } - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt deleted file mode 100644 index a2c39f727e8bf7a08f91af468435ec2e3980981a..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.seeker - -import android.content.Context -import android.nearby.FastPairDeviceMetadata -import android.nearby.NearbyManager -import android.nearby.ScanCallback -import android.nearby.ScanRequest -import android.nearby.fastpair.seeker.FAKE_TEST_ACCOUNT_NAME -import android.nearby.integration.ui.CheckNearbyHalfSheetUiTest -import android.nearby.integration.ui.DismissNearbyHalfSheetUiTest -import android.nearby.integration.ui.PairByNearbyHalfSheetUiTest -import android.nearby.multidevices.fastpair.seeker.data.FastPairTestDataManager -import android.nearby.multidevices.fastpair.seeker.events.PairingCallbackEvents -import android.nearby.multidevices.fastpair.seeker.events.ScanCallbackEvents -import android.provider.Settings -import androidx.test.core.app.ApplicationProvider -import com.google.android.mobly.snippet.Snippet -import com.google.android.mobly.snippet.rpc.AsyncRpc -import com.google.android.mobly.snippet.rpc.Rpc -import com.google.android.mobly.snippet.util.Log - -/** Expose Mobly RPC methods for Python side to test fast pair seeker role. */ -class FastPairSeekerSnippet : Snippet { - private val appContext = ApplicationProvider.getApplicationContext() - private val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager - private val fastPairTestDataManager = FastPairTestDataManager(appContext) - private lateinit var scanCallback: ScanCallback - - /** - * Starts scanning as a Fast Pair seeker to find provider devices. - * - * @param callbackId the callback ID corresponding to the {@link FastPairSeekerSnippet#startScan} - * call that started the scanning. - */ - @AsyncRpc(description = "Starts scanning as Fast Pair seeker to find provider devices.") - fun startScan(callbackId: String) { - val scanRequest = ScanRequest.Builder() - .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY) - .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR) - .setBleEnabled(true) - .build() - scanCallback = ScanCallbackEvents(callbackId) - - Log.i("Start Fast Pair scanning via BLE...") - nearbyManager.startScan(scanRequest, /* executor */ { it.run() }, scanCallback) - } - - /** Stops the Fast Pair seeker scanning. */ - @Rpc(description = "Stops the Fast Pair seeker scanning.") - fun stopScan() { - Log.i("Stop Fast Pair scanning.") - nearbyManager.stopScan(scanCallback) - } - - /** Waits and asserts the HalfSheet showed for Fast Pair pairing. - * - * @param modelId the expected model id to be associated with the HalfSheet. - * @param timeout the number of seconds to wait before giving up. - */ - @Rpc(description = "Waits the HalfSheet showed for Fast Pair pairing.") - fun waitAndAssertHalfSheetShowed(modelId: String, timeout: Int) { - Log.i("Waits and asserts the HalfSheet showed for Fast Pair model $modelId.") - - val deviceMetadata: FastPairDeviceMetadata = - fastPairTestDataManager.testDataCache.getFastPairDeviceMetadata(modelId) - ?: throw IllegalArgumentException( - "Can't find $modelId-FastPairAntispoofKeyDeviceMetadata pair in " + - "FastPairTestDataCache." - ) - val deviceName = deviceMetadata.name!! - val initialPairingDescriptionTemplateText = deviceMetadata.initialPairingDescription!! - - CheckNearbyHalfSheetUiTest().apply { - updateTestArguments( - waitHalfSheetPopupTimeoutSeconds = timeout, - halfSheetTitleText = deviceName, - halfSheetSubtitleText = initialPairingDescriptionTemplateText.format( - deviceName, - FAKE_TEST_ACCOUNT_NAME - ) - ) - checkNearbyHalfSheetUi() - } - } - - /** Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache. - * - * @param modelId a string of model id to be associated with. - * @param json a string of FastPairAntispoofKeyDeviceMetadata JSON object. - */ - @Rpc( - description = - "Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache." - ) - fun putAntispoofKeyDeviceMetadata(modelId: String, json: String) { - Log.i("Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache.") - fastPairTestDataManager.sendAntispoofKeyDeviceMetadata(modelId, json) - } - - /** Puts an array of FastPairAccountKeyDeviceMetadata into test data cache. - * - * @param json a string of FastPairAccountKeyDeviceMetadata JSON array. - */ - @Rpc(description = "Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.") - fun putAccountKeyDeviceMetadata(json: String) { - Log.i("Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.") - fastPairTestDataManager.sendAccountKeyDeviceMetadataJsonArray(json) - } - - /** Dumps all FastPairAccountKeyDeviceMetadata from the test data cache. */ - @Rpc(description = "Dumps all FastPairAccountKeyDeviceMetadata from the test data cache.") - fun dumpAccountKeyDeviceMetadata(): String { - Log.i("Dumps all FastPairAccountKeyDeviceMetadata from the test data cache.") - return fastPairTestDataManager.testDataCache.dumpAccountKeyDeviceMetadataListAsJson() - } - - /** Writes into {@link Settings} whether Fast Pair scan is enabled. - * - * @param enable whether the Fast Pair scan should be enabled. - */ - @Rpc(description = "Writes into Settings whether Fast Pair scan is enabled.") - fun setFastPairScanEnabled(enable: Boolean) { - Log.i("Writes into Settings whether Fast Pair scan is enabled.") - // TODO(b/228406038): Change back to use NearbyManager.setFastPairScanEnabled once un-hide. - val resolver = appContext.contentResolver - Settings.Secure.putInt(resolver, "fast_pair_scan_enabled", if (enable) 1 else 0) - } - - /** Dismisses the half sheet UI if showed. */ - @Rpc(description = "Dismisses the half sheet UI if showed.") - fun dismissHalfSheet() { - Log.i("Dismisses the half sheet UI if showed.") - - DismissNearbyHalfSheetUiTest().dismissHalfSheet() - } - - /** Starts pairing by interacting with half sheet UI. - * - * @param callbackId the callback ID corresponding to the - * {@link FastPairSeekerSnippet#startPairing} call that started the pairing. - */ - @AsyncRpc(description = "Starts pairing by interacting with half sheet UI.") - fun startPairing(callbackId: String) { - Log.i("Starts pairing by interacting with half sheet UI.") - - PairByNearbyHalfSheetUiTest().clickConnectButton() - fastPairTestDataManager.registerDataReceiveListener(PairingCallbackEvents(callbackId)) - } - - /** Invokes when the snippet runner shutting down. */ - override fun shutdown() { - super.shutdown() - - Log.i("Resets the Fast Pair test data cache.") - fastPairTestDataManager.unregisterDataReceiveListener() - fastPairTestDataManager.sendResetCache() - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt deleted file mode 100644 index 239ac613369cfad99659ec8d89bc17299c93730d..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.seeker.data - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.nearby.fastpair.seeker.ACTION_RESET_TEST_DATA_CACHE -import android.nearby.fastpair.seeker.ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.DATA_JSON_STRING_KEY -import android.nearby.fastpair.seeker.DATA_MODEL_ID_STRING_KEY -import android.nearby.fastpair.seeker.FastPairTestDataCache -import android.util.Log - -/** Manage local FastPairTestDataCache and send to/sync from the remote cache in data provider. */ -class FastPairTestDataManager(private val context: Context) : BroadcastReceiver() { - val testDataCache = FastPairTestDataCache() - var listener: EventListener? = null - - /** Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into local and remote cache. - * - * @param modelId a string of model id to be associated with. - * @param json a string of FastPairAntispoofKeyDeviceMetadata JSON object. - */ - fun sendAntispoofKeyDeviceMetadata(modelId: String, json: String) { - Intent().also { intent -> - intent.action = ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA - intent.putExtra(DATA_MODEL_ID_STRING_KEY, modelId) - intent.putExtra(DATA_JSON_STRING_KEY, json) - context.sendBroadcast(intent) - } - testDataCache.putAntispoofKeyDeviceMetadata(modelId, json) - } - - /** Puts account key device metadata array to local and remote cache. - * - * @param json a string of FastPairAccountKeyDeviceMetadata JSON array. - */ - fun sendAccountKeyDeviceMetadataJsonArray(json: String) { - Intent().also { intent -> - intent.action = ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA - intent.putExtra(DATA_JSON_STRING_KEY, json) - context.sendBroadcast(intent) - } - testDataCache.putAccountKeyDeviceMetadataJsonArray(json) - } - - /** Clears local and remote cache. */ - fun sendResetCache() { - context.sendBroadcast(Intent(ACTION_RESET_TEST_DATA_CACHE)) - testDataCache.reset() - } - - /** - * Callback method for receiving Intent broadcast from FastPairTestDataProvider. - * - * See [BroadcastReceiver#onReceive]. - * - * @param context the Context in which the receiver is running. - * @param intent the Intent being received. - */ - override fun onReceive(context: Context, intent: Intent) { - when (intent.action) { - ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA -> { - Log.d(TAG, "ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA received!") - val json = intent.getStringExtra(DATA_JSON_STRING_KEY)!! - testDataCache.putAccountKeyDeviceMetadataJsonObject(json) - listener?.onManageFastPairAccountDevice(json) - } - else -> Log.d(TAG, "Unknown action received!") - } - } - - fun registerDataReceiveListener(listener: EventListener) { - this.listener = listener - val bondStateFilter = IntentFilter(ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA) - context.registerReceiver(this, bondStateFilter) - } - - fun unregisterDataReceiveListener() { - this.listener = null - context.unregisterReceiver(this) - } - - /** Interface for listening the data receive from the remote cache in data provider. */ - interface EventListener { - /** Reports a FastPairAccountKeyDeviceMetadata write into the cache. - * - * @param json the FastPairAccountKeyDeviceMetadata as JSON object string. - */ - fun onManageFastPairAccountDevice(json: String) - } - - companion object { - private const val TAG = "FastPairTestDataManager" - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/PairingCallbackEvents.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/PairingCallbackEvents.kt deleted file mode 100644 index 19de1d995275a910244d2376ca41390b2b276ebf..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/PairingCallbackEvents.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.seeker.events - -import android.nearby.multidevices.fastpair.seeker.data.FastPairTestDataManager -import com.google.android.mobly.snippet.util.postSnippetEvent - -/** The Mobly snippet events to report to the Python side. */ -class PairingCallbackEvents(private val callbackId: String) : - FastPairTestDataManager.EventListener { - - /** Reports a FastPairAccountKeyDeviceMetadata write into the cache. - * - * @param json the FastPairAccountKeyDeviceMetadata as JSON object string. - */ - override fun onManageFastPairAccountDevice(json: String) { - postSnippetEvent(callbackId, "onManageAccountDevice") { - putString("accountDeviceJsonString", json) - } - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/ScanCallbackEvents.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/ScanCallbackEvents.kt deleted file mode 100644 index 363355fc8cde955a24019f54c09bc9e114fa67c6..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/ScanCallbackEvents.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.multidevices.fastpair.seeker.events - -import android.nearby.NearbyDevice -import android.nearby.ScanCallback -import com.google.android.mobly.snippet.util.postSnippetEvent - -/** The Mobly snippet events to report to the Python side. */ -class ScanCallbackEvents(private val callbackId: String) : ScanCallback { - - override fun onDiscovered(device: NearbyDevice) { - postSnippetEvent(callbackId, "onDiscovered") { - putString("device", device.toString()) - } - } - - override fun onUpdated(device: NearbyDevice) { - postSnippetEvent(callbackId, "onUpdated") { - putString("device", device.toString()) - } - } - - override fun onLost(device: NearbyDevice) { - postSnippetEvent(callbackId, "onLost") { - putString("device", device.toString()) - } - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp deleted file mode 100644 index b40677602dd0f3a5232342f2896717dfb304e91b..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_library { - name: "NearbyFastPairSeekerSharedLib", - srcs: ["shared/**/*.kt"], - sdk_version: "test_current", - static_libs: [ - // TODO(b/228406038): Remove "framework-nearby-static" once Fast Pair system APIs add back. - "framework-nearby-static", - "gson", - "guava", - ], -} - -android_library { - name: "NearbyFastPairSeekerDataProviderLib", - srcs: ["src/**/*.kt"], - sdk_version: "test_current", - static_libs: ["NearbyFastPairSeekerSharedLib"], -} - -android_app { - name: "NearbyFastPairSeekerDataProvider", - sdk_version: "test_current", - certificate: "platform", - static_libs: ["NearbyFastPairSeekerDataProviderLib"], - optimize: { - enabled: true, - shrink: true, - proguard_flags_files: ["proguard.flags"], - }, -} diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/AndroidManifest.xml b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/AndroidManifest.xml deleted file mode 100644 index 1d62f04c94b22d1f2b2e539332ba67c2f81f5efd..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/AndroidManifest.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/proguard.flags b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/proguard.flags deleted file mode 100644 index 15debab5e9c3bb95b26c6689cc76d9c618f63a4b..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/proguard.flags +++ /dev/null @@ -1,19 +0,0 @@ -# Keep all receivers/service classes. --keep class android.nearby.fastpair.seeker.** { - *; -} - -# Keep names for easy debugging. --dontobfuscate - -# Necessary to allow debugging. --keepattributes * - -# By default, proguard leaves all classes in their original package, which -# needlessly repeats com.google.android.apps.etc. --repackageclasses "" - -# Allows proguard to make private and protected methods and fields public as -# part of optimization. This lets proguard inline trivial getter/setter -# methods. --allowaccessmodification \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/Constants.kt b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/Constants.kt deleted file mode 100644 index 60701403d8c568e333e1ca536a19b2cecc744de5..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/Constants.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.fastpair.seeker - -const val FAKE_TEST_ACCOUNT_NAME = "nearby-mainline-fpseeker@google.com" - -const val ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA = - "android.nearby.fastpair.seeker.action.ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA" -const val ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA = - "android.nearby.fastpair.seeker.action.ACCOUNT_KEY_DEVICE_METADATA" -const val ACTION_RESET_TEST_DATA_CACHE = "android.nearby.fastpair.seeker.action.RESET" -const val ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA = - "android.nearby.fastpair.seeker.action.WRITE_ACCOUNT_KEY_DEVICE_METADATA" - -const val DATA_JSON_STRING_KEY = "json" -const val DATA_MODEL_ID_STRING_KEY = "modelId" diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt deleted file mode 100644 index 4fb883242da8e1685c8488810f183dbcf6e37ed2..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.fastpair.seeker - -import android.nearby.FastPairAccountKeyDeviceMetadata -import android.nearby.FastPairAntispoofKeyDeviceMetadata -import android.nearby.FastPairDeviceMetadata -import android.nearby.FastPairDiscoveryItem -import com.google.common.io.BaseEncoding -import com.google.gson.GsonBuilder -import com.google.gson.annotations.SerializedName - -/** Manage a cache of Fast Pair test data for testing. */ -class FastPairTestDataCache { - private val gson = GsonBuilder().disableHtmlEscaping().create() - private val accountKeyDeviceMetadataList = mutableListOf() - private val antispoofKeyDeviceMetadataDataMap = - mutableMapOf() - - fun putAccountKeyDeviceMetadataJsonArray(json: String) { - accountKeyDeviceMetadataList += - gson.fromJson(json, Array::class.java) - .map { it.toFastPairAccountKeyDeviceMetadata() } - } - - fun putAccountKeyDeviceMetadataJsonObject(json: String) { - accountKeyDeviceMetadataList += - gson.fromJson(json, FastPairAccountKeyDeviceMetadataData::class.java) - .toFastPairAccountKeyDeviceMetadata() - } - - fun putAccountKeyDeviceMetadata(accountKeyDeviceMetadata: FastPairAccountKeyDeviceMetadata) { - accountKeyDeviceMetadataList += accountKeyDeviceMetadata - } - - fun getAccountKeyDeviceMetadataList(): List = - accountKeyDeviceMetadataList.toList() - - fun dumpAccountKeyDeviceMetadataAsJson(metadata: FastPairAccountKeyDeviceMetadata): String = - gson.toJson(FastPairAccountKeyDeviceMetadataData(metadata)) - - fun dumpAccountKeyDeviceMetadataListAsJson(): String = - gson.toJson(accountKeyDeviceMetadataList.map { FastPairAccountKeyDeviceMetadataData(it) }) - - fun putAntispoofKeyDeviceMetadata(modelId: String, json: String) { - antispoofKeyDeviceMetadataDataMap[modelId] = - gson.fromJson(json, FastPairAntispoofKeyDeviceMetadataData::class.java) - } - - fun getAntispoofKeyDeviceMetadata(modelId: String): FastPairAntispoofKeyDeviceMetadata? { - return antispoofKeyDeviceMetadataDataMap[modelId]?.toFastPairAntispoofKeyDeviceMetadata() - } - - fun getFastPairDeviceMetadata(modelId: String): FastPairDeviceMetadata? = - antispoofKeyDeviceMetadataDataMap[modelId]?.deviceMeta?.toFastPairDeviceMetadata() - - fun reset() { - accountKeyDeviceMetadataList.clear() - antispoofKeyDeviceMetadataDataMap.clear() - } - - data class FastPairAccountKeyDeviceMetadataData( - @SerializedName("account_key") val accountKey: String?, - @SerializedName("sha256_account_key_public_address") val accountKeyPublicAddress: String?, - @SerializedName("fast_pair_device_metadata") val deviceMeta: FastPairDeviceMetadataData?, - @SerializedName("fast_pair_discovery_item") val discoveryItem: FastPairDiscoveryItemData? - ) { - constructor(meta: FastPairAccountKeyDeviceMetadata) : this( - accountKey = meta.deviceAccountKey?.base64Encode(), - accountKeyPublicAddress = meta.sha256DeviceAccountKeyPublicAddress?.base64Encode(), - deviceMeta = meta.fastPairDeviceMetadata?.let { FastPairDeviceMetadataData(it) }, - discoveryItem = meta.fastPairDiscoveryItem?.let { FastPairDiscoveryItemData(it) } - ) - - fun toFastPairAccountKeyDeviceMetadata(): FastPairAccountKeyDeviceMetadata { - return FastPairAccountKeyDeviceMetadata.Builder() - .setDeviceAccountKey(accountKey?.base64Decode()) - .setSha256DeviceAccountKeyPublicAddress(accountKeyPublicAddress?.base64Decode()) - .setFastPairDeviceMetadata(deviceMeta?.toFastPairDeviceMetadata()) - .setFastPairDiscoveryItem(discoveryItem?.toFastPairDiscoveryItem()) - .build() - } - } - - data class FastPairAntispoofKeyDeviceMetadataData( - @SerializedName("anti_spoofing_public_key_str") val antispoofPublicKey: String?, - @SerializedName("fast_pair_device_metadata") val deviceMeta: FastPairDeviceMetadataData? - ) { - fun toFastPairAntispoofKeyDeviceMetadata(): FastPairAntispoofKeyDeviceMetadata { - return FastPairAntispoofKeyDeviceMetadata.Builder() - .setAntispoofPublicKey(antispoofPublicKey?.base64Decode()) - .setFastPairDeviceMetadata(deviceMeta?.toFastPairDeviceMetadata()) - .build() - } - } - - data class FastPairDeviceMetadataData( - @SerializedName("ble_tx_power") val bleTxPower: Int, - @SerializedName("connect_success_companion_app_installed") val compAppInstalled: String?, - @SerializedName("connect_success_companion_app_not_installed") val comAppNotIns: String?, - @SerializedName("device_type") val deviceType: Int, - @SerializedName("download_companion_app_description") val downloadComApp: String?, - @SerializedName("fail_connect_go_to_settings_description") val failConnectDes: String?, - @SerializedName("image_url") val imageUrl: String?, - @SerializedName("initial_notification_description") val initNotification: String?, - @SerializedName("initial_notification_description_no_account") val initNoAccount: String?, - @SerializedName("initial_pairing_description") val initialPairingDescription: String?, - @SerializedName("intent_uri") val intentUri: String?, - @SerializedName("name") val name: String?, - @SerializedName("open_companion_app_description") val openCompanionAppDescription: String?, - @SerializedName("retroactive_pairing_description") val retroactivePairingDes: String?, - @SerializedName("subsequent_pairing_description") val subsequentPairingDescription: String?, - @SerializedName("trigger_distance") val triggerDistance: Double, - @SerializedName("case_url") val trueWirelessImageUrlCase: String?, - @SerializedName("left_bud_url") val trueWirelessImageUrlLeftBud: String?, - @SerializedName("right_bud_url") val trueWirelessImageUrlRightBud: String?, - @SerializedName("unable_to_connect_description") val unableToConnectDescription: String?, - @SerializedName("unable_to_connect_title") val unableToConnectTitle: String?, - @SerializedName("update_companion_app_description") val updateCompAppDes: String?, - @SerializedName("wait_launch_companion_app_description") val waitLaunchCompApp: String? - ) { - constructor(meta: FastPairDeviceMetadata) : this( - bleTxPower = meta.bleTxPower, - compAppInstalled = meta.connectSuccessCompanionAppInstalled, - comAppNotIns = meta.connectSuccessCompanionAppNotInstalled, - deviceType = meta.deviceType, - downloadComApp = meta.downloadCompanionAppDescription, - failConnectDes = meta.failConnectGoToSettingsDescription, - imageUrl = meta.imageUrl, - initNotification = meta.initialNotificationDescription, - initNoAccount = meta.initialNotificationDescriptionNoAccount, - initialPairingDescription = meta.initialPairingDescription, - intentUri = meta.intentUri, - name = meta.name, - openCompanionAppDescription = meta.openCompanionAppDescription, - retroactivePairingDes = meta.retroactivePairingDescription, - subsequentPairingDescription = meta.subsequentPairingDescription, - triggerDistance = meta.triggerDistance.toDouble(), - trueWirelessImageUrlCase = meta.trueWirelessImageUrlCase, - trueWirelessImageUrlLeftBud = meta.trueWirelessImageUrlLeftBud, - trueWirelessImageUrlRightBud = meta.trueWirelessImageUrlRightBud, - unableToConnectDescription = meta.unableToConnectDescription, - unableToConnectTitle = meta.unableToConnectTitle, - updateCompAppDes = meta.updateCompanionAppDescription, - waitLaunchCompApp = meta.waitLaunchCompanionAppDescription - ) - - fun toFastPairDeviceMetadata(): FastPairDeviceMetadata { - return FastPairDeviceMetadata.Builder() - .setBleTxPower(bleTxPower) - .setConnectSuccessCompanionAppInstalled(compAppInstalled) - .setConnectSuccessCompanionAppNotInstalled(comAppNotIns) - .setDeviceType(deviceType) - .setDownloadCompanionAppDescription(downloadComApp) - .setFailConnectGoToSettingsDescription(failConnectDes) - .setImageUrl(imageUrl) - .setInitialNotificationDescription(initNotification) - .setInitialNotificationDescriptionNoAccount(initNoAccount) - .setInitialPairingDescription(initialPairingDescription) - .setIntentUri(intentUri) - .setName(name) - .setOpenCompanionAppDescription(openCompanionAppDescription) - .setRetroactivePairingDescription(retroactivePairingDes) - .setSubsequentPairingDescription(subsequentPairingDescription) - .setTriggerDistance(triggerDistance.toFloat()) - .setTrueWirelessImageUrlCase(trueWirelessImageUrlCase) - .setTrueWirelessImageUrlLeftBud(trueWirelessImageUrlLeftBud) - .setTrueWirelessImageUrlRightBud(trueWirelessImageUrlRightBud) - .setUnableToConnectDescription(unableToConnectDescription) - .setUnableToConnectTitle(unableToConnectTitle) - .setUpdateCompanionAppDescription(updateCompAppDes) - .setWaitLaunchCompanionAppDescription(waitLaunchCompApp) - .build() - } - } - - data class FastPairDiscoveryItemData( - @SerializedName("action_url") val actionUrl: String?, - @SerializedName("action_url_type") val actionUrlType: Int, - @SerializedName("app_name") val appName: String?, - @SerializedName("authentication_public_key_secp256r1") val authenticationPublicKey: String?, - @SerializedName("description") val description: String?, - @SerializedName("device_name") val deviceName: String?, - @SerializedName("display_url") val displayUrl: String?, - @SerializedName("first_observation_timestamp_millis") val firstObservationMs: Long, - @SerializedName("icon_fife_url") val iconFfeUrl: String?, - @SerializedName("icon_png") val iconPng: String?, - @SerializedName("id") val id: String?, - @SerializedName("last_observation_timestamp_millis") val lastObservationMs: Long, - @SerializedName("mac_address") val macAddress: String?, - @SerializedName("package_name") val packageName: String?, - @SerializedName("pending_app_install_timestamp_millis") val pendingAppInstallMs: Long, - @SerializedName("rssi") val rssi: Int, - @SerializedName("state") val state: Int, - @SerializedName("title") val title: String?, - @SerializedName("trigger_id") val triggerId: String?, - @SerializedName("tx_power") val txPower: Int - ) { - constructor(item: FastPairDiscoveryItem) : this( - actionUrl = item.actionUrl, - actionUrlType = item.actionUrlType, - appName = item.appName, - authenticationPublicKey = item.authenticationPublicKeySecp256r1?.base64Encode(), - description = item.description, - deviceName = item.deviceName, - displayUrl = item.displayUrl, - firstObservationMs = item.firstObservationTimestampMillis, - iconFfeUrl = item.iconFfeUrl, - iconPng = item.iconPng?.base64Encode(), - id = item.id, - lastObservationMs = item.lastObservationTimestampMillis, - macAddress = item.macAddress, - packageName = item.packageName, - pendingAppInstallMs = item.pendingAppInstallTimestampMillis, - rssi = item.rssi, - state = item.state, - title = item.title, - triggerId = item.triggerId, - txPower = item.txPower - ) - - fun toFastPairDiscoveryItem(): FastPairDiscoveryItem { - return FastPairDiscoveryItem.Builder() - .setActionUrl(actionUrl) - .setActionUrlType(actionUrlType) - .setAppName(appName) - .setAuthenticationPublicKeySecp256r1(authenticationPublicKey?.base64Decode()) - .setDescription(description) - .setDeviceName(deviceName) - .setDisplayUrl(displayUrl) - .setFirstObservationTimestampMillis(firstObservationMs) - .setIconFfeUrl(iconFfeUrl) - .setIconPng(iconPng?.base64Decode()) - .setId(id) - .setLastObservationTimestampMillis(lastObservationMs) - .setMacAddress(macAddress) - .setPackageName(packageName) - .setPendingAppInstallTimestampMillis(pendingAppInstallMs) - .setRssi(rssi) - .setState(state) - .setTitle(title) - .setTriggerId(triggerId) - .setTxPower(txPower) - .build() - } - } -} - -private fun String.base64Decode(): ByteArray = BaseEncoding.base64().decode(this) - -private fun ByteArray.base64Encode(): String = BaseEncoding.base64().encode(this) diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt deleted file mode 100644 index e924da1d55f79078e35b255a6b00c5c11a3c5c7f..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.fastpair.seeker.data - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.nearby.FastPairAccountKeyDeviceMetadata -import android.nearby.fastpair.seeker.ACTION_RESET_TEST_DATA_CACHE -import android.nearby.fastpair.seeker.ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.DATA_JSON_STRING_KEY -import android.nearby.fastpair.seeker.DATA_MODEL_ID_STRING_KEY -import android.nearby.fastpair.seeker.FastPairTestDataCache -import android.util.Log - -/** Manage local FastPairTestDataCache and receive/update the remote cache in test snippet. */ -class FastPairTestDataManager(private val context: Context) : BroadcastReceiver() { - val testDataCache = FastPairTestDataCache() - - /** Writes a FastPairAccountKeyDeviceMetadata into local and remote cache. - * - * @param accountKeyDeviceMetadata the FastPairAccountKeyDeviceMetadata to write. - * @return a json object string of the accountKeyDeviceMetadata. - */ - fun writeAccountKeyDeviceMetadata( - accountKeyDeviceMetadata: FastPairAccountKeyDeviceMetadata - ): String { - testDataCache.putAccountKeyDeviceMetadata(accountKeyDeviceMetadata) - - val json = - testDataCache.dumpAccountKeyDeviceMetadataAsJson(accountKeyDeviceMetadata) - Intent().also { intent -> - intent.action = ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA - intent.putExtra(DATA_JSON_STRING_KEY, json) - context.sendBroadcast(intent) - } - return json - } - - /** - * Callback method for receiving Intent broadcast from test snippet. - * - * See [BroadcastReceiver#onReceive]. - * - * @param context the Context in which the receiver is running. - * @param intent the Intent being received. - */ - override fun onReceive(context: Context, intent: Intent) { - when (intent.action) { - ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA -> { - Log.d(TAG, "ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA received!") - val modelId = intent.getStringExtra(DATA_MODEL_ID_STRING_KEY)!! - val json = intent.getStringExtra(DATA_JSON_STRING_KEY)!! - testDataCache.putAntispoofKeyDeviceMetadata(modelId, json) - } - ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA -> { - Log.d(TAG, "ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA received!") - val json = intent.getStringExtra(DATA_JSON_STRING_KEY)!! - testDataCache.putAccountKeyDeviceMetadataJsonArray(json) - } - ACTION_RESET_TEST_DATA_CACHE -> { - Log.d(TAG, "ACTION_RESET_TEST_DATA_CACHE received!") - testDataCache.reset() - } - else -> Log.d(TAG, "Unknown action received!") - } - } - - companion object { - private const val TAG = "FastPairTestDataManager" - } -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/dataprovider/FastPairTestDataProviderService.kt b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/dataprovider/FastPairTestDataProviderService.kt deleted file mode 100644 index aec1379d4fa6c639ac0625839d3eaf5ab2d9028e..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/dataprovider/FastPairTestDataProviderService.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2022 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.nearby.fastpair.seeker.dataprovider - -import android.accounts.Account -import android.content.IntentFilter -import android.nearby.FastPairDataProviderService -import android.nearby.FastPairEligibleAccount -import android.nearby.fastpair.seeker.ACTION_RESET_TEST_DATA_CACHE -import android.nearby.fastpair.seeker.ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA -import android.nearby.fastpair.seeker.FAKE_TEST_ACCOUNT_NAME -import android.nearby.fastpair.seeker.data.FastPairTestDataManager -import android.util.Log - -/** - * Fast Pair Test Data Provider Service entry point for platform overlay. - */ -class FastPairTestDataProviderService : FastPairDataProviderService(TAG) { - private lateinit var testDataManager: FastPairTestDataManager - - override fun onCreate() { - Log.d(TAG, "onCreate()") - testDataManager = FastPairTestDataManager(this) - - val bondStateFilter = IntentFilter(ACTION_RESET_TEST_DATA_CACHE).apply { - addAction(ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA) - addAction(ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA) - } - registerReceiver(testDataManager, bondStateFilter) - } - - override fun onDestroy() { - Log.d(TAG, "onDestroy()") - unregisterReceiver(testDataManager) - - super.onDestroy() - } - - override fun onLoadFastPairAntispoofKeyDeviceMetadata( - request: FastPairAntispoofKeyDeviceMetadataRequest, - callback: FastPairAntispoofKeyDeviceMetadataCallback - ) { - val requestedModelId = request.modelId.bytesToStringLowerCase() - Log.d(TAG, "onLoadFastPairAntispoofKeyDeviceMetadata(modelId: $requestedModelId)") - - val fastPairAntispoofKeyDeviceMetadata = - testDataManager.testDataCache.getAntispoofKeyDeviceMetadata(requestedModelId) - if (fastPairAntispoofKeyDeviceMetadata != null) { - callback.onFastPairAntispoofKeyDeviceMetadataReceived( - fastPairAntispoofKeyDeviceMetadata - ) - } else { - Log.d(TAG, "No metadata available for $requestedModelId!") - callback.onError(ERROR_CODE_BAD_REQUEST, "No metadata available for $requestedModelId") - } - } - - override fun onLoadFastPairAccountDevicesMetadata( - request: FastPairAccountDevicesMetadataRequest, - callback: FastPairAccountDevicesMetadataCallback - ) { - val requestedAccount = request.account - val requestedAccountKeys = request.deviceAccountKeys - Log.d( - TAG, "onLoadFastPairAccountDevicesMetadata(" + - "account: $requestedAccount, accountKeys:$requestedAccountKeys)" - ) - Log.d(TAG, testDataManager.testDataCache.dumpAccountKeyDeviceMetadataListAsJson()) - - callback.onFastPairAccountDevicesMetadataReceived( - testDataManager.testDataCache.getAccountKeyDeviceMetadataList() - ) - } - - override fun onLoadFastPairEligibleAccounts( - request: FastPairEligibleAccountsRequest, - callback: FastPairEligibleAccountsCallback - ) { - Log.d(TAG, "onLoadFastPairEligibleAccounts()") - callback.onFastPairEligibleAccountsReceived(ELIGIBLE_ACCOUNTS_TEST_CONSTANT) - } - - override fun onManageFastPairAccount( - request: FastPairManageAccountRequest, - callback: FastPairManageActionCallback - ) { - val requestedAccount = request.account - val requestType = request.requestType - Log.d(TAG, "onManageFastPairAccount(account: $requestedAccount, requestType: $requestType)") - - callback.onSuccess() - } - - override fun onManageFastPairAccountDevice( - request: FastPairManageAccountDeviceRequest, - callback: FastPairManageActionCallback - ) { - val requestedAccount = request.account - val requestType = request.requestType - val requestTypeString = if (requestType == MANAGE_REQUEST_ADD) "Add" else "Remove" - val requestedAccountKeyDeviceMetadata = request.accountKeyDeviceMetadata - Log.d( - TAG, - "onManageFastPairAccountDevice(requestedAccount: $requestedAccount, " + - "requestType: $requestTypeString," - ) - - val requestedAccountKeyDeviceMetadataInJson = - testDataManager.writeAccountKeyDeviceMetadata(requestedAccountKeyDeviceMetadata) - Log.d(TAG, "requestedAccountKeyDeviceMetadata: $requestedAccountKeyDeviceMetadataInJson)") - - callback.onSuccess() - } - - companion object { - private const val TAG = "FastPairTestDataProviderService" - private val ELIGIBLE_ACCOUNTS_TEST_CONSTANT = listOf( - FastPairEligibleAccount.Builder() - .setAccount(Account(FAKE_TEST_ACCOUNT_NAME, "FakeTestAccount")) - .setOptIn(true) - .build() - ) - - private fun ByteArray.bytesToStringLowerCase(): String = - joinToString(separator = "") { eachByte -> "%02x".format(eachByte) } - } -} diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp deleted file mode 100644 index 298c9dcfb0925ccf00452b054ada7cf373298838..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -android_library { - name: "NearbyFastPairProviderLib", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - sdk_version: "core_platform", - libs: [ - // order matters: classes in framework-bluetooth are resolved before framework, meaning - // @hide APIs in framework-bluetooth are resolved before @SystemApi stubs in framework - "framework-bluetooth.impl", - "framework", - - // if sdk_version="" this gets automatically included, but here we need to add manually. - "framework-res", - ], - static_libs: [ - "NearbyFastPairProviderLiteProtos", - "androidx.core_core", - "androidx.test.core", - "error_prone_annotations", - "fast-pair-lite-protos", - "framework-annotations-lib", - "guava", - "kotlin-stdlib", - "nearby-common-lib", - ], -} diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/AndroidManifest.xml b/nearby/tests/multidevices/clients/test_support/fastpair_provider/AndroidManifest.xml deleted file mode 100644 index 400a434c7b99450ec20f1dace74a5dd8d9619892..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/proto/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/proto/Android.bp deleted file mode 100644 index 7ae43e595416492a06c567bdd78e0dfecc67968e..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/proto/Android.bp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -java_library { - name: "NearbyFastPairProviderLiteProtos", - proto: { - type: "lite", - canonical_path_from_root: false, - }, - sdk_version: "system_current", - min_sdk_version: "30", - srcs: ["*.proto"], -} - - diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/proto/event_stream_protocol.proto b/nearby/tests/multidevices/clients/test_support/fastpair_provider/proto/event_stream_protocol.proto deleted file mode 100644 index 54db34a0afc2f09b0e72ced4d74d156496bf3021..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/proto/event_stream_protocol.proto +++ /dev/null @@ -1,85 +0,0 @@ -syntax = "proto2"; - -package android.nearby.fastpair.provider; - -option java_package = "android.nearby.fastpair.provider"; -option java_outer_classname = "EventStreamProtocol"; - -enum EventGroup { - UNSPECIFIED = 0; - BLUETOOTH = 1; - LOGGING = 2; - DEVICE = 3; - DEVICE_ACTION = 4; - DEVICE_CONFIGURATION = 5; - DEVICE_CAPABILITY_SYNC = 6; - SMART_AUDIO_SOURCE_SWITCHING = 7; - ACKNOWLEDGEMENT = 255; -} - -enum BluetoothEventCode { - BLUETOOTH_UNSPECIFIED = 0; - BLUETOOTH_ENABLE_SILENCE_MODE = 1; - BLUETOOTH_DISABLE_SILENCE_MODE = 2; -} - -enum LoggingEventCode { - LOG_UNSPECIFIED = 0; - LOG_FULL = 1; - LOG_SAVE_TO_BUFFER = 2; -} - -enum DeviceEventCode { - DEVICE_UNSPECIFIED = 0; - DEVICE_MODEL_ID = 1; - DEVICE_BLE_ADDRESS = 2; - DEVICE_BATTERY_INFO = 3; - ACTIVE_COMPONENTS_REQUEST = 5; - ACTIVE_COMPONENTS_RESPONSE = 6; - DEVICE_CAPABILITY = 7; - PLATFORM_TYPE = 8; - FIRMWARE_VERSION = 9; - SECTION_NONCE = 10; -} - -enum DeviceActionEventCode { - DEVICE_ACTION_UNSPECIFIED = 0; - DEVICE_ACTION_RING = 1; -} - -enum DeviceConfigurationEventCode { - CONFIGURATION_UNSPECIFIED = 0; - CONFIGURATION_BUFFER_SIZE = 1; -} - -enum DeviceCapabilitySyncEventCode { - REQUEST_UNSPECIFIED = 0; - REQUEST_CAPABILITY_UPDATE = 1; - CONFIGURABLE_BUFFER_SIZE_RANGE = 2; -} - -enum AcknowledgementEventCode { - ACKNOWLEDGEMENT_UNSPECIFIED = 0; - ACKNOWLEDGEMENT_ACK = 1; - ACKNOWLEDGEMENT_NAK = 2; -} - -enum PlatformType { - PLATFORM_TYPE_UNKNOWN = 0; - ANDROID = 1; -} - -enum SassEventCode { - EVENT_UNSPECIFIED = 0; - EVENT_GET_CAPABILITY_OF_SASS = 0x10; - EVENT_NOTIFY_CAPABILITY_OF_SASS = 0x11; - EVENT_SET_MULTI_POINT_STATE = 0x12; - EVENT_SWITCH_AUDIO_SOURCE_BETWEEN_CONNECTED_DEVICES = 0x30; - EVENT_SWITCH_BACK = 0x31; - EVENT_NOTIFY_MULTIPOINT_SWITCH_EVENT = 0x32; - EVENT_GET_CONNECTION_STATUS = 0x33; - EVENT_NOTIFY_CONNECTION_STATUS = 0x34; - EVENT_SASS_INITIATED_CONNECTION = 0x40; - EVENT_INDICATE_IN_USE_ACCOUNT_KEY = 0x41; - EVENT_SET_CUSTOM_DATA = 0x42; -} diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp deleted file mode 100644 index 125c34e637db44d4bcc989c35f83aff99d1de6f7..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -// Build and install NearbyFastPairProviderSimulatorApp to your phone: -// m NearbyFastPairProviderSimulatorApp -// adb root -// adb remount && adb reboot (make first time remount work) -// -// adb root -// adb remount -// adb push ${ANDROID_PRODUCT_OUT}/system/app/NearbyFastPairProviderSimulatorApp /system/app/ -// adb reboot -// Grant all permissions requested to NearbyFastPairProviderSimulatorApp before launching it. -android_app { - name: "NearbyFastPairProviderSimulatorApp", - sdk_version: "test_current", - // Sign with "platform" certificate for accessing Bluetooth @SystemAPI - certificate: "platform", - static_libs: ["NearbyFastPairProviderSimulatorLib"], - optimize: { - enabled: true, - shrink: true, - proguard_flags_files: ["proguard.flags"], - }, -} - -android_library { - name: "NearbyFastPairProviderSimulatorLib", - sdk_version: "test_current", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - static_libs: [ - "NearbyFastPairProviderLib", - "NearbyFastPairProviderLiteProtos", - "NearbyFastPairProviderSimulatorLiteProtos", - "androidx.annotation_annotation", - "error_prone_annotations", - "fast-pair-lite-protos", - ], -} \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/AndroidManifest.xml b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/AndroidManifest.xml deleted file mode 100644 index 8880b110718fee2d3b10da3b42384c1a4785807e..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/AndroidManifest.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags deleted file mode 100644 index 0827c608de9ecce1656140110bc9754654d112b7..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags +++ /dev/null @@ -1,19 +0,0 @@ -# Keep AdvertisingSetCallback#onOwnAddressRead callback. --keep class * extends android.bluetooth.le.AdvertisingSetCallback { - *; -} - -# Keep names for easy debugging. --dontobfuscate - -# Necessary to allow debugging. --keepattributes * - -# By default, proguard leaves all classes in their original package, which -# needlessly repeats com.google.android.apps.etc. --repackageclasses "" - -# Allows proguard to make private and protected methods and fields public as -# part of optimization. This lets proguard inline trivial getter/setter -# methods. --allowaccessmodification \ No newline at end of file diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proto/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proto/Android.bp deleted file mode 100644 index e9648004d0eed40d9e146c9ca045d71b7c5971dd..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proto/Android.bp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2022 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -java_library { - name: "NearbyFastPairProviderSimulatorLiteProtos", - proto: { - type: "lite", - canonical_path_from_root: false, - }, - sdk_version: "system_current", - min_sdk_version: "30", - srcs: ["*.proto"], -} - - diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proto/simulator_stream_protocol.proto b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proto/simulator_stream_protocol.proto deleted file mode 100644 index 9b17fda9b0f70600945fc78838ae5e49a1677895..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proto/simulator_stream_protocol.proto +++ /dev/null @@ -1,110 +0,0 @@ -syntax = "proto2"; - -package android.nearby.fastpair.provider.simulator; - -option java_package = "android.nearby.fastpair.provider.simulator"; -option java_outer_classname = "SimulatorStreamProtocol"; - -// Used by remote devices to control simulator behaviors. -message Command { - // Type of this command. - required Code code = 1; - - // Required for SHOW_BATTERY. - optional BatteryInfo battery_info = 2; - - enum Code { - // Request for simulator's acknowledge message. - POLLING = 0; - - // Reset and clear bluetooth state. - RESET = 1; - - // Present battery information in the advertisement. - SHOW_BATTERY = 2; - - // Remove battery information in the advertisement. - HIDE_BATTERY = 3; - - // Request for BR/EDR address. - REQUEST_BLUETOOTH_ADDRESS_PUBLIC = 4; - - // Request for BLE address. - REQUEST_BLUETOOTH_ADDRESS_BLE = 5; - - // Request for account key. - REQUEST_ACCOUNT_KEY = 6; - } - - // Battery information for true wireless headsets. - // https://devsite.googleplex.com/nearby/fast-pair/early-access/spec#BatteryNotification - message BatteryInfo { - // Show or hide the battery UI notification. - optional bool suppress_notification = 1; - repeated BatteryValue battery_values = 2; - - // Advertised battery level data. - message BatteryValue { - // The charging flag. - required bool charging = 1; - - // Battery level from 0 to 100. - required uint32 level = 2; - } - } -} - -// Notify the remote devices when states are changed or response the command on -// the simulator. -message Event { - // Type of this event. - required Code code = 1; - - // Required for BLUETOOTH_STATE_BOND. - optional int32 bond_state = 2; - - // Required for BLUETOOTH_STATE_CONNECTION. - optional int32 connection_state = 3; - - // Required for BLUETOOTH_STATE_SCAN_MODE. - optional int32 scan_mode = 4; - - // Required for BLUETOOTH_ADDRESS_PUBLIC. - optional string public_address = 5; - - // Required for BLUETOOTH_ADDRESS_BLE. - optional string ble_address = 6; - - // Required for BLUETOOTH_ALIAS_NAME. - optional string alias_name = 7; - - // Required for REQUEST_ACCOUNT_KEY. - optional bytes account_key = 8; - - enum Code { - // Response the polling. - ACKNOWLEDGE = 0; - - // Notify the event android.bluetooth.device.action.BOND_STATE_CHANGED - BLUETOOTH_STATE_BOND = 1; - - // Notify the event - // android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED - BLUETOOTH_STATE_CONNECTION = 2; - - // Notify the event android.bluetooth.adapter.action.SCAN_MODE_CHANGED - BLUETOOTH_STATE_SCAN_MODE = 3; - - // Notify the current BR/EDR address - BLUETOOTH_ADDRESS_PUBLIC = 4; - - // Notify the current BLE address - BLUETOOTH_ADDRESS_BLE = 5; - - // Notify the event android.bluetooth.device.action.ALIAS_CHANGED - BLUETOOTH_ALIAS_NAME = 6; - - // Response the REQUEST_ACCOUNT_KEY. - ACCOUNT_KEY = 7; - } -} diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/res/layout/activity_main.xml b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/res/layout/activity_main.xml deleted file mode 100644 index b7e85eb13b7c92768b47eeea1ea7038a2e642819..0000000000000000000000000000000000000000 --- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/res/layout/activity_main.xml +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -