Merge "Merge history of Nearby" into tm-dev
This commit is contained in:
@@ -114,7 +114,7 @@ java_sdk_library {
|
||||
// In preparation for future move
|
||||
"//packages/modules/Connectivity/apex",
|
||||
"//packages/modules/Connectivity/service-t",
|
||||
"//packages/modules/Nearby/service",
|
||||
"//packages/modules/Connectivity/nearby/service",
|
||||
"//frameworks/base",
|
||||
|
||||
// Tests using hidden APIs
|
||||
@@ -131,10 +131,10 @@ java_sdk_library {
|
||||
"//frameworks/opt/telephony/tests/telephonytests",
|
||||
"//packages/modules/CaptivePortalLogin/tests",
|
||||
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/nearby/tests:__subpackages__",
|
||||
"//packages/modules/Connectivity/tests:__subpackages__",
|
||||
"//packages/modules/IPsec/tests/iketests",
|
||||
"//packages/modules/NetworkStack/tests:__subpackages__",
|
||||
"//packages/modules/Nearby/tests:__subpackages__",
|
||||
"//packages/modules/Wifi/service/tests/wifitests",
|
||||
],
|
||||
}
|
||||
|
||||
8
nearby/.gitignore
vendored
Normal file
8
nearby/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Eclipse project
|
||||
**/.classpath
|
||||
**/.project
|
||||
|
||||
# IntelliJ project
|
||||
**/.idea
|
||||
**/*.iml
|
||||
**/*.ipr
|
||||
10
nearby/PREUPLOAD.cfg
Normal file
10
nearby/PREUPLOAD.cfg
Normal file
@@ -0,0 +1,10 @@
|
||||
[Builtin Hooks]
|
||||
xmllint = true
|
||||
clang_format = true
|
||||
commit_msg_changeid_field = true
|
||||
|
||||
[Builtin Hooks Options]
|
||||
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
|
||||
|
||||
[Hook Scripts]
|
||||
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
|
||||
42
nearby/README.md
Normal file
42
nearby/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Nearby Mainline Module
|
||||
This directory contains code for the AOSP Nearby mainline module.
|
||||
|
||||
##Directory Structure
|
||||
|
||||
`apex`
|
||||
- Files associated with the Nearby mainline module APEX.
|
||||
|
||||
`framework`
|
||||
- Contains client side APIs and AIDL files.
|
||||
|
||||
`jni`
|
||||
- JNI wrapper for invoking Android APIs from native code.
|
||||
|
||||
`native`
|
||||
- Native code implementation for nearby module services.
|
||||
|
||||
`service`
|
||||
- Server side implementation for nearby module services.
|
||||
|
||||
`tests`
|
||||
- Unit/Multi devices tests for Nearby module (both Java and native code).
|
||||
|
||||
## IDE setup
|
||||
|
||||
```sh
|
||||
$ source build/envsetup.sh && lunch <TARGET>
|
||||
$ cd packages/modules/Nearby
|
||||
$ aidegen .
|
||||
# This will launch Intellij project for Nearby module.
|
||||
```
|
||||
|
||||
## Build and Install
|
||||
|
||||
```sh
|
||||
$ source build/envsetup.sh && lunch <TARGET>
|
||||
$ m com.google.android.tethering.next deapexer
|
||||
$ $ANDROID_BUILD_TOP/out/host/linux-x86/bin/deapexer decompress --input \
|
||||
${ANDROID_PRODUCT_OUT}/system/apex/com.google.android.tethering.next.capex \
|
||||
--output /tmp/tethering.apex
|
||||
$ adb install -r /tmp/tethering.apex
|
||||
```
|
||||
24
nearby/TEST_MAPPING
Normal file
24
nearby/TEST_MAPPING
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "NearbyUnitTests"
|
||||
},
|
||||
{
|
||||
"name": "NearbyIntegrationPrivilegedTests"
|
||||
},
|
||||
{
|
||||
"name": "NearbyIntegrationUntrustedTests"
|
||||
}
|
||||
],
|
||||
"postsubmit": [
|
||||
{
|
||||
"name": "NearbyUnitTests"
|
||||
}
|
||||
]
|
||||
// TODO(b/193602229): uncomment once it's supported.
|
||||
//"mainline-presubmit": [
|
||||
// {
|
||||
// "name": "NearbyUnitTests[com.google.android.nearby.apex]"
|
||||
// }
|
||||
//]
|
||||
}
|
||||
21
nearby/apex/Android.bp
Normal file
21
nearby/apex/Android.bp
Normal file
@@ -0,0 +1,21 @@
|
||||
// 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"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "nearby-jarjar-rules",
|
||||
srcs: ["jarjar-rules.txt"],
|
||||
}
|
||||
1
nearby/apex/jarjar-rules.txt
Normal file
1
nearby/apex/jarjar-rules.txt
Normal file
@@ -0,0 +1 @@
|
||||
rule com.android.internal.** com.android.nearby.jarjar.@0
|
||||
4
nearby/apex/manifest.json
Normal file
4
nearby/apex/manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "com.android.nearby",
|
||||
"version": 1
|
||||
}
|
||||
55
nearby/framework/Android.bp
Normal file
55
nearby/framework/Android.bp
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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"],
|
||||
}
|
||||
|
||||
// Sources included in the framework-connectivity-t jar
|
||||
// TODO: consider moving files to packages/modules/Connectivity
|
||||
filegroup {
|
||||
name: "framework-nearby-java-sources",
|
||||
srcs: [
|
||||
"java/**/*.java",
|
||||
"java/**/*.aidl",
|
||||
],
|
||||
path: "java",
|
||||
visibility: [
|
||||
"//packages/modules/Connectivity/framework-t:__subpackages__",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-nearby-sources",
|
||||
srcs: [
|
||||
":framework-nearby-java-sources",
|
||||
],
|
||||
visibility: ["//frameworks/base"],
|
||||
}
|
||||
|
||||
// Build of only framework-nearby (not as part of connectivity) for
|
||||
// unit tests
|
||||
java_library {
|
||||
name: "framework-nearby-static",
|
||||
srcs: [":framework-nearby-java-sources"],
|
||||
sdk_version: "module_current",
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"framework-bluetooth",
|
||||
],
|
||||
static_libs: [
|
||||
"modules-utils-preconditions",
|
||||
],
|
||||
visibility: ["//packages/modules/Connectivity/nearby/tests:__subpackages__"],
|
||||
}
|
||||
64
nearby/framework/java/android/nearby/BroadcastCallback.java
Normal file
64
nearby/framework/java/android/nearby/BroadcastCallback.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.SystemApi;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Callback when broadcasting request using nearby specification.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface BroadcastCallback {
|
||||
/** Broadcast was successful. */
|
||||
int STATUS_OK = 0;
|
||||
|
||||
/** General status code when broadcast failed. */
|
||||
int STATUS_FAILURE = 1;
|
||||
|
||||
/**
|
||||
* Broadcast failed as the callback was already registered.
|
||||
*/
|
||||
int STATUS_FAILURE_ALREADY_REGISTERED = 2;
|
||||
|
||||
/**
|
||||
* Broadcast failed as the request contains excessive data.
|
||||
*/
|
||||
int STATUS_FAILURE_SIZE_EXCEED_LIMIT = 3;
|
||||
|
||||
/**
|
||||
* Broadcast failed as the client doesn't hold required permissions.
|
||||
*/
|
||||
int STATUS_FAILURE_MISSING_PERMISSIONS = 4;
|
||||
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({STATUS_OK, STATUS_FAILURE, STATUS_FAILURE_ALREADY_REGISTERED,
|
||||
STATUS_FAILURE_SIZE_EXCEED_LIMIT, STATUS_FAILURE_MISSING_PERMISSIONS})
|
||||
@interface BroadcastStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when broadcast status changes.
|
||||
*/
|
||||
void onStatusChanged(@BroadcastStatus int status);
|
||||
}
|
||||
171
nearby/framework/java/android/nearby/BroadcastRequest.java
Normal file
171
nearby/framework/java/android/nearby/BroadcastRequest.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a {@link BroadcastRequest}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class BroadcastRequest {
|
||||
|
||||
/** An unknown nearby broadcast request type. */
|
||||
public static final int BROADCAST_TYPE_UNKNOWN = -1;
|
||||
|
||||
/** Broadcast type for advertising using nearby presence protocol. */
|
||||
public static final int BROADCAST_TYPE_NEARBY_PRESENCE = 3;
|
||||
|
||||
/** @hide **/
|
||||
// Currently, only Nearby Presence broadcast is supported, in the future
|
||||
// broadcasting using other nearby specifications will be added.
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({BROADCAST_TYPE_UNKNOWN, BROADCAST_TYPE_NEARBY_PRESENCE})
|
||||
public @interface BroadcastType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tx Power when the value is not set in the broadcast.
|
||||
*/
|
||||
public static final int UNKNOWN_TX_POWER = -127;
|
||||
|
||||
/**
|
||||
* An unknown version of presence broadcast request.
|
||||
*/
|
||||
public static final int PRESENCE_VERSION_UNKNOWN = -1;
|
||||
|
||||
/**
|
||||
* A legacy presence version that is only suitable for legacy (31 bytes) BLE advertisements.
|
||||
* This exists to support legacy presence version, and not recommended for use.
|
||||
*/
|
||||
public static final int PRESENCE_VERSION_V0 = 0;
|
||||
|
||||
/**
|
||||
* V1 of Nearby Presence Protocol. This version supports both legacy (31 bytes) BLE
|
||||
* advertisements, and extended BLE advertisements.
|
||||
*/
|
||||
public static final int PRESENCE_VERSION_V1 = 1;
|
||||
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({PRESENCE_VERSION_UNKNOWN, PRESENCE_VERSION_V0, PRESENCE_VERSION_V1})
|
||||
public @interface BroadcastVersion {
|
||||
}
|
||||
|
||||
/**
|
||||
* The medium where the broadcast request should be sent.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@IntDef({Medium.BLE, Medium.MDNS})
|
||||
public @interface Medium {
|
||||
int BLE = 1;
|
||||
int MDNS = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BroadcastRequest} from parcel.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public static BroadcastRequest createFromParcel(Parcel in) {
|
||||
int type = in.readInt();
|
||||
switch (type) {
|
||||
case BroadcastRequest.BROADCAST_TYPE_NEARBY_PRESENCE:
|
||||
return PresenceBroadcastRequest.createFromParcelBody(in);
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"Unexpected broadcast type (value " + type + ") in parcel.");
|
||||
}
|
||||
}
|
||||
|
||||
private final @BroadcastType int mType;
|
||||
private final @BroadcastVersion int mVersion;
|
||||
private final int mTxPower;
|
||||
private final @Medium List<Integer> mMediums;
|
||||
|
||||
BroadcastRequest(@BroadcastType int type, @BroadcastVersion int version, int txPower,
|
||||
@Medium List<Integer> mediums) {
|
||||
this.mType = type;
|
||||
this.mVersion = version;
|
||||
this.mTxPower = txPower;
|
||||
this.mMediums = mediums;
|
||||
}
|
||||
|
||||
BroadcastRequest(@BroadcastType int type, Parcel in) {
|
||||
mType = type;
|
||||
mVersion = in.readInt();
|
||||
mTxPower = in.readInt();
|
||||
mMediums = new ArrayList<>();
|
||||
in.readList(mMediums, Integer.class.getClassLoader(), Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the broadcast.
|
||||
*/
|
||||
public @BroadcastType int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the broadcast.
|
||||
*/
|
||||
public @BroadcastVersion int getVersion() {
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the calibrated TX power when this request is broadcast.
|
||||
*/
|
||||
@IntRange(from = -127, to = 126)
|
||||
public int getTxPower() {
|
||||
return mTxPower;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of broadcast mediums.
|
||||
*/
|
||||
@NonNull
|
||||
@Medium
|
||||
public List<Integer> getMediums() {
|
||||
return mMediums;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the BroadcastRequest to the parcel.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mType);
|
||||
dest.writeInt(mVersion);
|
||||
dest.writeInt(mTxPower);
|
||||
dest.writeList(mMediums);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
parcelable BroadcastRequestParcelable;
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A wrapper of {@link BroadcastRequest} that is parcelable.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class BroadcastRequestParcelable implements Parcelable {
|
||||
private final BroadcastRequest mBroadcastRequest;
|
||||
|
||||
public static final Creator<BroadcastRequestParcelable> CREATOR =
|
||||
new Creator<BroadcastRequestParcelable>() {
|
||||
@Override
|
||||
public BroadcastRequestParcelable createFromParcel(Parcel in) {
|
||||
return new BroadcastRequestParcelable(BroadcastRequest.createFromParcel(in));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BroadcastRequestParcelable[] newArray(int size) {
|
||||
return new BroadcastRequestParcelable[size];
|
||||
}
|
||||
};
|
||||
|
||||
BroadcastRequestParcelable(BroadcastRequest broadcastRequest) {
|
||||
mBroadcastRequest = broadcastRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the broadcastRequest.
|
||||
*/
|
||||
public BroadcastRequest getBroadcastRequest() {
|
||||
return mBroadcastRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
mBroadcastRequest.writeToParcel(dest, flags);
|
||||
}
|
||||
}
|
||||
90
nearby/framework/java/android/nearby/CredentialElement.java
Normal file
90
nearby/framework/java/android/nearby/CredentialElement.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Represents an element in {@link PresenceCredential}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class CredentialElement implements Parcelable {
|
||||
private final String mKey;
|
||||
private final byte[] mValue;
|
||||
|
||||
/**
|
||||
* Constructs a {@link CredentialElement}.
|
||||
*/
|
||||
public CredentialElement(@NonNull String key, @NonNull byte[] value) {
|
||||
Preconditions.checkState(key != null && value != null,
|
||||
"neither key or value can be null");
|
||||
mKey = key;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<CredentialElement> CREATOR =
|
||||
new Parcelable.Creator<CredentialElement>() {
|
||||
@Override
|
||||
public CredentialElement createFromParcel(Parcel in) {
|
||||
String key = in.readString();
|
||||
byte[] value = new byte[in.readInt()];
|
||||
in.readByteArray(value);
|
||||
return new CredentialElement(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialElement[] newArray(int size) {
|
||||
return new CredentialElement[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mKey);
|
||||
dest.writeInt(mValue.length);
|
||||
dest.writeByteArray(mValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of the credential element.
|
||||
*/
|
||||
@NonNull
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the credential element.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getValue() {
|
||||
return mValue;
|
||||
}
|
||||
}
|
||||
89
nearby/framework/java/android/nearby/DataElement.java
Normal file
89
nearby/framework/java/android/nearby/DataElement.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a data element in Nearby Presence.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class DataElement implements Parcelable {
|
||||
|
||||
private final int mKey;
|
||||
private final byte[] mValue;
|
||||
|
||||
/**
|
||||
* Constructs a {@link DataElement}.
|
||||
*/
|
||||
public DataElement(int key, @NonNull byte[] value) {
|
||||
Preconditions.checkState(value != null, "value cannot be null");
|
||||
mKey = key;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<DataElement> CREATOR = new Creator<DataElement>() {
|
||||
@Override
|
||||
public DataElement createFromParcel(Parcel in) {
|
||||
int key = in.readInt();
|
||||
byte[] value = new byte[in.readInt()];
|
||||
in.readByteArray(value);
|
||||
return new DataElement(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataElement[] newArray(int size) {
|
||||
return new DataElement[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mKey);
|
||||
dest.writeInt(mValue.length);
|
||||
dest.writeByteArray(mValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of the data element, as defined in the nearby presence specification.
|
||||
*/
|
||||
public int getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the data element.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getValue() {
|
||||
return mValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.annotation.SystemApi;
|
||||
import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
|
||||
|
||||
/**
|
||||
* Class for metadata of a Fast Pair device associated with an account.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public byte[] getSha256DeviceAccountKeyPublicAddress() {
|
||||
return mMetadataParcel.sha256DeviceAccountKeyPublicAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata of a Fast Pair device type.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public FastPairDiscoveryItem getFastPairDiscoveryItem() {
|
||||
if (mMetadataParcel.discoveryItem == null) {
|
||||
return null;
|
||||
}
|
||||
return new FastPairDiscoveryItem(mMetadataParcel.discoveryItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create FastPairAccountKeyDeviceMetadata.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final class Builder {
|
||||
|
||||
private final FastPairAccountKeyDeviceMetadataParcel mBuilderParcel;
|
||||
|
||||
/**
|
||||
* Default constructor of Builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public FastPairAccountKeyDeviceMetadata build() {
|
||||
return new FastPairAccountKeyDeviceMetadata(mBuilderParcel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.annotation.SystemApi;
|
||||
import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
|
||||
|
||||
/**
|
||||
* Class for a type of registered Fast Pair device keyed by modelID, or antispoofKey.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public class FastPairAntispoofKeyDeviceMetadata {
|
||||
|
||||
FastPairAntispoofKeyDeviceMetadataParcel mMetadataParcel;
|
||||
FastPairAntispoofKeyDeviceMetadata(
|
||||
FastPairAntispoofKeyDeviceMetadataParcel metadataParcel) {
|
||||
this.mMetadataParcel = metadataParcel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Antispoof public key.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public byte[] getAntispoofPublicKey() {
|
||||
return this.mMetadataParcel.antispoofPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata of a Fast Pair device type.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public FastPairDeviceMetadata getFastPairDeviceMetadata() {
|
||||
if (this.mMetadataParcel.deviceMetadata == null) {
|
||||
return null;
|
||||
}
|
||||
return new FastPairDeviceMetadata(this.mMetadataParcel.deviceMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create FastPairAntispoofkeyDeviceMetadata.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final class Builder {
|
||||
|
||||
private final FastPairAntispoofKeyDeviceMetadataParcel mBuilderParcel;
|
||||
|
||||
/**
|
||||
* Default constructor of Builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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 <a href="https://developers.google.com/nearby/fast-pair/spec#data_format">Data Format</a>.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public FastPairAntispoofKeyDeviceMetadata build() {
|
||||
return new FastPairAntispoofKeyDeviceMetadata(mBuilderParcel);
|
||||
}
|
||||
}
|
||||
}
|
||||
99
nearby/framework/java/android/nearby/FastPairClient.java
Normal file
99
nearby/framework/java/android/nearby/FastPairClient.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.BinderThread;
|
||||
import android.content.Context;
|
||||
import android.nearby.aidl.IFastPairClient;
|
||||
import android.nearby.aidl.IFastPairStatusCallback;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* 0p API for controlling Fast Pair. It communicates between main thread and service.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class FastPairClient {
|
||||
|
||||
private static final String TAG = "FastPairClient";
|
||||
private final IBinder mBinder;
|
||||
private final WeakReference<Context> mWeakContext;
|
||||
IFastPairClient mFastPairClient;
|
||||
PairStatusCallbackIBinder mPairStatusCallbackIBinder;
|
||||
|
||||
/**
|
||||
* The Ibinder instance should be from
|
||||
* {@link com.android.server.nearby.fastpair.halfsheet.FastPairService} so that the client can
|
||||
* talk with the service.
|
||||
*/
|
||||
public FastPairClient(Context context, IBinder binder) {
|
||||
mBinder = binder;
|
||||
mFastPairClient = IFastPairClient.Stub.asInterface(mBinder);
|
||||
mWeakContext = new WeakReference<>(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback at service to get UI updates.
|
||||
*/
|
||||
public void registerHalfSheet(FastPairStatusCallback fastPairStatusCallback) {
|
||||
if (mPairStatusCallbackIBinder != null) {
|
||||
return;
|
||||
}
|
||||
mPairStatusCallbackIBinder = new PairStatusCallbackIBinder(fastPairStatusCallback);
|
||||
try {
|
||||
mFastPairClient.registerHalfSheet(mPairStatusCallbackIBinder);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to register fastPairStatusCallback", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pairs the device at service.
|
||||
*/
|
||||
public void connect(FastPairDevice fastPairDevice) {
|
||||
try {
|
||||
mFastPairClient.connect(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
* 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.annotation.SystemApi;
|
||||
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.
|
||||
*
|
||||
* <p>Fast Pair providers are identified by their UID / package name.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int MANAGE_REQUEST_ADD = 0;
|
||||
|
||||
/**
|
||||
* Manage request type to remove, or opt-out.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int ERROR_CODE_BAD_REQUEST = 0;
|
||||
|
||||
/**
|
||||
* Error code for internal error.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
public interface FastPairAntispoofKeyDeviceMetadataCallback {
|
||||
|
||||
/**
|
||||
* Invoked once the meta data is loaded.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onFastPairAntispoofKeyDeviceMetadataReceived(
|
||||
@NonNull FastPairAntispoofKeyDeviceMetadata metadata);
|
||||
|
||||
/** Invoked in case of error.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onError(@ErrorCode int code, @Nullable String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when Fast Pair devices of a given account is loaded.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface FastPairAccountDevicesMetadataCallback {
|
||||
|
||||
/**
|
||||
* Should be invoked once the metadatas are loaded.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onFastPairAccountDevicesMetadataReceived(
|
||||
@NonNull Collection<FastPairAccountKeyDeviceMetadata> metadatas);
|
||||
/**
|
||||
* Invoked in case of error.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onError(@ErrorCode int code, @Nullable String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when FastPair eligible accounts are loaded.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface FastPairEligibleAccountsCallback {
|
||||
|
||||
/**
|
||||
* Should be invoked once the eligible accounts are loaded.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onFastPairEligibleAccountsReceived(
|
||||
@NonNull Collection<FastPairEligibleAccount> accounts);
|
||||
/**
|
||||
* Invoked in case of error.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onError(@ErrorCode int code, @Nullable String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when a management action is finished.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface FastPairManageActionCallback {
|
||||
|
||||
/**
|
||||
* Should be invoked once the manage action is successful.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
void onSuccess();
|
||||
/**
|
||||
* Invoked in case of error.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
* <a href="https://developers.google.com/nearby/fast-pair/spec#model_id">Model ID</a>.
|
||||
* @return raw bytes of modelId in the same format returned by Google at device registration
|
||||
* time.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
public @NonNull Collection<byte[]> getDeviceAccountKeys() {
|
||||
if (this.mMetadataRequestParcel.deviceAccountKeys == null) {
|
||||
return new ArrayList<byte[]>(0);
|
||||
}
|
||||
List<byte[]> 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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
public @ManageRequestType int getRequestType() {
|
||||
return this.mAccountRequestParcel.requestType;
|
||||
}
|
||||
/**
|
||||
* Get account.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
public @ManageRequestType int getRequestType() {
|
||||
return this.mRequestParcel.requestType;
|
||||
}
|
||||
/**
|
||||
* Get account.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public @NonNull Account getAccount() {
|
||||
return this.mRequestParcel.account;
|
||||
}
|
||||
/**
|
||||
* Get BleAddress.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public @Nullable String getBleAddress() {
|
||||
return this.mRequestParcel.bleAddress;
|
||||
}
|
||||
/**
|
||||
* Get account key device metadata.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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<FastPairAccountKeyDeviceMetadata> 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<FastPairEligibleAccount> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
24
nearby/framework/java/android/nearby/FastPairDevice.aidl
Normal file
24
nearby/framework/java/android/nearby/FastPairDevice.aidl
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A class represents a Fast Pair device that can be discovered by multiple mediums.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
parcelable FastPairDevice;
|
||||
299
nearby/framework/java/android/nearby/FastPairDevice.java
Normal file
299
nearby/framework/java/android/nearby/FastPairDevice.java
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class represents a Fast Pair device that can be discovered by multiple mediums.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class FastPairDevice extends NearbyDevice implements Parcelable {
|
||||
/**
|
||||
* Used to read a FastPairDevice from a Parcel.
|
||||
*/
|
||||
public static final Creator<FastPairDevice> CREATOR = new Creator<FastPairDevice>() {
|
||||
@Override
|
||||
public FastPairDevice createFromParcel(Parcel in) {
|
||||
FastPairDevice.Builder builder = new FastPairDevice.Builder();
|
||||
if (in.readInt() == 1) {
|
||||
builder.setName(in.readString());
|
||||
}
|
||||
int size = in.readInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
builder.addMedium(in.readInt());
|
||||
}
|
||||
builder.setRssi(in.readInt());
|
||||
if (in.readInt() == 1) {
|
||||
builder.setModelId(in.readString());
|
||||
}
|
||||
builder.setBluetoothAddress(in.readString());
|
||||
if (in.readInt() == 1) {
|
||||
int dataLength = in.readInt();
|
||||
byte[] data = new byte[dataLength];
|
||||
in.readByteArray(data);
|
||||
builder.setData(data);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastPairDevice[] newArray(int size) {
|
||||
return new FastPairDevice[size];
|
||||
}
|
||||
};
|
||||
|
||||
// Some OEM devices devices don't have model Id.
|
||||
@Nullable private final String mModelId;
|
||||
|
||||
// Bluetooth hardware address as string. Can be read from BLE ScanResult.
|
||||
private final String mBluetoothAddress;
|
||||
|
||||
@Nullable
|
||||
private final byte[] mData;
|
||||
|
||||
/**
|
||||
* Creates a new FastPairDevice.
|
||||
*
|
||||
* @param name Name of the FastPairDevice. Can be {@code null} if there is no name.
|
||||
* @param mediums The {@link Medium}s over which the device is discovered.
|
||||
* @param rssi The received signal strength in dBm.
|
||||
* @param modelId The identifier of the Fast Pair device.
|
||||
* Can be {@code null} if there is no Model ID.
|
||||
* @param bluetoothAddress The hardware address of this BluetoothDevice.
|
||||
* @param data Extra data for a Fast Pair device.
|
||||
*/
|
||||
public FastPairDevice(@Nullable String name,
|
||||
List<Integer> mediums,
|
||||
int rssi,
|
||||
@Nullable String modelId,
|
||||
@NonNull String bluetoothAddress,
|
||||
@Nullable byte[] data) {
|
||||
super(name, mediums, rssi);
|
||||
this.mModelId = modelId;
|
||||
this.mBluetoothAddress = bluetoothAddress;
|
||||
this.mData = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identifier of the Fast Pair device. Can be {@code null} if there is no Model ID.
|
||||
*/
|
||||
@Nullable
|
||||
public String getModelId() {
|
||||
return this.mModelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hardware address of this BluetoothDevice.
|
||||
*/
|
||||
@NonNull
|
||||
public String getBluetoothAddress() {
|
||||
return mBluetoothAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extra data for a Fast Pair device. Can be {@code null} if there is extra data.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] getData() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
/**
|
||||
* No special parcel contents.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this FastPairDevice.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("FastPairDevice [");
|
||||
String name = getName();
|
||||
if (getName() != null && !name.isEmpty()) {
|
||||
stringBuilder.append("name=").append(name).append(", ");
|
||||
}
|
||||
stringBuilder.append("medium={");
|
||||
for (int medium: getMediums()) {
|
||||
stringBuilder.append(mediumToString(medium));
|
||||
}
|
||||
stringBuilder.append("} rssi=").append(getRssi());
|
||||
stringBuilder.append(" modelId=").append(mModelId);
|
||||
stringBuilder.append(" bluetoothAddress=").append(mBluetoothAddress);
|
||||
stringBuilder.append("]");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof FastPairDevice) {
|
||||
FastPairDevice otherDevice = (FastPairDevice) other;
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(mModelId, otherDevice.mModelId)
|
||||
&& Objects.equals(mBluetoothAddress, otherDevice.mBluetoothAddress)
|
||||
&& Arrays.equals(mData, otherDevice.mData);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
getName(), getMediums(), getRssi(), mModelId, mBluetoothAddress,
|
||||
Arrays.hashCode(mData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
String name = getName();
|
||||
dest.writeInt(name == null ? 0 : 1);
|
||||
if (name != null) {
|
||||
dest.writeString(name);
|
||||
}
|
||||
List<Integer> mediums = getMediums();
|
||||
dest.writeInt(mediums.size());
|
||||
for (int medium : mediums) {
|
||||
dest.writeInt(medium);
|
||||
}
|
||||
dest.writeInt(getRssi());
|
||||
dest.writeInt(mModelId == null ? 0 : 1);
|
||||
if (mModelId != null) {
|
||||
dest.writeString(mModelId);
|
||||
}
|
||||
dest.writeString(mBluetoothAddress);
|
||||
dest.writeInt(mData == null ? 0 : 1);
|
||||
if (mData != null) {
|
||||
dest.writeInt(mData.length);
|
||||
dest.writeByteArray(mData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder class for {@link FastPairDevice}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final List<Integer> mMediums;
|
||||
|
||||
@Nullable private String mName;
|
||||
private int mRssi;
|
||||
@Nullable private String mModelId;
|
||||
private String mBluetoothAddress;
|
||||
@Nullable private byte[] mData;
|
||||
|
||||
public Builder() {
|
||||
mMediums = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the Fast Pair device.
|
||||
*
|
||||
* @param name Name of the FastPairDevice. Can be {@code null} if there is no name.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setName(@Nullable String name) {
|
||||
mName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the medium over which the Fast Pair device is discovered.
|
||||
*
|
||||
* @param medium The {@link Medium} over which the device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addMedium(@Medium int medium) {
|
||||
mMediums.add(medium);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RSSI between the scan device and the discovered Fast Pair device.
|
||||
*
|
||||
* @param rssi The received signal strength in dBm.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRssi(@IntRange(from = -127, to = 126) int rssi) {
|
||||
mRssi = rssi;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model Id of this Fast Pair device.
|
||||
*
|
||||
* @param modelId The identifier of the Fast Pair device. Can be {@code null}
|
||||
* if there is no Model ID.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setModelId(@Nullable String modelId) {
|
||||
mModelId = modelId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hardware address of this BluetoothDevice.
|
||||
*
|
||||
* @param bluetoothAddress The hardware address of this BluetoothDevice.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setBluetoothAddress(@NonNull String bluetoothAddress) {
|
||||
Objects.requireNonNull(bluetoothAddress);
|
||||
mBluetoothAddress = bluetoothAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw data for a FastPairDevice. Can be {@code null} if there is no extra data.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setData(@Nullable byte[] data) {
|
||||
mData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a FastPairDevice and return it.
|
||||
*/
|
||||
@NonNull
|
||||
public FastPairDevice build() {
|
||||
return new FastPairDevice(mName, mMediums, mRssi, mModelId,
|
||||
mBluetoothAddress, mData);
|
||||
}
|
||||
}
|
||||
}
|
||||
1009
nearby/framework/java/android/nearby/FastPairDeviceMetadata.java
Normal file
1009
nearby/framework/java/android/nearby/FastPairDeviceMetadata.java
Normal file
File diff suppressed because it is too large
Load Diff
825
nearby/framework/java/android/nearby/FastPairDiscoveryItem.java
Normal file
825
nearby/framework/java/android/nearby/FastPairDiscoveryItem.java
Normal file
@@ -0,0 +1,825 @@
|
||||
/*
|
||||
* 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.annotation.SystemApi;
|
||||
import android.nearby.aidl.FastPairDiscoveryItemParcel;
|
||||
|
||||
/**
|
||||
* Class for FastPairDiscoveryItem and its builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public class FastPairDiscoveryItem {
|
||||
|
||||
FastPairDiscoveryItemParcel mMetadataParcel;
|
||||
|
||||
FastPairDiscoveryItem(
|
||||
FastPairDiscoveryItemParcel metadataParcel) {
|
||||
this.mMetadataParcel = metadataParcel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return mMetadataParcel.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getType() {
|
||||
return mMetadataParcel.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MacAddress.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getMacAddress() {
|
||||
return mMetadataParcel.macAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ActionUrl.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getActionUrl() {
|
||||
return mMetadataParcel.actionUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DeviceName.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getDeviceName() {
|
||||
return mMetadataParcel.deviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return mMetadataParcel.title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return mMetadataParcel.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DisplayUrl.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getDisplayUrl() {
|
||||
return mMetadataParcel.displayUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LastObservationTimestampMillis.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public long getLastObservationTimestampMillis() {
|
||||
return mMetadataParcel.lastObservationTimestampMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get FirstObservationTimestampMillis.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public long getFirstObservationTimestampMillis() {
|
||||
return mMetadataParcel.firstObservationTimestampMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get State.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getState() {
|
||||
return mMetadataParcel.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ActionUrlType.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getActionUrlType() {
|
||||
return mMetadataParcel.actionUrlType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Rssi.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getRssi() {
|
||||
return mMetadataParcel.rssi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PendingAppInstallTimestampMillis.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public long getPendingAppInstallTimestampMillis() {
|
||||
return mMetadataParcel.pendingAppInstallTimestampMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TxPower.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getTxPower() {
|
||||
return mMetadataParcel.txPower;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AppName.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getAppName() {
|
||||
return mMetadataParcel.appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GroupId.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getGroupId() {
|
||||
return mMetadataParcel.groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AttachmentType.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getAttachmentType() {
|
||||
return mMetadataParcel.attachmentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PackageName.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getPackageName() {
|
||||
return mMetadataParcel.packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get FeatureGraphicUrl.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getFeatureGraphicUrl() {
|
||||
return mMetadataParcel.featureGraphicUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TriggerId.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public byte[] getIconPng() {
|
||||
return mMetadataParcel.iconPng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IconFifeUrl.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getIconFfeUrl() {
|
||||
return mMetadataParcel.iconFifeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DebugMessage.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getDebugMessage() {
|
||||
return mMetadataParcel.debugMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DebugCategory.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getDebugCategory() {
|
||||
return mMetadataParcel.debugCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LostMillis.
|
||||
*/
|
||||
public long getLostMillis() {
|
||||
return mMetadataParcel.lostMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LastUserExperience.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public int getLastUserExperience() {
|
||||
return mMetadataParcel.lastUserExperience;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BleRecordBytes. Raw bytes of {@link android.bluetooth.le.ScanRecord}.
|
||||
* It is the most recent BLE advertisement related to this item.
|
||||
*
|
||||
* @return the most recent BLE advertisement in raw bytes of
|
||||
* {@link android.bluetooth.le.ScanRecord}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public byte[] getBleRecordBytes() {
|
||||
return mMetadataParcel.bleRecordBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get EntityId.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getEntityId() {
|
||||
return mMetadataParcel.entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authenticationPublicKeySecp256r1, which is same as AntiSpoof public key, see
|
||||
* <a href="https://developers.google.com/nearby/fast-pair/spec#data_format">Data Format</a>.
|
||||
*
|
||||
* @return 64-byte authenticationPublicKeySecp256r1.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public byte[] getAuthenticationPublicKeySecp256r1() {
|
||||
return mMetadataParcel.authenticationPublicKeySecp256r1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create FastPairDiscoveryItem.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final class Builder {
|
||||
|
||||
private final FastPairDiscoveryItemParcel mBuilderParcel;
|
||||
|
||||
/**
|
||||
* Default constructor of Builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public Builder() {
|
||||
mBuilderParcel = new FastPairDiscoveryItemParcel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Id.
|
||||
*
|
||||
* @param id Unique id.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setId(@Nullable String id) {
|
||||
mBuilderParcel.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Nearby Type.
|
||||
*
|
||||
* @param type Nearby type.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setType(int type) {
|
||||
mBuilderParcel.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set MacAddress.
|
||||
*
|
||||
* @param macAddress Fast Pair device rotating mac address.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setAppName(@Nullable String appName) {
|
||||
mBuilderParcel.appName = appName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set GroupId.
|
||||
*
|
||||
* @param groupId ID used for associating several DiscoveryItems. These items may be
|
||||
* visually displayed together.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setGroupId(@Nullable String groupId) {
|
||||
mBuilderParcel.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set AttachmentType.
|
||||
*
|
||||
* @param attachmentType Whether the attachment is created in debug namespace.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setAttachmentType(int attachmentType) {
|
||||
mBuilderParcel.attachmentType = attachmentType;
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setPackageName(@Nullable String packageName) {
|
||||
mBuilderParcel.packageName = packageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set FeatureGraphicUrl.
|
||||
*
|
||||
* @param featureGraphicUrl The "feature" graphic image url used for large sized list view
|
||||
* entries.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setFeatureGraphicUrl(@Nullable String featureGraphicUrl) {
|
||||
mBuilderParcel.featureGraphicUrl = featureGraphicUrl;
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setIconFfeUrl(@Nullable String iconFifeUrl) {
|
||||
mBuilderParcel.iconFifeUrl = iconFifeUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DebugMessage.
|
||||
*
|
||||
* @param debugMessage Message written to bugreport for 3P developers.(No sensitive info)
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setDebugMessage(@Nullable String debugMessage) {
|
||||
mBuilderParcel.debugMessage = debugMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DebugCategory.
|
||||
*
|
||||
* @param debugCategory Weather the item is filtered out on server.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setDebugCategory(int debugCategory) {
|
||||
mBuilderParcel.debugCategory = debugCategory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set LostMillis.
|
||||
*
|
||||
* @param lostMillis Client timestamp when the trigger (e.g. beacon) was last lost
|
||||
* (e.g. when Messages told us the beacon's no longer nearby).
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setLostMillis(long lostMillis) {
|
||||
mBuilderParcel.lostMillis = lostMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set LastUserExperience.
|
||||
*
|
||||
* @param lastUserExperience The kind of experience the user last had with this (e.g. if
|
||||
* they dismissed the notification, that's bad; but if they tapped
|
||||
* it, that's good).
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setLastUserExperience(int lastUserExperience) {
|
||||
mBuilderParcel.lastUserExperience = lastUserExperience;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set BleRecordBytes.
|
||||
*
|
||||
* @param bleRecordBytes The most recent BLE advertisement related to this item. Raw bytes
|
||||
* of {@link android.bluetooth.le.ScanRecord}.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setBleRecordBytes(@Nullable byte[] bleRecordBytes) {
|
||||
mBuilderParcel.bleRecordBytes = bleRecordBytes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set EntityId.
|
||||
*
|
||||
* @param entityId An ID generated on the server to uniquely identify content.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setEntityId(@Nullable String entityId) {
|
||||
mBuilderParcel.entityId = entityId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set authenticationPublicKeySecp256r1, which is same as AntiSpoof public key, see
|
||||
* <a href="https://developers.google.com/nearby/fast-pair/spec#data_format">Data Format</a>
|
||||
*
|
||||
* @param authenticationPublicKeySecp256r1 64-byte Fast Pair device public key.
|
||||
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setAuthenticationPublicKeySecp256r1(
|
||||
@Nullable byte[] authenticationPublicKeySecp256r1) {
|
||||
mBuilderParcel.authenticationPublicKeySecp256r1 = authenticationPublicKeySecp256r1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link FastPairDiscoveryItem} with the currently set configuration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public FastPairDiscoveryItem build() {
|
||||
return new FastPairDiscoveryItem(mBuilderParcel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.annotation.SystemApi;
|
||||
import android.nearby.aidl.FastPairEligibleAccountParcel;
|
||||
|
||||
/**
|
||||
* Class for FastPairEligibleAccount and its builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public class FastPairEligibleAccount {
|
||||
|
||||
FastPairEligibleAccountParcel mAccountParcel;
|
||||
|
||||
FastPairEligibleAccount(FastPairEligibleAccountParcel accountParcel) {
|
||||
this.mAccountParcel = accountParcel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Account.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public Account getAccount() {
|
||||
return this.mAccountParcel.account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OptIn Status.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public boolean isOptIn() {
|
||||
return this.mAccountParcel.optIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create FastPairEligibleAccount.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final class Builder {
|
||||
|
||||
private final FastPairEligibleAccountParcel mBuilderParcel;
|
||||
|
||||
/**
|
||||
* Default constructor of Builder.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
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
|
||||
*/
|
||||
@SystemApi
|
||||
@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
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setOptIn(boolean optIn) {
|
||||
mBuilderParcel.optIn = optIn;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link FastPairEligibleAccount} with the currently set configuration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public FastPairEligibleAccount build() {
|
||||
return new FastPairEligibleAccount(mBuilderParcel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
27
nearby/framework/java/android/nearby/IBroadcastListener.aidl
Normal file
27
nearby/framework/java/android/nearby/IBroadcastListener.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Callback when brodacast status changes.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IBroadcastListener {
|
||||
/** Called when the broadcast status changes. */
|
||||
void onStatusChanged(int status);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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);
|
||||
}
|
||||
39
nearby/framework/java/android/nearby/INearbyManager.aidl
Normal file
39
nearby/framework/java/android/nearby/INearbyManager.aidl
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.nearby.IBroadcastListener;
|
||||
import android.nearby.IScanListener;
|
||||
import android.nearby.BroadcastRequestParcelable;
|
||||
import android.nearby.ScanRequest;
|
||||
|
||||
/**
|
||||
* Interface for communicating with the nearby services.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface INearbyManager {
|
||||
|
||||
int registerScanListener(in ScanRequest scanRequest, in IScanListener listener);
|
||||
|
||||
void unregisterScanListener(in IScanListener listener);
|
||||
|
||||
void startBroadcast(in BroadcastRequestParcelable broadcastRequest,
|
||||
in IBroadcastListener callback);
|
||||
|
||||
void stopBroadcast(in IBroadcastListener callback);
|
||||
}
|
||||
35
nearby/framework/java/android/nearby/IScanListener.aidl
Normal file
35
nearby/framework/java/android/nearby/IScanListener.aidl
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.nearby.NearbyDeviceParcelable;
|
||||
|
||||
/**
|
||||
* Binder callback for ScanCallback.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IScanListener {
|
||||
/** Reports a {@link NearbyDevice} being discovered. */
|
||||
void onDiscovered(in NearbyDeviceParcelable nearbyDeviceParcelable);
|
||||
|
||||
/** Reports a {@link NearbyDevice} information(distance, packet, and etc) changed. */
|
||||
void onUpdated(in NearbyDeviceParcelable nearbyDeviceParcelable);
|
||||
|
||||
/** Reports a {@link NearbyDevice} is no longer within range. */
|
||||
void onLost(in NearbyDeviceParcelable nearbyDeviceParcelable);
|
||||
}
|
||||
151
nearby/framework/java/android/nearby/NearbyDevice.java
Normal file
151
nearby/framework/java/android/nearby/NearbyDevice.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class represents a device that can be discovered by multiple mediums.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class NearbyDevice {
|
||||
|
||||
@Nullable
|
||||
private final String mName;
|
||||
|
||||
@Medium
|
||||
private final List<Integer> mMediums;
|
||||
|
||||
private final int mRssi;
|
||||
|
||||
/**
|
||||
* Creates a new NearbyDevice.
|
||||
*
|
||||
* @param name Local device name. Can be {@code null} if there is no name.
|
||||
* @param mediums The {@link Medium}s over which the device is discovered.
|
||||
* @param rssi The received signal strength in dBm.
|
||||
* @hide
|
||||
*/
|
||||
public NearbyDevice(@Nullable String name, List<Integer> mediums, int rssi) {
|
||||
for (int medium : mediums) {
|
||||
Preconditions.checkState(isValidMedium(medium),
|
||||
"Not supported medium: " + medium
|
||||
+ ", scan medium must be one of NearbyDevice#Medium.");
|
||||
}
|
||||
mName = name;
|
||||
mMediums = mediums;
|
||||
mRssi = rssi;
|
||||
}
|
||||
|
||||
static String mediumToString(@Medium int medium) {
|
||||
switch (medium) {
|
||||
case Medium.BLE:
|
||||
return "BLE";
|
||||
case Medium.BLUETOOTH:
|
||||
return "Bluetooth Classic";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the medium is defined in {@link Medium}.
|
||||
*
|
||||
* @param medium Integer that may represent a medium type.
|
||||
*/
|
||||
public static boolean isValidMedium(@Medium int medium) {
|
||||
return medium == Medium.BLE
|
||||
|| medium == Medium.BLUETOOTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the device, or null if not available.
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/** The medium over which this device was discovered. */
|
||||
@NonNull
|
||||
@Medium public List<Integer> getMediums() {
|
||||
return mMediums;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the received signal strength in dBm.
|
||||
*/
|
||||
@IntRange(from = -127, to = 126)
|
||||
public int getRssi() {
|
||||
return mRssi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("NearbyDevice [");
|
||||
if (mName != null && !mName.isEmpty()) {
|
||||
stringBuilder.append("name=").append(mName).append(", ");
|
||||
}
|
||||
stringBuilder.append("medium={");
|
||||
for (int medium : mMediums) {
|
||||
stringBuilder.append(mediumToString(medium));
|
||||
}
|
||||
stringBuilder.append("} rssi=").append(mRssi);
|
||||
stringBuilder.append("]");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof NearbyDevice) {
|
||||
NearbyDevice otherDevice = (NearbyDevice) other;
|
||||
return Objects.equals(mName, otherDevice.mName)
|
||||
&& mMediums == otherDevice.mMediums
|
||||
&& mRssi == otherDevice.mRssi;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mName, mMediums, mRssi);
|
||||
}
|
||||
|
||||
/**
|
||||
* The medium where a NearbyDevice was discovered on.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@IntDef({Medium.BLE, Medium.BLUETOOTH})
|
||||
public @interface Medium {
|
||||
int BLE = 1;
|
||||
int BLUETOOTH = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2012, 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;
|
||||
|
||||
parcelable NearbyDeviceParcelable;
|
||||
|
||||
343
nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
Normal file
343
nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 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.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.bluetooth.le.ScanRecord;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A data class representing scan result from Nearby Service. Scan result can come from multiple
|
||||
* mediums like BLE, Wi-Fi Aware, and etc.
|
||||
* A scan result consists of
|
||||
* An encapsulation of various parameters for requesting nearby scans.
|
||||
*
|
||||
* <p>All scan results generated through {@link NearbyManager} are guaranteed to have a valid
|
||||
* medium, identifier, timestamp (both UTC time and elapsed real-time since boot), and accuracy.
|
||||
* All other parameters are optional.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class NearbyDeviceParcelable implements Parcelable {
|
||||
|
||||
/**
|
||||
* Used to read a NearbyDeviceParcelable from a Parcel.
|
||||
*/
|
||||
@NonNull
|
||||
public static final Creator<NearbyDeviceParcelable> CREATOR =
|
||||
new Creator<NearbyDeviceParcelable>() {
|
||||
@Override
|
||||
public NearbyDeviceParcelable createFromParcel(Parcel in) {
|
||||
Builder builder = new Builder();
|
||||
if (in.readInt() == 1) {
|
||||
builder.setName(in.readString());
|
||||
}
|
||||
builder.setMedium(in.readInt());
|
||||
builder.setRssi(in.readInt());
|
||||
if (in.readInt() == 1) {
|
||||
builder.setFastPairModelId(in.readString());
|
||||
}
|
||||
if (in.readInt() == 1) {
|
||||
builder.setBluetoothAddress(in.readString());
|
||||
}
|
||||
if (in.readInt() == 1) {
|
||||
int dataLength = in.readInt();
|
||||
byte[] data = new byte[dataLength];
|
||||
in.readByteArray(data);
|
||||
builder.setData(data);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NearbyDeviceParcelable[] newArray(int size) {
|
||||
return new NearbyDeviceParcelable[size];
|
||||
}
|
||||
};
|
||||
|
||||
@ScanRequest.ScanType int mScanType;
|
||||
@Nullable
|
||||
private final String mName;
|
||||
@NearbyDevice.Medium
|
||||
private final int mMedium;
|
||||
private final int mRssi;
|
||||
|
||||
@Nullable
|
||||
private final String mBluetoothAddress;
|
||||
@Nullable
|
||||
private final String mFastPairModelId;
|
||||
@Nullable
|
||||
private final byte[] mData;
|
||||
|
||||
private NearbyDeviceParcelable(@ScanRequest.ScanType int scanType, @Nullable String name,
|
||||
int medium, int rssi, @Nullable String fastPairModelId,
|
||||
@Nullable String bluetoothAddress, @Nullable byte[] data) {
|
||||
mScanType = scanType;
|
||||
mName = name;
|
||||
mMedium = medium;
|
||||
mRssi = rssi;
|
||||
mFastPairModelId = fastPairModelId;
|
||||
mBluetoothAddress = bluetoothAddress;
|
||||
mData = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* No special parcel contents.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this NearbyDeviceParcelable in to a Parcel.
|
||||
*
|
||||
* @param dest The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mName == null ? 0 : 1);
|
||||
if (mName != null) {
|
||||
dest.writeString(mName);
|
||||
}
|
||||
dest.writeInt(mMedium);
|
||||
dest.writeInt(mRssi);
|
||||
dest.writeInt(mFastPairModelId == null ? 0 : 1);
|
||||
if (mFastPairModelId != null) {
|
||||
dest.writeString(mFastPairModelId);
|
||||
}
|
||||
dest.writeInt(mBluetoothAddress == null ? 0 : 1);
|
||||
if (mBluetoothAddress != null) {
|
||||
dest.writeString(mBluetoothAddress);
|
||||
}
|
||||
dest.writeInt(mData == null ? 0 : 1);
|
||||
if (mData != null) {
|
||||
dest.writeInt(mData.length);
|
||||
dest.writeByteArray(mData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this ScanRequest.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NearbyDeviceParcelable["
|
||||
+ "name=" + mName
|
||||
+ ", medium=" + NearbyDevice.mediumToString(mMedium)
|
||||
+ ", rssi=" + mRssi
|
||||
+ ", bluetoothAddress=" + mBluetoothAddress
|
||||
+ ", fastPairModelId=" + mFastPairModelId
|
||||
+ ", data=" + Arrays.toString(mData)
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof NearbyDeviceParcelable) {
|
||||
NearbyDeviceParcelable otherNearbyDeviceParcelable = (NearbyDeviceParcelable) other;
|
||||
return Objects.equals(mName, otherNearbyDeviceParcelable.mName)
|
||||
&& (mMedium == otherNearbyDeviceParcelable.mMedium)
|
||||
&& (mRssi == otherNearbyDeviceParcelable.mRssi)
|
||||
&& (Objects.equals(
|
||||
mBluetoothAddress, otherNearbyDeviceParcelable.mBluetoothAddress))
|
||||
&& (Objects.equals(
|
||||
mFastPairModelId, otherNearbyDeviceParcelable.mFastPairModelId))
|
||||
&& (Arrays.equals(mData, otherNearbyDeviceParcelable.mData));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
mName, mMedium, mRssi, mBluetoothAddress, mFastPairModelId, Arrays.hashCode(mData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the scan.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@ScanRequest.ScanType
|
||||
public int getScanType() {
|
||||
return mScanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name.
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link android.nearby.NearbyDevice.Medium} of the NearbyDeviceParcelable over which
|
||||
* it is discovered.
|
||||
*/
|
||||
@NearbyDevice.Medium
|
||||
public int getMedium() {
|
||||
return mMedium;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the received signal strength in dBm.
|
||||
*/
|
||||
@IntRange(from = -127, to = 126)
|
||||
public int getRssi() {
|
||||
return mRssi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Fast Pair identifier. Returns {@code null} if there is no Model ID or this is not a
|
||||
* Fast Pair device.
|
||||
*/
|
||||
@Nullable
|
||||
public String getFastPairModelId() {
|
||||
return mFastPairModelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Bluetooth device hardware address. Returns {@code null} if the device is not
|
||||
* discovered by Bluetooth.
|
||||
*/
|
||||
@Nullable
|
||||
public String getBluetoothAddress() {
|
||||
return mBluetoothAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw data from the scanning. Returns {@code null} if there is no extra data.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] getData() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link NearbyDeviceParcelable}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
@Nullable
|
||||
private String mName;
|
||||
@NearbyDevice.Medium
|
||||
private int mMedium;
|
||||
private int mRssi;
|
||||
@ScanRequest.ScanType int mScanType;
|
||||
@Nullable
|
||||
private String mFastPairModelId;
|
||||
@Nullable
|
||||
private String mBluetoothAddress;
|
||||
@Nullable
|
||||
private byte[] mData;
|
||||
|
||||
/**
|
||||
* Sets the scan type of the NearbyDeviceParcelable.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public Builder setScanType(@ScanRequest.ScanType int scanType) {
|
||||
mScanType = scanType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the scanned device.
|
||||
*
|
||||
* @param name The local name of the scanned device.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setName(@Nullable String name) {
|
||||
mName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the medium over which the device is discovered.
|
||||
*
|
||||
* @param medium The {@link NearbyDevice.Medium} over which the device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setMedium(@NearbyDevice.Medium int medium) {
|
||||
mMedium = medium;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RSSI between scanned device and the discovered device.
|
||||
*
|
||||
* @param rssi The received signal strength in dBm.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRssi(int rssi) {
|
||||
mRssi = rssi;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Fast Pair model Id.
|
||||
*
|
||||
* @param fastPairModelId Fast Pair device identifier.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setFastPairModelId(@Nullable String fastPairModelId) {
|
||||
mFastPairModelId = fastPairModelId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bluetooth address.
|
||||
*
|
||||
* @param bluetoothAddress The hardware address of the bluetooth device.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setBluetoothAddress(@Nullable String bluetoothAddress) {
|
||||
mBluetoothAddress = bluetoothAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scanned raw data.
|
||||
*
|
||||
* @param data Data the scan.
|
||||
* For example, {@link ScanRecord#getServiceData()} if scanned by Bluetooth.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setData(@Nullable byte[] data) {
|
||||
mData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a ScanResult.
|
||||
*/
|
||||
@NonNull
|
||||
public NearbyDeviceParcelable build() {
|
||||
return new NearbyDeviceParcelable(mScanType, mName, mMedium, mRssi, mFastPairModelId,
|
||||
mBluetoothAddress, mData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.SystemApi;
|
||||
import android.app.SystemServiceRegistry;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Class for performing registration for all Nearby services.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public final class NearbyFrameworkInitializer {
|
||||
|
||||
private NearbyFrameworkInitializer() {}
|
||||
|
||||
/**
|
||||
* Called by {@link SystemServiceRegistry}'s static initializer and registers all
|
||||
* Nearby services to {@link Context}, so that {@link Context#getSystemService} can return them.
|
||||
*
|
||||
* @throws IllegalStateException if this is called from anywhere besides
|
||||
* {@link SystemServiceRegistry}
|
||||
*/
|
||||
public static void registerServiceWrappers() {
|
||||
SystemServiceRegistry.registerContextAwareService(
|
||||
Context.NEARBY_SERVICE,
|
||||
NearbyManager.class,
|
||||
(context, serviceBinder) -> {
|
||||
INearbyManager service = INearbyManager.Stub.asInterface(serviceBinder);
|
||||
return new NearbyManager(service);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
355
nearby/framework/java/android/nearby/NearbyManager.java
Normal file
355
nearby/framework/java/android/nearby/NearbyManager.java
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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 android.nearby;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* This class provides a way to perform Nearby related operations such as scanning, broadcasting
|
||||
* and connecting to nearby devices.
|
||||
*
|
||||
* <p> To get a {@link NearbyManager} instance, call the
|
||||
* <code>Context.getSystemService(NearbyManager.class)</code>.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@SystemService(Context.NEARBY_SERVICE)
|
||||
public class NearbyManager {
|
||||
|
||||
/**
|
||||
* Represents the scanning state.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@IntDef({
|
||||
ScanStatus.UNKNOWN,
|
||||
ScanStatus.SUCCESS,
|
||||
ScanStatus.ERROR,
|
||||
})
|
||||
public @interface ScanStatus {
|
||||
// Default, invalid state.
|
||||
int UNKNOWN = 0;
|
||||
// The successful state.
|
||||
int SUCCESS = 1;
|
||||
// Failed state.
|
||||
int ERROR = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether allows Fast Pair to scan.
|
||||
*
|
||||
* (0 = disabled, 1 = enabled)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
|
||||
|
||||
@GuardedBy("sScanListeners")
|
||||
private static final WeakHashMap<ScanCallback, WeakReference<ScanListenerTransport>>
|
||||
sScanListeners = new WeakHashMap<>();
|
||||
@GuardedBy("sBroadcastListeners")
|
||||
private static final WeakHashMap<BroadcastCallback, WeakReference<BroadcastListenerTransport>>
|
||||
sBroadcastListeners = new WeakHashMap<>();
|
||||
|
||||
private final INearbyManager mService;
|
||||
|
||||
/**
|
||||
* Creates a new NearbyManager.
|
||||
*
|
||||
* @param service the service object
|
||||
*/
|
||||
NearbyManager(@NonNull INearbyManager service) {
|
||||
mService = service;
|
||||
}
|
||||
|
||||
private static NearbyDevice toClientNearbyDevice(
|
||||
NearbyDeviceParcelable nearbyDeviceParcelable,
|
||||
@ScanRequest.ScanType int scanType) {
|
||||
if (scanType == ScanRequest.SCAN_TYPE_FAST_PAIR) {
|
||||
return new FastPairDevice.Builder()
|
||||
.setName(nearbyDeviceParcelable.getName())
|
||||
.addMedium(nearbyDeviceParcelable.getMedium())
|
||||
.setRssi(nearbyDeviceParcelable.getRssi())
|
||||
.setModelId(nearbyDeviceParcelable.getFastPairModelId())
|
||||
.setBluetoothAddress(nearbyDeviceParcelable.getBluetoothAddress())
|
||||
.setData(nearbyDeviceParcelable.getData()).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start scan for nearby devices with given parameters. Devices matching {@link ScanRequest}
|
||||
* will be delivered through the given callback.
|
||||
*
|
||||
* @param scanRequest various parameters clients send when requesting scanning
|
||||
* @param executor executor where the listener method is called
|
||||
* @param scanCallback the callback to notify clients when there is a scan result
|
||||
*
|
||||
* @return whether scanning was successfully started
|
||||
*/
|
||||
@RequiresPermission(allOf = {android.Manifest.permission.BLUETOOTH_SCAN,
|
||||
android.Manifest.permission.BLUETOOTH_PRIVILEGED})
|
||||
@ScanStatus
|
||||
public int startScan(@NonNull ScanRequest scanRequest,
|
||||
@CallbackExecutor @NonNull Executor executor,
|
||||
@NonNull ScanCallback scanCallback) {
|
||||
Objects.requireNonNull(scanRequest, "scanRequest must not be null");
|
||||
Objects.requireNonNull(scanCallback, "scanCallback must not be null");
|
||||
Objects.requireNonNull(executor, "executor must not be null");
|
||||
|
||||
try {
|
||||
synchronized (sScanListeners) {
|
||||
WeakReference<ScanListenerTransport> reference = sScanListeners.get(scanCallback);
|
||||
ScanListenerTransport transport = reference != null ? reference.get() : null;
|
||||
if (transport == null) {
|
||||
transport = new ScanListenerTransport(scanRequest.getScanType(), scanCallback,
|
||||
executor);
|
||||
} else {
|
||||
Preconditions.checkState(transport.isRegistered());
|
||||
transport.setExecutor(executor);
|
||||
}
|
||||
@ScanStatus int status = mService.registerScanListener(scanRequest, transport);
|
||||
if (status != ScanStatus.SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
sScanListeners.put(scanCallback, new WeakReference<>(transport));
|
||||
return ScanStatus.SUCCESS;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the nearby device scan for the specified callback. The given callback
|
||||
* is guaranteed not to receive any invocations that happen after this method
|
||||
* is invoked.
|
||||
*
|
||||
* Suppressed lint: Registration methods should have overload that accepts delivery Executor.
|
||||
* Already have executor in startScan() method.
|
||||
*
|
||||
* @param scanCallback the callback that was used to start the scan
|
||||
*/
|
||||
@SuppressLint("ExecutorRegistration")
|
||||
@RequiresPermission(allOf = {android.Manifest.permission.BLUETOOTH_SCAN,
|
||||
android.Manifest.permission.BLUETOOTH_PRIVILEGED})
|
||||
public void stopScan(@NonNull ScanCallback scanCallback) {
|
||||
Preconditions.checkArgument(scanCallback != null,
|
||||
"invalid null scanCallback");
|
||||
try {
|
||||
synchronized (sScanListeners) {
|
||||
WeakReference<ScanListenerTransport> reference = sScanListeners.remove(
|
||||
scanCallback);
|
||||
ScanListenerTransport transport = reference != null ? reference.get() : null;
|
||||
if (transport != null) {
|
||||
transport.unregister();
|
||||
mService.unregisterScanListener(transport);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start broadcasting the request using nearby specification.
|
||||
*
|
||||
* @param broadcastRequest request for the nearby broadcast
|
||||
* @param executor executor for running the callback
|
||||
* @param callback callback for notifying the client
|
||||
*/
|
||||
@RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADVERTISE,
|
||||
android.Manifest.permission.BLUETOOTH_PRIVILEGED})
|
||||
public void startBroadcast(@NonNull BroadcastRequest broadcastRequest,
|
||||
@CallbackExecutor @NonNull Executor executor, @NonNull BroadcastCallback callback) {
|
||||
try {
|
||||
synchronized (sBroadcastListeners) {
|
||||
WeakReference<BroadcastListenerTransport> reference = sBroadcastListeners.get(
|
||||
callback);
|
||||
BroadcastListenerTransport transport = reference != null ? reference.get() : null;
|
||||
if (transport == null) {
|
||||
transport = new BroadcastListenerTransport(callback, executor);
|
||||
} else {
|
||||
Preconditions.checkState(transport.isRegistered());
|
||||
transport.setExecutor(executor);
|
||||
}
|
||||
mService.startBroadcast(new BroadcastRequestParcelable(broadcastRequest),
|
||||
transport);
|
||||
sBroadcastListeners.put(callback, new WeakReference<>(transport));
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the broadcast associated with the given callback.
|
||||
*
|
||||
* @param callback the callback that was used for starting the broadcast
|
||||
*/
|
||||
@SuppressLint("ExecutorRegistration")
|
||||
@RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADVERTISE,
|
||||
android.Manifest.permission.BLUETOOTH_PRIVILEGED})
|
||||
public void stopBroadcast(@NonNull BroadcastCallback callback) {
|
||||
try {
|
||||
synchronized (sBroadcastListeners) {
|
||||
WeakReference<BroadcastListenerTransport> reference = sBroadcastListeners.remove(
|
||||
callback);
|
||||
BroadcastListenerTransport transport = reference != null ? reference.get() : null;
|
||||
if (transport != null) {
|
||||
transport.unregister();
|
||||
mService.stopBroadcast(transport);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from {@link Settings} whether Fast Pair scan is enabled.
|
||||
*
|
||||
* @param context the {@link Context} to query the setting
|
||||
* @param def the default value if no setting value
|
||||
* @return whether the Fast Pair is enabled
|
||||
*/
|
||||
public static boolean getFastPairScanEnabled(@NonNull Context context, boolean def) {
|
||||
final int enabled = Settings.Secure.getInt(
|
||||
context.getContentResolver(), FAST_PAIR_SCAN_ENABLED, (def ? 1 : 0));
|
||||
return enabled != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write into {@link Settings} whether Fast Pair scan is enabled
|
||||
*
|
||||
* @param context the {@link Context} to set the setting
|
||||
* @param enable whether the Fast Pair scan should be enabled
|
||||
*/
|
||||
public static void setFastPairScanEnabled(@NonNull Context context, boolean enable) {
|
||||
Settings.Secure.putInt(
|
||||
context.getContentResolver(), FAST_PAIR_SCAN_ENABLED, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
private static class ScanListenerTransport extends IScanListener.Stub {
|
||||
|
||||
private @ScanRequest.ScanType int mScanType;
|
||||
private volatile @Nullable ScanCallback mScanCallback;
|
||||
private Executor mExecutor;
|
||||
|
||||
ScanListenerTransport(@ScanRequest.ScanType int scanType, ScanCallback scanCallback,
|
||||
@CallbackExecutor Executor executor) {
|
||||
Preconditions.checkArgument(scanCallback != null,
|
||||
"invalid null callback");
|
||||
Preconditions.checkState(ScanRequest.isValidScanType(scanType),
|
||||
"invalid scan type : " + scanType
|
||||
+ ", scan type must be one of ScanRequest#SCAN_TYPE_");
|
||||
mScanType = scanType;
|
||||
mScanCallback = scanCallback;
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
void setExecutor(Executor executor) {
|
||||
Preconditions.checkArgument(
|
||||
executor != null, "invalid null executor");
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
boolean isRegistered() {
|
||||
return mScanCallback != null;
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
mScanCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiscovered(NearbyDeviceParcelable nearbyDeviceParcelable)
|
||||
throws RemoteException {
|
||||
mExecutor.execute(() -> mScanCallback.onDiscovered(
|
||||
toClientNearbyDevice(nearbyDeviceParcelable, mScanType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdated(NearbyDeviceParcelable nearbyDeviceParcelable)
|
||||
throws RemoteException {
|
||||
mExecutor.execute(
|
||||
() -> mScanCallback.onUpdated(
|
||||
toClientNearbyDevice(nearbyDeviceParcelable, mScanType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLost(NearbyDeviceParcelable nearbyDeviceParcelable) throws RemoteException {
|
||||
mExecutor.execute(
|
||||
() -> mScanCallback.onLost(
|
||||
toClientNearbyDevice(nearbyDeviceParcelable, mScanType)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class BroadcastListenerTransport extends IBroadcastListener.Stub {
|
||||
private volatile @Nullable BroadcastCallback mBroadcastCallback;
|
||||
private Executor mExecutor;
|
||||
|
||||
BroadcastListenerTransport(BroadcastCallback broadcastCallback,
|
||||
@CallbackExecutor Executor executor) {
|
||||
mBroadcastCallback = broadcastCallback;
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
void setExecutor(Executor executor) {
|
||||
Preconditions.checkArgument(
|
||||
executor != null, "invalid null executor");
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
boolean isRegistered() {
|
||||
return mBroadcastCallback != null;
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
mBroadcastCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(int status) {
|
||||
mExecutor.execute(() -> {
|
||||
if (mBroadcastCallback != null) {
|
||||
mBroadcastCallback.onStatusChanged(status);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
24
nearby/framework/java/android/nearby/PairStatusMetadata.aidl
Normal file
24
nearby/framework/java/android/nearby/PairStatusMetadata.aidl
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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;
|
||||
117
nearby/framework/java/android/nearby/PairStatusMetadata.java
Normal file
117
nearby/framework/java/android/nearby/PairStatusMetadata.java
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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<PairStatusMetadata> CREATOR = new Creator<PairStatusMetadata>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Request for Nearby Presence Broadcast.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class PresenceBroadcastRequest extends BroadcastRequest implements Parcelable {
|
||||
private final byte[] mSalt;
|
||||
private final List<Integer> mActions;
|
||||
private final PrivateCredential mCredential;
|
||||
private final List<DataElement> mExtendedProperties;
|
||||
|
||||
private PresenceBroadcastRequest(@BroadcastVersion int version, int txPower,
|
||||
List<Integer> mediums, byte[] salt, List<Integer> actions,
|
||||
PrivateCredential credential, List<DataElement> extendedProperties) {
|
||||
super(BROADCAST_TYPE_NEARBY_PRESENCE, version, txPower, mediums);
|
||||
mSalt = salt;
|
||||
mActions = actions;
|
||||
mCredential = credential;
|
||||
mExtendedProperties = extendedProperties;
|
||||
}
|
||||
|
||||
private PresenceBroadcastRequest(Parcel in) {
|
||||
super(BROADCAST_TYPE_NEARBY_PRESENCE, in);
|
||||
mSalt = new byte[in.readInt()];
|
||||
in.readByteArray(mSalt);
|
||||
|
||||
mActions = new ArrayList<>();
|
||||
in.readList(mActions, Integer.class.getClassLoader(), Integer.class);
|
||||
mCredential = in.readParcelable(PrivateCredential.class.getClassLoader(),
|
||||
PrivateCredential.class);
|
||||
mExtendedProperties = new ArrayList<>();
|
||||
in.readList(mExtendedProperties, DataElement.class.getClassLoader(), DataElement.class);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<PresenceBroadcastRequest> CREATOR =
|
||||
new Creator<PresenceBroadcastRequest>() {
|
||||
@Override
|
||||
public PresenceBroadcastRequest createFromParcel(Parcel in) {
|
||||
// Skip Broadcast request type - it's used by parent class.
|
||||
in.readInt();
|
||||
return createFromParcelBody(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PresenceBroadcastRequest[] newArray(int size) {
|
||||
return new PresenceBroadcastRequest[size];
|
||||
}
|
||||
};
|
||||
|
||||
static PresenceBroadcastRequest createFromParcelBody(Parcel in) {
|
||||
return new PresenceBroadcastRequest(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the salt associated with this broadcast request.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getSalt() {
|
||||
return mSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns actions associated with this broadcast request.
|
||||
*/
|
||||
@NonNull
|
||||
public List<Integer> getActions() {
|
||||
return mActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private credential associated with this broadcast request.
|
||||
*/
|
||||
@NonNull
|
||||
public PrivateCredential getCredential() {
|
||||
return mCredential;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extended property information associated with this broadcast request.
|
||||
*/
|
||||
@NonNull
|
||||
public List<DataElement> getExtendedProperties() {
|
||||
return mExtendedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mSalt.length);
|
||||
dest.writeByteArray(mSalt);
|
||||
dest.writeList(mActions);
|
||||
dest.writeParcelable(mCredential, /** parcelableFlags= */0);
|
||||
dest.writeList(mExtendedProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link PresenceBroadcastRequest}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final List<Integer> mMediums;
|
||||
private final List<Integer> mActions;
|
||||
private final List<DataElement> mExtendedProperties;
|
||||
private final byte[] mSalt;
|
||||
private final PrivateCredential mCredential;
|
||||
|
||||
private int mVersion;
|
||||
private int mTxPower;
|
||||
|
||||
public Builder(@NonNull List<Integer> mediums, @NonNull byte[] salt,
|
||||
@NonNull PrivateCredential credential) {
|
||||
Preconditions.checkState(!mediums.isEmpty(), "mediums cannot be empty");
|
||||
Preconditions.checkState(salt != null && salt.length > 0, "salt cannot be empty");
|
||||
|
||||
mVersion = PRESENCE_VERSION_V0;
|
||||
mTxPower = UNKNOWN_TX_POWER;
|
||||
mCredential = credential;
|
||||
mActions = new ArrayList<>();
|
||||
mExtendedProperties = new ArrayList<>();
|
||||
|
||||
mSalt = salt;
|
||||
mMediums = mediums;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the version for this request.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setVersion(@BroadcastVersion int version) {
|
||||
mVersion = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the calibrated tx power level in dBm for this request. The tx power level should
|
||||
* be between -127 dBm and 126 dBm.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTxPower(@IntRange(from = -127, to = 126) int txPower) {
|
||||
mTxPower = txPower;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action for the presence broadcast request.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addAction(@IntRange(from = 1, to = 255) int action) {
|
||||
mActions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extended property for the presence broadcast request.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addExtendedProperty(@NonNull DataElement dataElement) {
|
||||
Objects.requireNonNull(dataElement);
|
||||
mExtendedProperties.add(dataElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link PresenceBroadcastRequest}.
|
||||
*/
|
||||
@NonNull
|
||||
public PresenceBroadcastRequest build() {
|
||||
return new PresenceBroadcastRequest(mVersion, mTxPower, mMediums, mSalt, mActions,
|
||||
mCredential, mExtendedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
160
nearby/framework/java/android/nearby/PresenceCredential.java
Normal file
160
nearby/framework/java/android/nearby/PresenceCredential.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a credential for Nearby Presence.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class PresenceCredential {
|
||||
/**
|
||||
* Private credential type.
|
||||
*/
|
||||
public static final int CREDENTIAL_TYPE_PRIVATE = 0;
|
||||
|
||||
/**
|
||||
* Public credential type.
|
||||
*/
|
||||
public static final int CREDENTIAL_TYPE_PUBLIC = 1;
|
||||
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({CREDENTIAL_TYPE_PUBLIC, CREDENTIAL_TYPE_PRIVATE})
|
||||
public @interface CredentialType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown identity type.
|
||||
*/
|
||||
public static final int IDENTITY_TYPE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Private identity type.
|
||||
*/
|
||||
public static final int IDENTITY_TYPE_PRIVATE = 1;
|
||||
/**
|
||||
* Provisioned identity type.
|
||||
*/
|
||||
public static final int IDENTITY_TYPE_PROVISIONED = 2;
|
||||
/**
|
||||
* Trusted identity type.
|
||||
*/
|
||||
public static final int IDENTITY_TYPE_TRUSTED = 3;
|
||||
/**
|
||||
* Public identity type.
|
||||
*/
|
||||
public static final int IDENTITY_TYPE_PUBLIC = 4;
|
||||
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({IDENTITY_TYPE_UNKNOWN, IDENTITY_TYPE_PRIVATE, IDENTITY_TYPE_PROVISIONED,
|
||||
IDENTITY_TYPE_TRUSTED, IDENTITY_TYPE_PUBLIC})
|
||||
public @interface IdentityType {
|
||||
}
|
||||
|
||||
private final @CredentialType int mType;
|
||||
private final @IdentityType int mIdentityType;
|
||||
private final byte[] mSecretId;
|
||||
private final byte[] mAuthenticityKey;
|
||||
private final List<CredentialElement> mCredentialElements;
|
||||
|
||||
PresenceCredential(@CredentialType int type, @IdentityType int identityType,
|
||||
byte[] secretId, byte[] authenticityKey, List<CredentialElement> credentialElements) {
|
||||
mType = type;
|
||||
mIdentityType = identityType;
|
||||
mSecretId = secretId;
|
||||
mAuthenticityKey = authenticityKey;
|
||||
mCredentialElements = credentialElements;
|
||||
}
|
||||
|
||||
PresenceCredential(@CredentialType int type, Parcel in) {
|
||||
mType = type;
|
||||
mIdentityType = in.readInt();
|
||||
mSecretId = new byte[in.readInt()];
|
||||
in.readByteArray(mSecretId);
|
||||
mAuthenticityKey = new byte[in.readInt()];
|
||||
in.readByteArray(mAuthenticityKey);
|
||||
mCredentialElements = new ArrayList<>();
|
||||
in.readList(mCredentialElements, CredentialElement.class.getClassLoader(),
|
||||
CredentialElement.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the credential.
|
||||
*/
|
||||
public @CredentialType int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identity type of the credential.
|
||||
*/
|
||||
public @IdentityType int getIdentityType() {
|
||||
return mIdentityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the secret id of the credential.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getSecretId() {
|
||||
return mSecretId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authenticity key of the credential.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getAuthenticityKey() {
|
||||
return mAuthenticityKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elements of the credential.
|
||||
*/
|
||||
@NonNull
|
||||
public List<CredentialElement> getCredentialElements() {
|
||||
return mCredentialElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the presence credential to the parcel.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mType);
|
||||
dest.writeInt(mIdentityType);
|
||||
dest.writeInt(mSecretId.length);
|
||||
dest.writeByteArray(mSecretId);
|
||||
dest.writeInt(mAuthenticityKey.length);
|
||||
dest.writeByteArray(mAuthenticityKey);
|
||||
dest.writeList(mCredentialElements);
|
||||
}
|
||||
}
|
||||
373
nearby/framework/java/android/nearby/PresenceDevice.java
Normal file
373
nearby/framework/java/android/nearby/PresenceDevice.java
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a Presence device from nearby scans.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class PresenceDevice extends NearbyDevice implements Parcelable {
|
||||
|
||||
/** The type of presence device. */
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
DeviceType.UNKNOWN,
|
||||
DeviceType.PHONE,
|
||||
DeviceType.TABLET,
|
||||
DeviceType.DISPLAY,
|
||||
DeviceType.LAPTOP,
|
||||
DeviceType.TV,
|
||||
DeviceType.WATCH,
|
||||
})
|
||||
public @interface DeviceType {
|
||||
/** The type of the device is unknown. */
|
||||
int UNKNOWN = 0;
|
||||
/** The device is a phone. */
|
||||
int PHONE = 1;
|
||||
/** The device is a tablet. */
|
||||
int TABLET = 2;
|
||||
/** The device is a display. */
|
||||
int DISPLAY = 3;
|
||||
/** The device is a laptop. */
|
||||
int LAPTOP = 4;
|
||||
/** The device is a TV. */
|
||||
int TV = 5;
|
||||
/** The device is a watch. */
|
||||
int WATCH = 6;
|
||||
}
|
||||
|
||||
private final String mDeviceId;
|
||||
private final byte[] mSalt;
|
||||
private final byte[] mSecretId;
|
||||
private final byte[] mEncryptedIdentity;
|
||||
private final int mDeviceType;
|
||||
private final String mDeviceImageUrl;
|
||||
private final long mDiscoveryTimestampMillis;
|
||||
private final List<DataElement> mExtendedProperties;
|
||||
|
||||
/**
|
||||
* The id of the device.
|
||||
*
|
||||
* <p>This id is not a hardware id. It may rotate based on the remote device's broadcasts.
|
||||
*/
|
||||
@NonNull
|
||||
public String getDeviceId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the salt used when presence device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getSalt() {
|
||||
return mSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the secret used when presence device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getSecretId() {
|
||||
return mSecretId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encrypted identity used when presence device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getEncryptedIdentity() {
|
||||
return mEncryptedIdentity;
|
||||
}
|
||||
|
||||
/** The type of the device. */
|
||||
@DeviceType
|
||||
public int getDeviceType() {
|
||||
return mDeviceType;
|
||||
}
|
||||
|
||||
/** An image URL representing the device. */
|
||||
@Nullable
|
||||
public String getDeviceImageUrl() {
|
||||
return mDeviceImageUrl;
|
||||
}
|
||||
|
||||
/** The timestamp (since boot) when the device is discovered. */
|
||||
public long getDiscoveryTimestampMillis() {
|
||||
return mDiscoveryTimestampMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extended properties of the device.
|
||||
*/
|
||||
@NonNull
|
||||
public List<DataElement> getExtendedProperties() {
|
||||
return mExtendedProperties;
|
||||
}
|
||||
|
||||
private PresenceDevice(String deviceName, List<Integer> mMediums, int rssi, String deviceId,
|
||||
byte[] salt, byte[] secretId, byte[] encryptedIdentity, int deviceType,
|
||||
String deviceImageUrl, long discoveryTimestampMillis,
|
||||
List<DataElement> extendedProperties) {
|
||||
super(deviceName, mMediums, rssi);
|
||||
mDeviceId = deviceId;
|
||||
mSalt = salt;
|
||||
mSecretId = secretId;
|
||||
mEncryptedIdentity = encryptedIdentity;
|
||||
mDeviceType = deviceType;
|
||||
mDeviceImageUrl = deviceImageUrl;
|
||||
mDiscoveryTimestampMillis = discoveryTimestampMillis;
|
||||
mExtendedProperties = extendedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
String name = getName();
|
||||
dest.writeInt(name == null ? 0 : 1);
|
||||
if (name != null) {
|
||||
dest.writeString(name);
|
||||
}
|
||||
List<Integer> mediums = getMediums();
|
||||
dest.writeInt(mediums.size());
|
||||
for (int medium : mediums) {
|
||||
dest.writeInt(medium);
|
||||
}
|
||||
dest.writeInt(getRssi());
|
||||
dest.writeInt(mSalt.length);
|
||||
dest.writeByteArray(mSalt);
|
||||
dest.writeInt(mSecretId.length);
|
||||
dest.writeByteArray(mSecretId);
|
||||
dest.writeInt(mEncryptedIdentity.length);
|
||||
dest.writeByteArray(mEncryptedIdentity);
|
||||
dest.writeString(mDeviceId);
|
||||
dest.writeInt(mDeviceType);
|
||||
dest.writeInt(mDeviceImageUrl == null ? 0 : 1);
|
||||
if (mDeviceImageUrl != null) {
|
||||
dest.writeString(mDeviceImageUrl);
|
||||
}
|
||||
dest.writeLong(mDiscoveryTimestampMillis);
|
||||
dest.writeInt(mExtendedProperties.size());
|
||||
for (DataElement dataElement : mExtendedProperties) {
|
||||
dest.writeParcelable(dataElement, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<PresenceDevice> CREATOR = new Creator<PresenceDevice>() {
|
||||
@Override
|
||||
public PresenceDevice createFromParcel(Parcel in) {
|
||||
String name = null;
|
||||
if (in.readInt() == 1) {
|
||||
name = in.readString();
|
||||
}
|
||||
int size = in.readInt();
|
||||
List<Integer> mediums = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
mediums.add(in.readInt());
|
||||
}
|
||||
int rssi = in.readInt();
|
||||
byte[] salt = new byte[in.readInt()];
|
||||
in.readByteArray(salt);
|
||||
byte[] secretId = new byte[in.readInt()];
|
||||
in.readByteArray(secretId);
|
||||
byte[] encryptedIdentity = new byte[in.readInt()];
|
||||
in.readByteArray(encryptedIdentity);
|
||||
String deviceId = in.readString();
|
||||
int deviceType = in.readInt();
|
||||
String deviceImageUrl = null;
|
||||
if (in.readInt() == 1) {
|
||||
deviceImageUrl = in.readString();
|
||||
}
|
||||
long discoveryTimeMillis = in.readLong();
|
||||
int dataElementSize = in.readInt();
|
||||
List<DataElement> dataElements = new ArrayList<>();
|
||||
for (int i = 0; i < dataElementSize; i++) {
|
||||
dataElements.add(
|
||||
in.readParcelable(DataElement.class.getClassLoader(), DataElement.class));
|
||||
}
|
||||
Builder builder = new Builder(deviceId, salt, secretId, encryptedIdentity)
|
||||
.setName(name)
|
||||
.setRssi(rssi)
|
||||
.setDeviceType(deviceType)
|
||||
.setDeviceImageUrl(deviceImageUrl)
|
||||
.setDiscoveryTimestampMillis(discoveryTimeMillis);
|
||||
for (int i = 0; i < mediums.size(); i++) {
|
||||
builder.addMedium(mediums.get(i));
|
||||
}
|
||||
for (int i = 0; i < dataElements.size(); i++) {
|
||||
builder.addExtendedProperty(dataElements.get(i));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PresenceDevice[] newArray(int size) {
|
||||
return new PresenceDevice[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Builder class for {@link PresenceDevice}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private final List<DataElement> mExtendedProperties;
|
||||
private final List<Integer> mMediums;
|
||||
private final String mDeviceId;
|
||||
private final byte[] mSalt;
|
||||
private final byte[] mSecretId;
|
||||
private final byte[] mEncryptedIdentity;
|
||||
|
||||
private String mName;
|
||||
private int mRssi;
|
||||
private int mDeviceType;
|
||||
private String mDeviceImageUrl;
|
||||
private long mDiscoveryTimestampMillis;
|
||||
|
||||
/**
|
||||
* Constructs a {@link Builder}.
|
||||
*
|
||||
* @param deviceId the identifier on the discovered Presence device
|
||||
* @param salt a random salt used in the beacon from the Presence device.
|
||||
* @param secretId a secret identifier used in the beacon from the Presence device.
|
||||
* @param encryptedIdentity the identity associated with the Presence device.
|
||||
*/
|
||||
public Builder(@NonNull String deviceId, @NonNull byte[] salt, @NonNull byte[] secretId,
|
||||
@NonNull byte[] encryptedIdentity) {
|
||||
mDeviceId = deviceId;
|
||||
mSalt = salt;
|
||||
mSecretId = secretId;
|
||||
mEncryptedIdentity = encryptedIdentity;
|
||||
mMediums = new ArrayList<>();
|
||||
mExtendedProperties = new ArrayList<>();
|
||||
mRssi = -127;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the Presence device.
|
||||
*
|
||||
* @param name Name of the Presence. Can be {@code null} if there is no name.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setName(@Nullable String name) {
|
||||
mName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the medium over which the Presence device is discovered.
|
||||
*
|
||||
* @param medium The {@link Medium} over which the device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addMedium(@Medium int medium) {
|
||||
mMediums.add(medium);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RSSI on the discovered Presence device.
|
||||
*
|
||||
* @param rssi The received signal strength in dBm.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRssi(int rssi) {
|
||||
mRssi = rssi;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of discovered Presence device.
|
||||
*
|
||||
* @param deviceType Type of the Presence device.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setDeviceType(@DeviceType int deviceType) {
|
||||
mDeviceType = deviceType;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the image url of the discovered Presence device.
|
||||
*
|
||||
* @param deviceImageUrl Url of the image for the Presence device.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setDeviceImageUrl(@Nullable String deviceImageUrl) {
|
||||
mDeviceImageUrl = deviceImageUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets discovery timestamp, the clock is based on elapsed time.
|
||||
*
|
||||
* @param discoveryTimestampMillis Timestamp when the presence device is discovered.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setDiscoveryTimestampMillis(long discoveryTimestampMillis) {
|
||||
mDiscoveryTimestampMillis = discoveryTimestampMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an extended property of the discovered presence device.
|
||||
*
|
||||
* @param dataElement Data element of the extended property.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addExtendedProperty(@NonNull DataElement dataElement) {
|
||||
Objects.requireNonNull(dataElement);
|
||||
mExtendedProperties.add(dataElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Presence device.
|
||||
*/
|
||||
@NonNull
|
||||
public PresenceDevice build() {
|
||||
return new PresenceDevice(mName, mMediums, mRssi, mDeviceId,
|
||||
mSalt, mSecretId, mEncryptedIdentity,
|
||||
mDeviceType,
|
||||
mDeviceImageUrl,
|
||||
mDiscoveryTimestampMillis, mExtendedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
211
nearby/framework/java/android/nearby/PresenceScanFilter.java
Normal file
211
nearby/framework/java/android/nearby/PresenceScanFilter.java
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Filter for scanning a nearby presence device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class PresenceScanFilter extends ScanFilter implements Parcelable {
|
||||
|
||||
private final List<PublicCredential> mCredentials;
|
||||
private final List<Integer> mPresenceActions;
|
||||
private final List<DataElement> mExtendedProperties;
|
||||
|
||||
/**
|
||||
* A list of credentials to filter on.
|
||||
*/
|
||||
@NonNull
|
||||
public List<PublicCredential> getCredentials() {
|
||||
return mCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of presence actions for matching.
|
||||
*/
|
||||
@NonNull
|
||||
public List<Integer> getPresenceActions() {
|
||||
return mPresenceActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* A bundle of extended properties for matching.
|
||||
*/
|
||||
@NonNull
|
||||
public List<DataElement> getExtendedProperties() {
|
||||
return mExtendedProperties;
|
||||
}
|
||||
|
||||
private PresenceScanFilter(int rssiThreshold, List<PublicCredential> credentials,
|
||||
List<Integer> presenceActions, List<DataElement> extendedProperties) {
|
||||
super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, rssiThreshold);
|
||||
mCredentials = new ArrayList<>(credentials);
|
||||
mPresenceActions = new ArrayList<>(presenceActions);
|
||||
mExtendedProperties = extendedProperties;
|
||||
}
|
||||
|
||||
private PresenceScanFilter(Parcel in) {
|
||||
super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, in);
|
||||
mCredentials = new ArrayList<>();
|
||||
if (in.readInt() != 0) {
|
||||
in.readParcelableList(mCredentials, PublicCredential.class.getClassLoader(),
|
||||
PublicCredential.class);
|
||||
}
|
||||
mPresenceActions = new ArrayList<>();
|
||||
if (in.readInt() != 0) {
|
||||
in.readList(mPresenceActions, Integer.class.getClassLoader(), Integer.class);
|
||||
}
|
||||
mExtendedProperties = new ArrayList<>();
|
||||
if (in.readInt() != 0) {
|
||||
in.readParcelableList(mExtendedProperties, DataElement.class.getClassLoader(),
|
||||
DataElement.class);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<PresenceScanFilter> CREATOR = new Creator<PresenceScanFilter>() {
|
||||
@Override
|
||||
public PresenceScanFilter createFromParcel(Parcel in) {
|
||||
// Skip Scan Filter type as it's used for parent class.
|
||||
in.readInt();
|
||||
return createFromParcelBody(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PresenceScanFilter[] newArray(int size) {
|
||||
return new PresenceScanFilter[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a {@link PresenceScanFilter} from the parcel body. Scan Filter type is skipped.
|
||||
*/
|
||||
static PresenceScanFilter createFromParcelBody(Parcel in) {
|
||||
return new PresenceScanFilter(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mCredentials.size());
|
||||
if (!mCredentials.isEmpty()) {
|
||||
dest.writeParcelableList(mCredentials, 0);
|
||||
}
|
||||
dest.writeInt(mPresenceActions.size());
|
||||
if (!mPresenceActions.isEmpty()) {
|
||||
dest.writeList(mPresenceActions);
|
||||
}
|
||||
dest.writeInt(mExtendedProperties.size());
|
||||
if (!mExtendedProperties.isEmpty()) {
|
||||
dest.writeList(mExtendedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link PresenceScanFilter}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private int mMaxPathLoss;
|
||||
private final Set<PublicCredential> mCredentials;
|
||||
private final Set<Integer> mPresenceIdentities;
|
||||
private final Set<Integer> mPresenceActions;
|
||||
private final List<DataElement> mExtendedProperties;
|
||||
|
||||
public Builder() {
|
||||
mMaxPathLoss = 127;
|
||||
mCredentials = new ArraySet<>();
|
||||
mPresenceIdentities = new ArraySet<>();
|
||||
mPresenceActions = new ArraySet<>();
|
||||
mExtendedProperties = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max path loss (in dBm) for the scan request. The path loss is the attenuation
|
||||
* of radio energy between sender and receiver. Path loss here is defined as (TxPower -
|
||||
* Rssi).
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setMaxPathLoss(@IntRange(from = 0, to = 127) int maxPathLoss) {
|
||||
mMaxPathLoss = maxPathLoss;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a credential the scan filter is expected to match.
|
||||
*/
|
||||
|
||||
@NonNull
|
||||
public Builder addCredential(@NonNull PublicCredential credential) {
|
||||
Objects.requireNonNull(credential);
|
||||
mCredentials.add(credential);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a presence action for filtering, which is an action the discoverer could take
|
||||
* when it receives the broadcast of a presence device.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addPresenceAction(@IntRange(from = 1, to = 255) int action) {
|
||||
mPresenceActions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an extended property for scan filtering.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addExtendedProperty(@NonNull DataElement dataElement) {
|
||||
Objects.requireNonNull(dataElement);
|
||||
mExtendedProperties.add(dataElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the scan filter.
|
||||
*/
|
||||
@NonNull
|
||||
public PresenceScanFilter build() {
|
||||
Preconditions.checkState(!mCredentials.isEmpty(), "credentials cannot be empty");
|
||||
return new PresenceScanFilter(mMaxPathLoss,
|
||||
new ArrayList<>(mCredentials),
|
||||
new ArrayList<>(mPresenceActions),
|
||||
mExtendedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
nearby/framework/java/android/nearby/PrivateCredential.java
Normal file
161
nearby/framework/java/android/nearby/PrivateCredential.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a private credential.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class PrivateCredential extends PresenceCredential implements Parcelable {
|
||||
|
||||
@NonNull
|
||||
public static final Creator<PrivateCredential> CREATOR = new Creator<PrivateCredential>() {
|
||||
@Override
|
||||
public PrivateCredential createFromParcel(Parcel in) {
|
||||
in.readInt(); // Skip the type as it's used by parent class only.
|
||||
return createFromParcelBody(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateCredential[] newArray(int size) {
|
||||
return new PrivateCredential[size];
|
||||
}
|
||||
};
|
||||
|
||||
private byte[] mMetadataEncryptionKey;
|
||||
private String mDeviceName;
|
||||
|
||||
private PrivateCredential(Parcel in) {
|
||||
super(CREDENTIAL_TYPE_PRIVATE, in);
|
||||
mMetadataEncryptionKey = new byte[in.readInt()];
|
||||
in.readByteArray(mMetadataEncryptionKey);
|
||||
mDeviceName = in.readString();
|
||||
}
|
||||
|
||||
private PrivateCredential(int identityType, byte[] secretId,
|
||||
String deviceName, byte[] authenticityKey, List<CredentialElement> credentialElements,
|
||||
byte[] metadataEncryptionKey) {
|
||||
super(CREDENTIAL_TYPE_PRIVATE, identityType, secretId, authenticityKey,
|
||||
credentialElements);
|
||||
mDeviceName = deviceName;
|
||||
mMetadataEncryptionKey = metadataEncryptionKey;
|
||||
}
|
||||
|
||||
static PrivateCredential createFromParcelBody(Parcel in) {
|
||||
return new PrivateCredential(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mMetadataEncryptionKey.length);
|
||||
dest.writeByteArray(mMetadataEncryptionKey);
|
||||
dest.writeString(mDeviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata encryption key associated with this credential.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getMetadataEncryptionKey() {
|
||||
return mMetadataEncryptionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device name associated with this credential.
|
||||
*/
|
||||
@NonNull
|
||||
public String getDeviceName() {
|
||||
return mDeviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link PresenceCredential}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final List<CredentialElement> mCredentialElements;
|
||||
|
||||
private @IdentityType int mIdentityType;
|
||||
private final byte[] mSecretId;
|
||||
private final byte[] mAuthenticityKey;
|
||||
private final byte[] mMetadataEncryptionKey;
|
||||
private final String mDeviceName;
|
||||
|
||||
public Builder(@NonNull byte[] secretId, @NonNull byte[] authenticityKey,
|
||||
@NonNull byte[] metadataEncryptionKey, @NonNull String deviceName) {
|
||||
Preconditions.checkState(secretId != null && secretId.length > 0,
|
||||
"secret id cannot be empty");
|
||||
Preconditions.checkState(authenticityKey != null && authenticityKey.length > 0,
|
||||
"authenticity key cannot be empty");
|
||||
Preconditions.checkState(
|
||||
metadataEncryptionKey != null && metadataEncryptionKey.length > 0,
|
||||
"metadataEncryptionKey cannot be empty");
|
||||
Preconditions.checkState(deviceName != null && deviceName.length() > 0,
|
||||
"deviceName cannot be empty");
|
||||
mSecretId = secretId;
|
||||
mAuthenticityKey = authenticityKey;
|
||||
mMetadataEncryptionKey = metadataEncryptionKey;
|
||||
mDeviceName = deviceName;
|
||||
mCredentialElements = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identity type for the presence credential.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setIdentityType(@IdentityType int identityType) {
|
||||
mIdentityType = identityType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the credential.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addCredentialElement(@NonNull CredentialElement credentialElement) {
|
||||
mCredentialElements.add(credentialElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link PresenceCredential}.
|
||||
*/
|
||||
@NonNull
|
||||
public PrivateCredential build() {
|
||||
return new PrivateCredential(mIdentityType, mSecretId, mDeviceName,
|
||||
mAuthenticityKey, mCredentialElements, mMetadataEncryptionKey);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
184
nearby/framework/java/android/nearby/PublicCredential.java
Normal file
184
nearby/framework/java/android/nearby/PublicCredential.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a public credential.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class PublicCredential extends PresenceCredential implements Parcelable {
|
||||
@NonNull
|
||||
public static final Creator<PublicCredential> CREATOR = new Creator<PublicCredential>() {
|
||||
@Override
|
||||
public PublicCredential createFromParcel(Parcel in) {
|
||||
in.readInt(); // Skip the type as it's used by parent class only.
|
||||
return createFromParcelBody(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicCredential[] newArray(int size) {
|
||||
return new PublicCredential[size];
|
||||
}
|
||||
};
|
||||
|
||||
private final byte[] mPublicKey;
|
||||
private final byte[] mEncryptedMetadata;
|
||||
private final byte[] mEncryptedMetadataKeyTag;
|
||||
|
||||
private PublicCredential(int identityType, byte[] secretId, byte[] authenticityKey,
|
||||
List<CredentialElement> credentialElements, byte[] publicKey, byte[] encryptedMetadata,
|
||||
byte[] metadataEncryptionKeyTag) {
|
||||
super(CREDENTIAL_TYPE_PUBLIC, identityType, secretId, authenticityKey, credentialElements);
|
||||
mPublicKey = publicKey;
|
||||
mEncryptedMetadata = encryptedMetadata;
|
||||
mEncryptedMetadataKeyTag = metadataEncryptionKeyTag;
|
||||
}
|
||||
|
||||
private PublicCredential(Parcel in) {
|
||||
super(CREDENTIAL_TYPE_PUBLIC, in);
|
||||
mPublicKey = new byte[in.readInt()];
|
||||
in.readByteArray(mPublicKey);
|
||||
mEncryptedMetadata = new byte[in.readInt()];
|
||||
in.readByteArray(mEncryptedMetadata);
|
||||
mEncryptedMetadataKeyTag = new byte[in.readInt()];
|
||||
in.readByteArray(mEncryptedMetadataKeyTag);
|
||||
}
|
||||
|
||||
static PublicCredential createFromParcelBody(Parcel in) {
|
||||
return new PublicCredential(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key associated with this credential.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getPublicKey() {
|
||||
return mPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encrypted metadata associated with this credential.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getEncryptedMetadata() {
|
||||
return mEncryptedMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata encryption key tag associated with this credential.
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] getEncryptedMetadataKeyTag() {
|
||||
return mEncryptedMetadataKeyTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mPublicKey.length);
|
||||
dest.writeByteArray(mPublicKey);
|
||||
dest.writeInt(mEncryptedMetadata.length);
|
||||
dest.writeByteArray(mEncryptedMetadata);
|
||||
dest.writeInt(mEncryptedMetadataKeyTag.length);
|
||||
dest.writeByteArray(mEncryptedMetadataKeyTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link PresenceCredential}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final List<CredentialElement> mCredentialElements;
|
||||
|
||||
private @IdentityType int mIdentityType;
|
||||
private final byte[] mSecretId;
|
||||
private final byte[] mAuthenticityKey;
|
||||
private final byte[] mPublicKey;
|
||||
private final byte[] mEncryptedMetadata;
|
||||
private final byte[] mEncryptedMetadataKeyTag;
|
||||
|
||||
public Builder(@NonNull byte[] secretId, @NonNull byte[] authenticityKey,
|
||||
@NonNull byte[] publicKey, @NonNull byte[] encryptedMetadata,
|
||||
@NonNull byte[] encryptedMetadataKeyTag) {
|
||||
Preconditions.checkState(secretId != null && secretId.length > 0,
|
||||
"secret id cannot be empty");
|
||||
Preconditions.checkState(authenticityKey != null && authenticityKey.length > 0,
|
||||
"authenticity key cannot be empty");
|
||||
Preconditions.checkState(
|
||||
publicKey != null && publicKey.length > 0,
|
||||
"publicKey cannot be empty");
|
||||
Preconditions.checkState(encryptedMetadata != null && encryptedMetadata.length > 0,
|
||||
"encryptedMetadata cannot be empty");
|
||||
Preconditions.checkState(
|
||||
encryptedMetadataKeyTag != null && encryptedMetadataKeyTag.length > 0,
|
||||
"encryptedMetadataKeyTag cannot be empty");
|
||||
|
||||
mSecretId = secretId;
|
||||
mAuthenticityKey = authenticityKey;
|
||||
mPublicKey = publicKey;
|
||||
mEncryptedMetadata = encryptedMetadata;
|
||||
mEncryptedMetadataKeyTag = encryptedMetadataKeyTag;
|
||||
mCredentialElements = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identity type for the presence credential.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setIdentityType(@IdentityType int identityType) {
|
||||
mIdentityType = identityType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the credential.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addCredentialElement(@NonNull CredentialElement credentialElement) {
|
||||
Objects.requireNonNull(credentialElement);
|
||||
mCredentialElements.add(credentialElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link PresenceCredential}.
|
||||
*/
|
||||
@NonNull
|
||||
public PublicCredential build() {
|
||||
return new PublicCredential(mIdentityType, mSecretId, mAuthenticityKey,
|
||||
mCredentialElements, mPublicKey, mEncryptedMetadata, mEncryptedMetadataKeyTag);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
54
nearby/framework/java/android/nearby/ScanCallback.java
Normal file
54
nearby/framework/java/android/nearby/ScanCallback.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.SystemApi;
|
||||
|
||||
/**
|
||||
* Reports newly discovered devices.
|
||||
* Note: The frequency of the callback is dependent on whether the caller
|
||||
* is in the foreground or background. Foreground callbacks will occur
|
||||
* as fast as the underlying medium supports, whereas background
|
||||
* use cases will be rate limited to improve performance (ie, only on
|
||||
* found/lost/significant changes).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface ScanCallback {
|
||||
/**
|
||||
* Reports a {@link NearbyDevice} being discovered.
|
||||
*
|
||||
* @param device {@link NearbyDevice} that is found.
|
||||
*/
|
||||
void onDiscovered(@NonNull NearbyDevice device);
|
||||
|
||||
/**
|
||||
* Reports a {@link NearbyDevice} information(distance, packet, and etc) changed.
|
||||
*
|
||||
* @param device {@link NearbyDevice} that has updates.
|
||||
*/
|
||||
void onUpdated(@NonNull NearbyDevice device);
|
||||
|
||||
/**
|
||||
* Reports a {@link NearbyDevice} is no longer within range.
|
||||
*
|
||||
* @param device {@link NearbyDevice} that is lost.
|
||||
*/
|
||||
void onLost(@NonNull NearbyDevice device);
|
||||
}
|
||||
99
nearby/framework/java/android/nearby/ScanFilter.java
Normal file
99
nearby/framework/java/android/nearby/ScanFilter.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
|
||||
/**
|
||||
* Filter for scanning a nearby device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class ScanFilter {
|
||||
/**
|
||||
* Creates a {@link ScanFilter} from the parcel.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static ScanFilter createFromParcel(Parcel in) {
|
||||
int type = in.readInt();
|
||||
switch (type) {
|
||||
// Currently, only Nearby Presence filtering is supported, in the future
|
||||
// filtering other nearby specifications will be added.
|
||||
case ScanRequest.SCAN_TYPE_NEARBY_PRESENCE:
|
||||
return PresenceScanFilter.createFromParcelBody(in);
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"Unexpected scan type (value " + type + ") in parcel.");
|
||||
}
|
||||
}
|
||||
|
||||
private final @ScanRequest.ScanType int mType;
|
||||
private final int mMaxPathLoss;
|
||||
|
||||
/**
|
||||
* Constructs a Scan Filter.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
ScanFilter(@ScanRequest.ScanType int type, @IntRange(from = 0, to = 127) int maxPathLoss) {
|
||||
mType = type;
|
||||
mMaxPathLoss = maxPathLoss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Scan Filter.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
ScanFilter(@ScanRequest.ScanType int type, Parcel in) {
|
||||
mType = type;
|
||||
mMaxPathLoss = in.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this scan filter.
|
||||
*/
|
||||
public @ScanRequest.ScanType int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum path loss (in dBm) of the received scan result. The path loss is the
|
||||
* attenuation of radio energy between sender and receiver. Path loss here is defined as
|
||||
* (TxPower - Rssi).
|
||||
*/
|
||||
@IntRange(from = 0, to = 127)
|
||||
public int getMaxPathLoss() {
|
||||
return mMaxPathLoss;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Writes the scan filter to the parcel.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mType);
|
||||
dest.writeInt(mMaxPathLoss);
|
||||
}
|
||||
}
|
||||
19
nearby/framework/java/android/nearby/ScanRequest.aidl
Normal file
19
nearby/framework/java/android/nearby/ScanRequest.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
parcelable ScanRequest;
|
||||
366
nearby/framework/java/android/nearby/ScanRequest.java
Normal file
366
nearby/framework/java/android/nearby/ScanRequest.java
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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.Manifest;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.WorkSource;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An encapsulation of various parameters for requesting nearby scans.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class ScanRequest implements Parcelable {
|
||||
|
||||
/** Scan type for scanning devices using fast pair protocol. */
|
||||
public static final int SCAN_TYPE_FAST_PAIR = 1;
|
||||
/** Scan type for scanning devices using nearby share protocol. */
|
||||
public static final int SCAN_TYPE_NEARBY_SHARE = 2;
|
||||
/** Scan type for scanning devices using nearby presence protocol. */
|
||||
public static final int SCAN_TYPE_NEARBY_PRESENCE = 3;
|
||||
/** Scan type for scanning devices using exposure notification protocol. */
|
||||
public static final int SCAN_TYPE_EXPOSURE_NOTIFICATION = 4;
|
||||
|
||||
/** Scan mode uses highest duty cycle. */
|
||||
public static final int SCAN_MODE_LOW_LATENCY = 2;
|
||||
/** Scan in balanced power mode.
|
||||
* Scan results are returned at a rate that provides a good trade-off between scan
|
||||
* frequency and power consumption.
|
||||
*/
|
||||
public static final int SCAN_MODE_BALANCED = 1;
|
||||
/** Perform scan in low power mode. This is the default scan mode. */
|
||||
public static final int SCAN_MODE_LOW_POWER = 0;
|
||||
/**
|
||||
* A special scan mode. Applications using this scan mode will passively listen for other scan
|
||||
* results without starting BLE scans themselves.
|
||||
*/
|
||||
public static final int SCAN_MODE_NO_POWER = -1;
|
||||
/**
|
||||
* Used to read a ScanRequest from a Parcel.
|
||||
*/
|
||||
@NonNull
|
||||
public static final Creator<ScanRequest> CREATOR = new Creator<ScanRequest>() {
|
||||
@Override
|
||||
public ScanRequest createFromParcel(Parcel in) {
|
||||
ScanRequest.Builder builder = new ScanRequest.Builder()
|
||||
.setScanType(in.readInt())
|
||||
.setScanMode(in.readInt())
|
||||
.setBleEnabled(in.readBoolean())
|
||||
.setWorkSource(in.readTypedObject(WorkSource.CREATOR));
|
||||
for (int i = 0; i < in.readInt(); i++) {
|
||||
builder.addScanFilter(ScanFilter.createFromParcel(in));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScanRequest[] newArray(int size) {
|
||||
return new ScanRequest[size];
|
||||
}
|
||||
};
|
||||
|
||||
private final @ScanType int mScanType;
|
||||
private final @ScanMode int mScanMode;
|
||||
private final boolean mBleEnabled;
|
||||
private final @NonNull WorkSource mWorkSource;
|
||||
private final List<ScanFilter> mScanFilters;
|
||||
|
||||
private ScanRequest(@ScanType int scanType, @ScanMode int scanMode, boolean bleEnabled,
|
||||
@NonNull WorkSource workSource, List<ScanFilter> scanFilters) {
|
||||
mScanType = scanType;
|
||||
mScanMode = scanMode;
|
||||
mBleEnabled = bleEnabled;
|
||||
mWorkSource = workSource;
|
||||
mScanFilters = scanFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert scan mode to readable string.
|
||||
*
|
||||
* @param scanMode Integer that may represent a{@link ScanMode}.
|
||||
*/
|
||||
@NonNull
|
||||
public static String scanModeToString(@ScanMode int scanMode) {
|
||||
switch (scanMode) {
|
||||
case SCAN_MODE_LOW_LATENCY:
|
||||
return "SCAN_MODE_LOW_LATENCY";
|
||||
case SCAN_MODE_BALANCED:
|
||||
return "SCAN_MODE_BALANCED";
|
||||
case SCAN_MODE_LOW_POWER:
|
||||
return "SCAN_MODE_LOW_POWER";
|
||||
case SCAN_MODE_NO_POWER:
|
||||
return "SCAN_MODE_NO_POWER";
|
||||
default:
|
||||
return "SCAN_MODE_INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an integer is a defined scan type.
|
||||
*/
|
||||
public static boolean isValidScanType(@ScanType int scanType) {
|
||||
return scanType == SCAN_TYPE_FAST_PAIR
|
||||
|| scanType == SCAN_TYPE_NEARBY_SHARE
|
||||
|| scanType == SCAN_TYPE_NEARBY_PRESENCE
|
||||
|| scanType == SCAN_TYPE_EXPOSURE_NOTIFICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an integer is a defined scan mode.
|
||||
*/
|
||||
public static boolean isValidScanMode(@ScanMode int scanMode) {
|
||||
return scanMode == SCAN_MODE_LOW_LATENCY
|
||||
|| scanMode == SCAN_MODE_BALANCED
|
||||
|| scanMode == SCAN_MODE_LOW_POWER
|
||||
|| scanMode == SCAN_MODE_NO_POWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scan type for this request.
|
||||
*/
|
||||
public @ScanType int getScanType() {
|
||||
return mScanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scan mode for this request.
|
||||
*/
|
||||
public @ScanMode int getScanMode() {
|
||||
return mScanMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if Bluetooth Low Energy enabled for scanning.
|
||||
*/
|
||||
public boolean isBleEnabled() {
|
||||
return mBleEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Scan Filters for this request.
|
||||
*/
|
||||
@NonNull
|
||||
public List<ScanFilter> getScanFilters() {
|
||||
return mScanFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the work source used for power attribution of this request.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public WorkSource getWorkSource() {
|
||||
return mWorkSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* No special parcel contents.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this ScanRequest.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("Request[")
|
||||
.append("scanType=").append(mScanType);
|
||||
stringBuilder.append(", scanMode=").append(scanModeToString(mScanMode));
|
||||
stringBuilder.append(", enableBle=").append(mBleEnabled);
|
||||
stringBuilder.append(", workSource=").append(mWorkSource);
|
||||
stringBuilder.append(", scanFilters=").append(mScanFilters);
|
||||
stringBuilder.append("]");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mScanType);
|
||||
dest.writeInt(mScanMode);
|
||||
dest.writeBoolean(mBleEnabled);
|
||||
dest.writeTypedObject(mWorkSource, /* parcelableFlags= */0);
|
||||
dest.writeInt(mScanFilters.size());
|
||||
for (int i = 0; i < mScanFilters.size(); ++i) {
|
||||
mScanFilters.get(i).writeToParcel(dest, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof ScanRequest) {
|
||||
ScanRequest otherRequest = (ScanRequest) other;
|
||||
return mScanType == otherRequest.mScanType
|
||||
&& (mScanMode == otherRequest.mScanMode)
|
||||
&& (mBleEnabled == otherRequest.mBleEnabled)
|
||||
&& (Objects.equals(mWorkSource, otherRequest.mWorkSource));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mScanType, mScanMode, mBleEnabled, mWorkSource);
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({SCAN_TYPE_FAST_PAIR, SCAN_TYPE_NEARBY_SHARE, SCAN_TYPE_NEARBY_PRESENCE,
|
||||
SCAN_TYPE_EXPOSURE_NOTIFICATION})
|
||||
public @interface ScanType {
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({SCAN_MODE_LOW_LATENCY, SCAN_MODE_BALANCED,
|
||||
SCAN_MODE_LOW_POWER,
|
||||
SCAN_MODE_NO_POWER})
|
||||
public @interface ScanMode {}
|
||||
|
||||
/** A builder class for {@link ScanRequest}. */
|
||||
public static final class Builder {
|
||||
private static final int INVALID_SCAN_TYPE = -1;
|
||||
private @ScanType int mScanType;
|
||||
private @ScanMode int mScanMode;
|
||||
|
||||
private boolean mBleEnabled;
|
||||
private WorkSource mWorkSource;
|
||||
private List<ScanFilter> mScanFilters;
|
||||
|
||||
/** Creates a new Builder with the given scan type. */
|
||||
public Builder() {
|
||||
mScanType = INVALID_SCAN_TYPE;
|
||||
mBleEnabled = true;
|
||||
mWorkSource = new WorkSource();
|
||||
mScanFilters = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scan type for the request. The scan type must be one of the SCAN_TYPE_ constants
|
||||
* in {@link ScanRequest}.
|
||||
*
|
||||
* @param scanType The scan type for the request
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setScanType(@ScanType int scanType) {
|
||||
mScanType = scanType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scan mode for the request. The scan type must be one of the SCAN_MODE_ constants
|
||||
* in {@link ScanRequest}.
|
||||
*
|
||||
* @param scanMode The scan mode for the request
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setScanMode(@ScanMode int scanMode) {
|
||||
mScanMode = scanMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the ble is enabled for scanning.
|
||||
*
|
||||
* @param bleEnabled If the BluetoothLe is enabled in the device.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setBleEnabled(boolean bleEnabled) {
|
||||
mBleEnabled = bleEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the work source to use for power attribution for this scan request. Defaults to
|
||||
* empty work source, which implies the caller that sends the scan request will be used
|
||||
* for power attribution.
|
||||
*
|
||||
* <p>Permission enforcement occurs when the resulting scan request is used, not when
|
||||
* this method is invoked.
|
||||
*
|
||||
* @param workSource identifying the application(s) for which to blame for the scan.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
|
||||
@NonNull
|
||||
@SystemApi
|
||||
public Builder setWorkSource(@Nullable WorkSource workSource) {
|
||||
if (workSource == null) {
|
||||
mWorkSource = new WorkSource();
|
||||
} else {
|
||||
mWorkSource = workSource;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a scan filter to the request. Client can call this method multiple times to add
|
||||
* more than one scan filter. Scan results that match any of these scan filters will
|
||||
* be returned.
|
||||
*
|
||||
* <p>On devices with hardware support, scan filters can significantly improve the battery
|
||||
* usage of Nearby scans.
|
||||
*
|
||||
* @param scanFilter Filter for scanning the request.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addScanFilter(@NonNull ScanFilter scanFilter) {
|
||||
Objects.requireNonNull(scanFilter);
|
||||
mScanFilters.add(scanFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a scan request from this builder.
|
||||
*
|
||||
* @return a new nearby scan request.
|
||||
* @throws IllegalStateException if the scanType is not one of the SCAN_TYPE_ constants in
|
||||
* {@link ScanRequest}.
|
||||
*/
|
||||
@NonNull
|
||||
public ScanRequest build() {
|
||||
Preconditions.checkState(isValidScanType(mScanType),
|
||||
"invalid scan type : " + mScanType
|
||||
+ ", scan type must be one of ScanRequest#SCAN_TYPE_");
|
||||
Preconditions.checkState(isValidScanMode(mScanMode),
|
||||
"invalid scan mode : " + mScanMode
|
||||
+ ", scan mode must be one of ScanMode#SCAN_MODE_");
|
||||
return new ScanRequest(mScanType, mScanMode, mBleEnabled, mWorkSource, mScanFilters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
// Stings to be displayed in notification surfaced for a device.
|
||||
// The locale of all of the Strings.
|
||||
String locale;
|
||||
|
||||
// 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;
|
||||
|
||||
// The title of the UI to ask the user to confirm the pin code.
|
||||
String confirmPinTitle;
|
||||
|
||||
// The description of the UI to ask the user to confirm the pin code.
|
||||
String confirmPinDescription;
|
||||
|
||||
// The title of the UI to ask the user to confirm to sync contacts.
|
||||
String syncContactsTitle;
|
||||
|
||||
// The description of the UI to ask the user to confirm to sync contacts.
|
||||
String syncContactsDescription;
|
||||
|
||||
// The title of the UI to ask the user to confirm to sync SMS.
|
||||
String syncSmsTitle;
|
||||
|
||||
// The description of the UI to ask the user to confirm to sync SMS.
|
||||
String syncSmsDescription;
|
||||
|
||||
// The description in half sheet to ask user setup google assistant
|
||||
String assistantSetupHalfSheet;
|
||||
|
||||
// The description in notification to ask user setup google assistant
|
||||
String assistantSetupNotification;
|
||||
|
||||
// Description of the connect device action on TV, when user is not logged in.
|
||||
String fastPairTvConnectDeviceNoAccountDescription;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
int type;
|
||||
|
||||
// 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;
|
||||
|
||||
// ID used for associating several DiscoveryItems. These items may be
|
||||
// visually displayed together.
|
||||
String groupId;
|
||||
|
||||
// Whether the attachment is created in debug namespace
|
||||
int attachmentType;
|
||||
|
||||
// Package name of the App that owns this item.
|
||||
String packageName;
|
||||
|
||||
// The "feature" graphic image url used for large sized list view entries.
|
||||
String featureGraphicUrl;
|
||||
|
||||
// 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;
|
||||
|
||||
// Message written to bugreport for 3P developers.(No sensitive info)
|
||||
// null if the item is valid
|
||||
String debugMessage;
|
||||
|
||||
// Weather the item is filtered out on server.
|
||||
int debugCategory;
|
||||
|
||||
// Client timestamp when the trigger (e.g. beacon) was last lost (e.g. when
|
||||
// Messages told us the beacon's no longer nearby).
|
||||
long lostMillis;
|
||||
|
||||
// The kind of experience the user last had with this (e.g. if they dismissed
|
||||
// the notification, that's bad; but if they tapped it, that's good).
|
||||
int lastUserExperience;
|
||||
|
||||
// The most recent BLE advertisement related to this item.
|
||||
byte[] bleRecordBytes;
|
||||
|
||||
// An ID generated on the server to uniquely identify content.
|
||||
String entityId;
|
||||
|
||||
// Fast Pair antispoof key.
|
||||
byte[] authenticationPublicKeySecp256r1;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 {
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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;
|
||||
// BLE address of the device at the device add time.
|
||||
String bleAddress;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 IFastPairClient {
|
||||
|
||||
void registerHalfSheet(in IFastPairStatusCallback fastPairStatusCallback);
|
||||
|
||||
void unregisterHalfSheet(in IFastPairStatusCallback fastPairStatusCallback);
|
||||
|
||||
void connect(in FastPairDevice fastPairDevice);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
57
nearby/halfsheet/Android.bp
Normal file
57
nearby/halfsheet/Android.bp
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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",
|
||||
target_sdk_version: "current",
|
||||
updatable: true,
|
||||
certificate: ":com.android.nearby.halfsheetcertificate",
|
||||
libs: [
|
||||
"framework-bluetooth",
|
||||
"framework-connectivity-t",
|
||||
"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",
|
||||
],
|
||||
plugins: ["java_api_finder"],
|
||||
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"
|
||||
}
|
||||
31
nearby/halfsheet/AndroidManifest.xml
Normal file
31
nearby/halfsheet/AndroidManifest.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.nearby.halfsheet">
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.android.nearby.halfsheet.HalfSheetActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/HalfSheetStyle" >
|
||||
<intent-filter>
|
||||
<action android:name="android.nearby.SHOW_HALFSHEET"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
BIN
nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.pk8
Normal file
BIN
nearby/halfsheet/apk-certs/com.android.nearby.halfsheet.pk8
Normal file
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
-----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-----
|
||||
52
nearby/halfsheet/apk-certs/key.pem
Normal file
52
nearby/halfsheet/apk-certs/key.pem
Normal file
@@ -0,0 +1,52 @@
|
||||
-----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-----
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:interpolator/decelerate_quint">
|
||||
<translate android:fromYDelta="100%"
|
||||
android:toYDelta="0"
|
||||
android:duration="900"/>
|
||||
</set>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:interpolator/decelerate_quint">
|
||||
<translate android:fromYDelta="0"
|
||||
android:toYDelta="100%"
|
||||
android:duration="500"/>
|
||||
</set>
|
||||
14
nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml
Normal file
14
nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:targetApi="23"
|
||||
android:duration="@integer/half_sheet_slide_in_duration"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in">
|
||||
<translate
|
||||
android:fromYDelta="100%p"
|
||||
android:toYDelta="0%p"/>
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
||||
15
nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml
Normal file
15
nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:duration="@integer/half_sheet_fade_out_duration"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in">
|
||||
|
||||
<translate
|
||||
android:fromYDelta="0%p"
|
||||
android:toYDelta="100%p"/>
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
|
||||
</set>
|
||||
25
nearby/halfsheet/res/drawable/fast_pair_ic_info.xml
Normal file
25
nearby/halfsheet/res/drawable/fast_pair_ic_info.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="@color/fast_pair_half_sheet_subtitle_color">
|
||||
<path
|
||||
android:fillColor="@color/fast_pair_half_sheet_subtitle_color"
|
||||
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
|
||||
</vector>
|
||||
7
nearby/halfsheet/res/drawable/fastpair_outline.xml
Normal file
7
nearby/halfsheet/res/drawable/fastpair_outline.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/fast_pair_notification_image_outline"/>
|
||||
</shape>
|
||||
10
nearby/halfsheet/res/drawable/half_sheet_bg.xml
Normal file
10
nearby/halfsheet/res/drawable/half_sheet_bg.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:targetApi="23">
|
||||
<solid android:color="@color/fast_pair_half_sheet_background" />
|
||||
<corners
|
||||
android:topLeftRadius="16dp"
|
||||
android:topRightRadius="16dp"
|
||||
android:padding="8dp"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="RtlCompat"
|
||||
android:layout_width="match_parent" android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="340dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
<TextView
|
||||
android:id="@+id/header_subtitle"
|
||||
android:textColor="@color/fast_pair_half_sheet_subtitle_color"
|
||||
android:fontFamily="google-sans"
|
||||
android:textSize="14sp"
|
||||
android:maxLines="3"
|
||||
android:gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pairing_pic"
|
||||
android:layout_width="@dimen/fast_pair_half_sheet_image_size"
|
||||
android:layout_height="@dimen/fast_pair_half_sheet_image_size"
|
||||
android:paddingTop="18dp"
|
||||
android:paddingBottom="18dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/header_subtitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pin_code"
|
||||
android:textColor="@color/fast_pair_half_sheet_subtitle_color"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/fast_pair_half_sheet_image_size"
|
||||
android:paddingTop="18dp"
|
||||
android:paddingBottom="18dp"
|
||||
android:visibility="invisible"
|
||||
android:textSize="50sp"
|
||||
android:letterSpacing="0.2"
|
||||
android:fontFamily="google-sans-medium"
|
||||
android:gravity="center"
|
||||
android:importantForAccessibility="yes"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/header_subtitle" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/connect_progressbar"
|
||||
android:layout_width="@dimen/fast_pair_half_sheet_image_size"
|
||||
android:layout_height="2dp"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/fast_pair_progress_color"
|
||||
android:indeterminateTintMode="src_in"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_marginBottom="6dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pairing_pic"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/connect_progressbar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/info_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
app:srcCompat="@drawable/fast_pair_ic_info"
|
||||
android:layout_centerInParent="true"
|
||||
android:contentDescription="@null"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_toStartOf="@id/connect_btn"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/connect_btn"
|
||||
android:layout_width="@dimen/fast_pair_half_sheet_image_size"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/paring_action_connect"
|
||||
android:layout_centerInParent="true"
|
||||
style="@style/HalfSheetButton" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/settings_btn"
|
||||
android:text="@string/paring_action_settings"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="@dimen/fast_pair_half_sheet_image_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/connect_progressbar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:visibility="invisible"
|
||||
style="@style/HalfSheetButton" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/cancel_btn"
|
||||
android:text="@string/paring_action_done"
|
||||
android:visibility="invisible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:gravity="start|center_vertical"
|
||||
android:layout_marginTop="6dp"
|
||||
style="@style/HalfSheetButtonBorderless"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/setup_btn"
|
||||
android:text="@string/paring_action_launch"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@color/fast_pair_half_sheet_button_color"
|
||||
android:visibility="invisible"
|
||||
android:layout_height="@dimen/fast_pair_half_sheet_bottom_button_height"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/HalfSheetButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
66
nearby/halfsheet/res/layout/fast_pair_half_sheet.xml
Normal file
66
nearby/halfsheet/res/layout/fast_pair_half_sheet.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="RtlCompat, UselessParent, MergeRootFrame"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card"
|
||||
android:orientation="vertical"
|
||||
android:transitionName="card"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_gravity= "center|bottom"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:background="@drawable/half_sheet_bg"
|
||||
android:accessibilityLiveRegion="polite"
|
||||
android:gravity="bottom">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/toolbar_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_marginTop="9dp"
|
||||
android:layout_marginBottom="9dp"
|
||||
android:id="@+id/toolbar_image"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:contentDescription="@null"
|
||||
android:layout_toStartOf="@id/toolbar_title"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="18dp"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:id="@+id/toolbar_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="google-sans-medium"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/fast_pair_half_sheet_text_color"
|
||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
|
||||
</RelativeLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false"
|
||||
android:background="@color/fast_pair_notification_background"
|
||||
tools:ignore="ContentDescription,UnusedAttribute,RtlCompat,Overdraw">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginTop="@dimen/fast_pair_notification_padding"
|
||||
android:layout_marginStart="@dimen/fast_pair_notification_padding"
|
||||
android:layout_marginEnd="@dimen/fast_pair_notification_padding">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textSize="@dimen/fast_pair_notification_text_size"
|
||||
android:textColor="@color/fast_pair_primary_text"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:lines="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/fast_pair_notification_text_size_small"
|
||||
android:textColor="@color/fast_pair_primary_text"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:lines="1"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/fast_pair_notification_text_size"
|
||||
android:textColor="@color/fast_pair_primary_text"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
android:breakStrategy="simple" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@android:id/secondaryProgress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@android:id/progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:indeterminateTint="@color/discovery_activity_accent"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@android:id/icon1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,7 @@
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="@dimen/fast_pair_notification_large_image_size"
|
||||
android:layout_height="@dimen/fast_pair_notification_large_image_size"
|
||||
android:scaleType="fitStart"
|
||||
tools:ignore="ContentDescription"/>
|
||||
@@ -0,0 +1,11 @@
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="@dimen/fast_pair_notification_small_image_size"
|
||||
android:layout_height="@dimen/fast_pair_notification_small_image_size"
|
||||
android:layout_marginTop="@dimen/fast_pair_notification_padding"
|
||||
android:layout_marginBottom="@dimen/fast_pair_notification_padding"
|
||||
android:layout_marginStart="@dimen/fast_pair_notification_padding"
|
||||
android:layout_marginEnd="@dimen/fast_pair_notification_padding"
|
||||
android:scaleType="fitStart"
|
||||
tools:ignore="ContentDescription,RtlCompat"/>
|
||||
28
nearby/halfsheet/res/values/colors.xml
Normal file
28
nearby/halfsheet/res/values/colors.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Use original background color -->
|
||||
<color name="fast_pair_notification_background">#00000000</color>
|
||||
<!-- Ignores NewApi as below system colors are available since API 31, and HalfSheet is always
|
||||
running on T+ even though it has min_sdk 30 to match its containing APEX -->
|
||||
<color name="fast_pair_half_sheet_button_color" tools:ignore="NewApi">@android:color/system_accent1_100</color>
|
||||
<color name="fast_pair_half_sheet_button_text" tools:ignore="NewApi">@android:color/system_neutral1_900</color>
|
||||
<color name="fast_pair_half_sheet_button_accent_text" tools:ignore="NewApi">@android:color/system_neutral1_900</color>
|
||||
<color name="fast_pair_progress_color" tools:ignore="NewApi">@android:color/system_accent1_600</color>
|
||||
<color name="fast_pair_half_sheet_subtitle_color" tools:ignore="NewApi">@android:color/system_neutral2_700</color>
|
||||
<color name="fast_pair_half_sheet_text_color" tools:ignore="NewApi">@android:color/system_neutral1_900</color>
|
||||
|
||||
<!-- Nearby Discoverer -->
|
||||
<color name="discovery_activity_accent">#4285F4</color>
|
||||
|
||||
<!-- Fast Pair -->
|
||||
<color name="fast_pair_primary_text">#DE000000</color>
|
||||
<color name="fast_pair_notification_image_outline">#24000000</color>
|
||||
<color name="fast_pair_battery_level_low">#D93025</color>
|
||||
<color name="fast_pair_battery_level_normal">#80868B</color>
|
||||
<color name="fast_pair_half_sheet_background">#FFFFFF</color>
|
||||
<color name="fast_pair_half_sheet_color_accent">#1A73E8</color>
|
||||
<color name="fast_pair_fail_progress_color">#F44336</color>
|
||||
<color name="fast_pair_progress_back_ground">#24000000</color>
|
||||
</resources>
|
||||
42
nearby/halfsheet/res/values/dimens.xml
Normal file
42
nearby/halfsheet/res/values/dimens.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Fast Pair notification values -->
|
||||
<dimen name="fast_pair_halfsheet_mid_image_size">160dp</dimen>
|
||||
<dimen name="fast_pair_notification_text_size">14sp</dimen>
|
||||
<dimen name="fast_pair_notification_text_size_small">11sp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_empty_view_height">4dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_margin_top">8dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_margin_bottom">8dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_content_height">40dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_content_height_v2">64dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_image_size">32dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_image_padding">3dp</dimen>
|
||||
<dimen name="fast_pair_half_sheet_min_height">350dp</dimen>
|
||||
<dimen name="fast_pair_half_sheet_image_size">215dp</dimen>
|
||||
<dimen name="fast_pair_half_sheet_land_image_size">136dp</dimen>
|
||||
<dimen name="fast_pair_connect_button_height">36dp</dimen>
|
||||
<dimen name="accessibility_required_min_touch_target_size">48dp</dimen>
|
||||
<dimen name="fast_pair_half_sheet_battery_case_image_size">152dp</dimen>
|
||||
<dimen name="fast_pair_half_sheet_battery_bud_image_size">100dp</dimen>
|
||||
<integer name="half_sheet_battery_case_width_dp">156</integer>
|
||||
<integer name="half_sheet_battery_case_height_dp">182</integer>
|
||||
|
||||
<!-- Maximum height for SliceView, override on slices/view/src/main/res/values/dimens.xml -->
|
||||
<dimen name="abc_slice_large_height">360dp</dimen>
|
||||
|
||||
<dimen name="action_dialog_content_margin_left">16dp</dimen>
|
||||
<dimen name="action_dialog_content_margin_top">70dp</dimen>
|
||||
<dimen name="action_button_focused_elevation">4dp</dimen>
|
||||
<!-- Subsequent Notification -->
|
||||
<dimen name="fast_pair_notification_padding">4dp</dimen>
|
||||
<dimen name="fast_pair_notification_large_image_size">32dp</dimen>
|
||||
<dimen name="fast_pair_notification_small_image_size">32dp</dimen>
|
||||
<!-- Battery Notification -->
|
||||
<dimen name="fast_pair_battery_notification_main_view_padding">0dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_title_image_margin_start">0dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_title_text_margin_start">0dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_title_text_margin_start_v2">0dp</dimen>
|
||||
<dimen name="fast_pair_battery_notification_image_margin_start">0dp</dimen>
|
||||
|
||||
<dimen name="fast_pair_half_sheet_bottom_button_height">48dp</dimen>
|
||||
</resources>
|
||||
5
nearby/halfsheet/res/values/ints.xml
Normal file
5
nearby/halfsheet/res/values/ints.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="half_sheet_slide_in_duration">250</integer>
|
||||
<integer name="half_sheet_fade_out_duration">250</integer>
|
||||
</resources>
|
||||
24
nearby/halfsheet/res/values/overlayable.xml
Normal file
24
nearby/halfsheet/res/values/overlayable.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<overlayable name="NearbyHalfSheetResourcesConfig">
|
||||
<policy type="product|system|vendor">
|
||||
<item type="color" name="fast_pair_half_sheet_background"/>
|
||||
<item type="color" name="fast_pair_half_sheet_button_color"/>
|
||||
</policy>
|
||||
</overlayable>
|
||||
</resources>
|
||||
72
nearby/halfsheet/res/values/strings.xml
Normal file
72
nearby/halfsheet/res/values/strings.xml
Normal file
@@ -0,0 +1,72 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!--
|
||||
============================================================
|
||||
PAIRING FRAGMENT
|
||||
============================================================
|
||||
-->
|
||||
|
||||
<!--
|
||||
A button shown to remind user setup is in progress. [CHAR LIMIT=30]
|
||||
-->
|
||||
<string name="fast_pair_setup_in_progress">Starting Setup…</string>
|
||||
<!--
|
||||
Title text shown to remind user to setup a device through companion app. [CHAR LIMIT=40]
|
||||
-->
|
||||
<string name="fast_pair_title_setup">Set up device</string>
|
||||
<!--
|
||||
Title after we successfully pair with the audio device
|
||||
[CHAR LIMIT=30]
|
||||
-->
|
||||
<string name="fast_pair_device_ready">Device connected</string>
|
||||
<!-- Title text shown when peripheral device fail to connect to phone. [CHAR_LIMIT=30] -->
|
||||
<string name="fast_pair_title_fail">Couldn\'t connect</string>
|
||||
|
||||
<!--
|
||||
============================================================
|
||||
MISCELLANEOUS
|
||||
============================================================
|
||||
-->
|
||||
|
||||
<!--
|
||||
A button shown after paring process to dismiss the current activity.
|
||||
[CHAR LIMIT=30]
|
||||
-->
|
||||
<string name="paring_action_done">Done</string>
|
||||
<!--
|
||||
A button shown for retroactive paring.
|
||||
[CHAR LIMIT=30]
|
||||
-->
|
||||
<string name="paring_action_save">Save</string>
|
||||
<!--
|
||||
A button to start connecting process.
|
||||
[CHAR LIMIT=30]
|
||||
-->
|
||||
<string name="paring_action_connect">Connect</string>
|
||||
<!--
|
||||
A button to launch a companion app.
|
||||
[CHAR LIMIT=30]
|
||||
-->
|
||||
<string name="paring_action_launch">Set up</string>
|
||||
<!--
|
||||
A button to launch a bluetooth Settings page.
|
||||
[CHAR LIMIT=20]
|
||||
-->
|
||||
<string name="paring_action_settings">Settings</string>
|
||||
</resources>
|
||||
38
nearby/halfsheet/res/values/styles.xml
Normal file
38
nearby/halfsheet/res/values/styles.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<style name="HalfSheetStyle" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowEnterAnimation">@anim/fast_pair_half_sheet_slide_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/fast_pair_half_sheet_slide_out</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:backgroundDimEnabled">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:fitsSystemWindows">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
</style>
|
||||
|
||||
<style name="HalfSheetButton" parent="@style/Widget.Material3.Button.TonalButton">
|
||||
<item name="android:textColor">@color/fast_pair_half_sheet_button_accent_text</item>
|
||||
<item name="android:backgroundTint">@color/fast_pair_half_sheet_button_color</item>
|
||||
<item name="android:textSize">@dimen/fast_pair_notification_text_size</item>
|
||||
<item name="android:fontFamily">google-sans-medium</item>
|
||||
<item name="android:textAlignment">center</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
</style>
|
||||
|
||||
<style name="HalfSheetButtonBorderless" parent="@style/Widget.Material3.Button.OutlinedButton">
|
||||
<item name="android:textColor">@color/fast_pair_half_sheet_button_text</item>
|
||||
<item name="android:strokeColor">@color/fast_pair_half_sheet_button_color</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:textSize">@dimen/fast_pair_notification_text_size</item>
|
||||
<item name="android:fontFamily">google-sans-medium</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:textAlignment">center</item>
|
||||
<item name="android:minHeight">@dimen/accessibility_required_min_touch_target_size</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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 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 = "HalfSheetActivity";
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
super.setTitle(title);
|
||||
TextView toolbarTitle = findViewById(R.id.toolbar_title);
|
||||
toolbarTitle.setText(title);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
* 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.FastPairClient;
|
||||
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.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.
|
||||
*
|
||||
* <p>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 FastPairClient mFastPairClient;
|
||||
|
||||
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) {
|
||||
mFastPairClient = new FastPairClient(getContext(), mBundle.getBinder(EXTRA_BINDER));
|
||||
mFastPairClient.registerHalfSheet(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();
|
||||
mFastPairClient.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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* <p>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();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
private BroadcastUtils() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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}.
|
||||
*
|
||||
* <p>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())
|
||||
.setType(Cache.NearbyType.NEARBY_DEVICE)
|
||||
.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() {}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
98
nearby/service/Android.bp
Normal file
98
nearby/service/Android.bp
Normal file
@@ -0,0 +1,98 @@
|
||||
// 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"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "nearby-service-srcs",
|
||||
srcs: [
|
||||
"java/**/*.java",
|
||||
":statslog-nearby-java-gen",
|
||||
],
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
|
||||
// Main lib for nearby services.
|
||||
java_library {
|
||||
name: "service-nearby-pre-jarjar",
|
||||
srcs: [":nearby-service-srcs"],
|
||||
|
||||
defaults: [
|
||||
"framework-system-server-module-defaults"
|
||||
],
|
||||
libs: [
|
||||
"framework-bluetooth.stubs.module_lib", // TODO(b/215722418): Change to framework-bluetooth once fixed
|
||||
"error_prone_annotations",
|
||||
"framework-connectivity-t.impl",
|
||||
"framework-statsd.stubs.module_lib",
|
||||
],
|
||||
static_libs: [
|
||||
"androidx.annotation_annotation",
|
||||
"androidx.core_core",
|
||||
"androidx.localbroadcastmanager_localbroadcastmanager",
|
||||
"guava",
|
||||
"libprotobuf-java-lite",
|
||||
"fast-pair-lite-protos",
|
||||
"modules-utils-build",
|
||||
"modules-utils-handlerexecutor",
|
||||
"modules-utils-preconditions",
|
||||
"modules-utils-backgroundthread",
|
||||
"presence-lite-protos",
|
||||
],
|
||||
sdk_version: "system_server_current",
|
||||
// This is included in service-connectivity which is 30+
|
||||
// TODO: allow APEXes to have service jars with higher min_sdk than the APEX
|
||||
// (service-connectivity is only used on 31+) and use 31 here
|
||||
min_sdk_version: "30",
|
||||
|
||||
installable: true,
|
||||
dex_preopt: {
|
||||
enabled: false,
|
||||
app_image: false,
|
||||
},
|
||||
visibility: [
|
||||
"//packages/modules/Nearby/apex",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "statslog-nearby-java-gen",
|
||||
tools: ["stats-log-api-gen"],
|
||||
cmd: "$(location stats-log-api-gen) --java $(out) --module nearby " +
|
||||
" --javaPackage com.android.server.nearby.proto --javaClass NearbyStatsLog" +
|
||||
" --minApiLevel 33",
|
||||
out: ["com/android/server/nearby/proto/NearbyStatsLog.java"],
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* A utility class for encapsulating Nearby feature flag configurations.
|
||||
*/
|
||||
public class NearbyConfiguration {
|
||||
|
||||
/**
|
||||
* Flag use to enable presence legacy broadcast.
|
||||
*/
|
||||
public static final String NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY =
|
||||
"nearby_enable_presence_broadcast_legacy";
|
||||
|
||||
private boolean mEnablePresenceBroadcastLegacy;
|
||||
|
||||
public NearbyConfiguration() {
|
||||
mEnablePresenceBroadcastLegacy = getDeviceConfigBoolean(
|
||||
NEARBY_ENABLE_PRESENCE_BROADCAST_LEGACY, false /* defaultValue */);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether broadcasting legacy presence spec is enabled.
|
||||
*/
|
||||
public boolean isPresenceBroadcastLegacyEnabled() {
|
||||
return mEnablePresenceBroadcastLegacy;
|
||||
}
|
||||
|
||||
private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
|
||||
final String value = getDeviceConfigProperty(name);
|
||||
return value != null ? Boolean.parseBoolean(value) : defaultValue;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected String getDeviceConfigProperty(String name) {
|
||||
return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TETHERING, name);
|
||||
}
|
||||
}
|
||||
198
nearby/service/java/com/android/server/nearby/NearbyService.java
Normal file
198
nearby/service/java/com/android/server/nearby/NearbyService.java
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
|
||||
import static com.android.server.SystemService.PHASE_THIRD_PARTY_APPS_CAN_START;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.location.ContextHubManager;
|
||||
import android.nearby.BroadcastRequestParcelable;
|
||||
import android.nearby.IBroadcastListener;
|
||||
import android.nearby.INearbyManager;
|
||||
import android.nearby.IScanListener;
|
||||
import android.nearby.NearbyManager;
|
||||
import android.nearby.ScanRequest;
|
||||
import android.util.Log;
|
||||
|
||||
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.presence.ChreCommunication;
|
||||
import com.android.server.nearby.presence.PresenceManager;
|
||||
import com.android.server.nearby.provider.BroadcastProviderManager;
|
||||
import com.android.server.nearby.provider.DiscoveryProviderManager;
|
||||
import com.android.server.nearby.provider.FastPairDataProvider;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import service.proto.Blefilter;
|
||||
|
||||
/** Service implementing nearby functionality. */
|
||||
public class NearbyService extends INearbyManager.Stub {
|
||||
public static final String TAG = "NearbyService";
|
||||
|
||||
private final Context mContext;
|
||||
private final SystemInjector mSystemInjector;
|
||||
private final FastPairManager mFastPairManager;
|
||||
private final PresenceManager mPresenceManager;
|
||||
private final BroadcastReceiver mBluetoothReceiver =
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int state =
|
||||
intent.getIntExtra(
|
||||
BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
||||
if (state == BluetoothAdapter.STATE_ON) {
|
||||
if (mSystemInjector != null) {
|
||||
// Have to do this logic in listener. Even during PHASE_BOOT_COMPLETED
|
||||
// phase, BluetoothAdapter is not null, the BleScanner is null.
|
||||
Log.v(TAG, "Initiating BluetoothAdapter when Bluetooth is turned on.");
|
||||
mSystemInjector.initializeBluetoothAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
private DiscoveryProviderManager mProviderManager;
|
||||
private BroadcastProviderManager mBroadcastProviderManager;
|
||||
|
||||
public NearbyService(Context context) {
|
||||
mContext = context;
|
||||
mSystemInjector = new SystemInjector(context);
|
||||
mProviderManager = new DiscoveryProviderManager(context, mSystemInjector);
|
||||
mBroadcastProviderManager = new BroadcastProviderManager(context, mSystemInjector);
|
||||
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
|
||||
mFastPairManager = new FastPairManager(lcw);
|
||||
mPresenceManager =
|
||||
new PresenceManager(
|
||||
mContext,
|
||||
(results) -> {
|
||||
// TODO(b/221082271): hooked with API codes.
|
||||
for (Blefilter.BleFilterResult result : results.getResultList()) {
|
||||
Log.i(
|
||||
TAG,
|
||||
String.format(
|
||||
"received filter result with id: %d",
|
||||
result.getId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@NearbyManager.ScanStatus
|
||||
public int registerScanListener(ScanRequest scanRequest, IScanListener listener) {
|
||||
if (mProviderManager.registerScanListener(scanRequest, listener)) {
|
||||
return NearbyManager.ScanStatus.SUCCESS;
|
||||
}
|
||||
return NearbyManager.ScanStatus.ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterScanListener(IScanListener listener) {
|
||||
mProviderManager.unregisterScanListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startBroadcast(BroadcastRequestParcelable broadcastRequestParcelable,
|
||||
IBroadcastListener listener) {
|
||||
mBroadcastProviderManager.startBroadcast(
|
||||
broadcastRequestParcelable.getBroadcastRequest(),
|
||||
listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopBroadcast(IBroadcastListener listener) {
|
||||
mBroadcastProviderManager.stopBroadcast(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the service initializer.
|
||||
*
|
||||
* <p>{@see com.android.server.SystemService#onBootPhase}.
|
||||
*/
|
||||
public void onBootPhase(int phase) {
|
||||
switch (phase) {
|
||||
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:
|
||||
// The nearby service must be functioning after this boot phase.
|
||||
mSystemInjector.initializeBluetoothAdapter();
|
||||
mContext.registerReceiver(
|
||||
mBluetoothReceiver,
|
||||
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
mFastPairManager.initiate();
|
||||
mSystemInjector.initializeContextHubManagerAdapter();
|
||||
mPresenceManager.initiate(
|
||||
new ChreCommunication(
|
||||
mSystemInjector, Executors.newSingleThreadExecutor()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SystemInjector implements Injector {
|
||||
private final Context mContext;
|
||||
@Nullable private BluetoothAdapter mBluetoothAdapter;
|
||||
@Nullable private ContextHubManagerAdapter mContextHubManagerAdapter;
|
||||
|
||||
SystemInjector(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BluetoothAdapter getBluetoothAdapter() {
|
||||
return mBluetoothAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextHubManagerAdapter getContextHubManagerAdapter() {
|
||||
return mContextHubManagerAdapter;
|
||||
}
|
||||
|
||||
synchronized void initializeBluetoothAdapter() {
|
||||
if (mBluetoothAdapter != null) {
|
||||
return;
|
||||
}
|
||||
BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
|
||||
if (manager == null) {
|
||||
return;
|
||||
}
|
||||
mBluetoothAdapter = manager.getAdapter();
|
||||
}
|
||||
|
||||
synchronized void initializeContextHubManagerAdapter() {
|
||||
if (mContextHubManagerAdapter != null) {
|
||||
return;
|
||||
}
|
||||
ContextHubManager manager = mContext.getSystemService(ContextHubManager.class);
|
||||
if (manager == null) {
|
||||
return;
|
||||
}
|
||||
mContextHubManagerAdapter = new ContextHubManagerAdapter(manager);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,746 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* <p>Current filtering on the following fields are supported:
|
||||
* <li>Service UUIDs which identify the bluetooth gatt services running on the device.
|
||||
* <li>Name of remote Bluetooth LE device.
|
||||
* <li>Mac address of the remote device.
|
||||
* <li>Service data which is the data associated with a service.
|
||||
* <li>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<BleFilter> CREATOR = new Creator<BleFilter>() {
|
||||
@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<ParcelUuid> 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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<ParcelUuid> mServiceUuids;
|
||||
|
||||
// null when the scan record is not valid.
|
||||
@Nullable
|
||||
private final SparseArray<byte[]> mManufacturerSpecificData;
|
||||
|
||||
// null when the scan record is not valid.
|
||||
@Nullable
|
||||
private final Map<ParcelUuid, byte[]> 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<ParcelUuid> serviceUuids,
|
||||
@Nullable SparseArray<byte[]> manufacturerData,
|
||||
@Nullable Map<ParcelUuid, byte[]> 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<ParcelUuid> getServiceUuids() {
|
||||
return mServiceUuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
|
||||
* data.
|
||||
*/
|
||||
@Nullable
|
||||
public SparseArray<byte[]> 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<ParcelUuid, byte[]> 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:
|
||||
*
|
||||
* <p><code>pathloss = txPowerLevel - rssi</code>
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
* <p>The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
|
||||
*
|
||||
* <p>All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
|
||||
* 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<ParcelUuid> serviceUuids = new ArrayList<>();
|
||||
String localName = null;
|
||||
int txPowerLevel = Integer.MIN_VALUE;
|
||||
|
||||
SparseArray<byte[]> manufacturerData = new SparseArray<>();
|
||||
Map<ParcelUuid, byte[]> 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<ParcelUuid> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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<BleSighting> CREATOR = new Creator<BleSighting>() {
|
||||
@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.
|
||||
* <p>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());
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user