Merge changes I57b37ea7,I00772535 into main
* changes: Introduce Powered Off Finding API Add Powered Off Finding operations to NearbyService
This commit is contained in:
commit
c412fd3987
12 changed files with 470 additions and 3 deletions
|
|
@ -118,6 +118,7 @@ java_library {
|
||||||
"framework-bluetooth",
|
"framework-bluetooth",
|
||||||
"framework-wifi",
|
"framework-wifi",
|
||||||
"framework-connectivity-pre-jarjar",
|
"framework-connectivity-pre-jarjar",
|
||||||
|
"framework-location.stubs.module_lib",
|
||||||
],
|
],
|
||||||
visibility: ["//packages/modules/Connectivity:__subpackages__"],
|
visibility: ["//packages/modules/Connectivity:__subpackages__"],
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +141,7 @@ java_sdk_library {
|
||||||
"sdk_module-lib_current_framework-connectivity",
|
"sdk_module-lib_current_framework-connectivity",
|
||||||
],
|
],
|
||||||
libs: [
|
libs: [
|
||||||
|
"framework-location.stubs.module_lib",
|
||||||
"sdk_module-lib_current_framework-connectivity",
|
"sdk_module-lib_current_framework-connectivity",
|
||||||
],
|
],
|
||||||
permitted_packages: [
|
permitted_packages: [
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,17 @@ package android.nearby {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NearbyManager {
|
public class NearbyManager {
|
||||||
|
method @FlaggedApi("com.android.nearby.flags.powered_off_finding") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getPoweredOffFindingMode();
|
||||||
method public void queryOffloadCapability(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.nearby.OffloadCapability>);
|
method public void queryOffloadCapability(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.nearby.OffloadCapability>);
|
||||||
|
method @FlaggedApi("com.android.nearby.flags.powered_off_finding") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setPoweredOffFindingEphemeralIds(@NonNull java.util.List<byte[]>);
|
||||||
|
method @FlaggedApi("com.android.nearby.flags.powered_off_finding") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setPoweredOffFindingMode(int);
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void startBroadcast(@NonNull android.nearby.BroadcastRequest, @NonNull java.util.concurrent.Executor, @NonNull android.nearby.BroadcastCallback);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void startBroadcast(@NonNull android.nearby.BroadcastRequest, @NonNull java.util.concurrent.Executor, @NonNull android.nearby.BroadcastCallback);
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int startScan(@NonNull android.nearby.ScanRequest, @NonNull java.util.concurrent.Executor, @NonNull android.nearby.ScanCallback);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int startScan(@NonNull android.nearby.ScanRequest, @NonNull java.util.concurrent.Executor, @NonNull android.nearby.ScanCallback);
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void stopBroadcast(@NonNull android.nearby.BroadcastCallback);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void stopBroadcast(@NonNull android.nearby.BroadcastCallback);
|
||||||
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void stopScan(@NonNull android.nearby.ScanCallback);
|
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void stopScan(@NonNull android.nearby.ScanCallback);
|
||||||
|
field @FlaggedApi("com.android.nearby.flags.powered_off_finding") public static final int POWERED_OFF_FINDING_MODE_DISABLED = 1; // 0x1
|
||||||
|
field @FlaggedApi("com.android.nearby.flags.powered_off_finding") public static final int POWERED_OFF_FINDING_MODE_ENABLED = 2; // 0x2
|
||||||
|
field @FlaggedApi("com.android.nearby.flags.powered_off_finding") public static final int POWERED_OFF_FINDING_MODE_UNSUPPORTED = 0; // 0x0
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class OffloadCapability implements android.os.Parcelable {
|
public final class OffloadCapability implements android.os.Parcelable {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ java_library {
|
||||||
"androidx.annotation_annotation",
|
"androidx.annotation_annotation",
|
||||||
"framework-annotations-lib",
|
"framework-annotations-lib",
|
||||||
"framework-bluetooth",
|
"framework-bluetooth",
|
||||||
|
"framework-location.stubs.module_lib",
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"modules-utils-preconditions",
|
"modules-utils-preconditions",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import android.nearby.IBroadcastListener;
|
||||||
import android.nearby.IScanListener;
|
import android.nearby.IScanListener;
|
||||||
import android.nearby.BroadcastRequestParcelable;
|
import android.nearby.BroadcastRequestParcelable;
|
||||||
import android.nearby.ScanRequest;
|
import android.nearby.ScanRequest;
|
||||||
|
import android.nearby.PoweredOffFindingEphemeralId;
|
||||||
import android.nearby.aidl.IOffloadCallback;
|
import android.nearby.aidl.IOffloadCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,4 +41,10 @@ interface INearbyManager {
|
||||||
void stopBroadcast(in IBroadcastListener callback, String packageName, @nullable String attributionTag);
|
void stopBroadcast(in IBroadcastListener callback, String packageName, @nullable String attributionTag);
|
||||||
|
|
||||||
void queryOffloadCapability(in IOffloadCallback callback) ;
|
void queryOffloadCapability(in IOffloadCallback callback) ;
|
||||||
|
|
||||||
|
void setPoweredOffFindingEphemeralIds(in List<PoweredOffFindingEphemeralId> eids);
|
||||||
|
|
||||||
|
void setPoweredOffModeEnabled(boolean enabled);
|
||||||
|
|
||||||
|
boolean getPoweredOffModeEnabled();
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ package android.nearby;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.CallbackExecutor;
|
import android.annotation.CallbackExecutor;
|
||||||
|
import android.annotation.FlaggedApi;
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
|
@ -25,9 +26,12 @@ import android.annotation.RequiresPermission;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
import android.annotation.SystemService;
|
import android.annotation.SystemService;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.location.LocationManager;
|
||||||
import android.nearby.aidl.IOffloadCallback;
|
import android.nearby.aidl.IOffloadCallback;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
@ -37,6 +41,7 @@ import com.android.internal.util.Preconditions;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
@ -75,8 +80,51 @@ public class NearbyManager {
|
||||||
int ERROR = 2;
|
int ERROR = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value of {@link #getPoweredOffFindingMode()} when this powered off finding is not
|
||||||
|
* supported the device.
|
||||||
|
*/
|
||||||
|
@FlaggedApi("com.android.nearby.flags.powered_off_finding")
|
||||||
|
public static final int POWERED_OFF_FINDING_MODE_UNSUPPORTED = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value of {@link #getPoweredOffFindingMode()} and argument of {@link
|
||||||
|
* #setPoweredOffFindingMode(int)} when powered off finding is supported but disabled. The
|
||||||
|
* device will not start to advertise when powered off.
|
||||||
|
*/
|
||||||
|
@FlaggedApi("com.android.nearby.flags.powered_off_finding")
|
||||||
|
public static final int POWERED_OFF_FINDING_MODE_DISABLED = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value of {@link #getPoweredOffFindingMode()} and argument of {@link
|
||||||
|
* #setPoweredOffFindingMode(int)} when powered off finding is enabled. The device will start to
|
||||||
|
* advertise when powered off.
|
||||||
|
*/
|
||||||
|
@FlaggedApi("com.android.nearby.flags.powered_off_finding")
|
||||||
|
public static final int POWERED_OFF_FINDING_MODE_ENABLED = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Powered off finding modes.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@IntDef(
|
||||||
|
prefix = {"POWERED_OFF_FINDING_MODE"},
|
||||||
|
value = {
|
||||||
|
POWERED_OFF_FINDING_MODE_UNSUPPORTED,
|
||||||
|
POWERED_OFF_FINDING_MODE_DISABLED,
|
||||||
|
POWERED_OFF_FINDING_MODE_ENABLED,
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface PoweredOffFindingMode {}
|
||||||
|
|
||||||
private static final String TAG = "NearbyManager";
|
private static final String TAG = "NearbyManager";
|
||||||
|
|
||||||
|
private static final int POWERED_OFF_FINDING_EID_LENGTH = 20;
|
||||||
|
|
||||||
|
private static final String POWER_OFF_FINDING_SUPPORTED_PROPERTY =
|
||||||
|
"ro.bluetooth.finder.supported";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO(b/286137024): Remove this when CTS R5 is rolled out.
|
* TODO(b/286137024): Remove this when CTS R5 is rolled out.
|
||||||
* Whether allows Fast Pair to scan.
|
* Whether allows Fast Pair to scan.
|
||||||
|
|
@ -456,4 +504,124 @@ public class NearbyManager {
|
||||||
"successfully %s Fast Pair scan", enable ? "enables" : "disables"));
|
"successfully %s Fast Pair scan", enable ? "enables" : "disables"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the precomputed EIDs for advertising when the phone is powered off. The Bluetooth
|
||||||
|
* controller will store these EIDs in its memory, and will start advertising them in Find My
|
||||||
|
* Device network EID frames when powered off, only if the powered off finding mode was
|
||||||
|
* previously enabled by calling {@link #setPoweredOffFindingMode(int)}.
|
||||||
|
*
|
||||||
|
* <p>The EIDs are cryptographic ephemeral identifiers that change periodically, based on the
|
||||||
|
* Android clock at the time of the shutdown. They are used as the public part of asymmetric key
|
||||||
|
* pairs. Members of the Find My Device network can use them to encrypt the location of where
|
||||||
|
* they sight the advertising device. Only someone in possession of the private key (the device
|
||||||
|
* owner or someone that the device owner shared the key with) can decrypt this encrypted
|
||||||
|
* location.
|
||||||
|
*
|
||||||
|
* <p>Android will typically call this method during the shutdown process. Even after the
|
||||||
|
* method was called, it is still possible to call {#link setPoweredOffFindingMode() to disable
|
||||||
|
* the advertisement, for example to temporarily disable it for a single shutdown.
|
||||||
|
*
|
||||||
|
* <p>If called more than once, the EIDs of the most recent call overrides the EIDs from any
|
||||||
|
* previous call.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the length of one of the EIDs is not 20 bytes
|
||||||
|
*/
|
||||||
|
@FlaggedApi("com.android.nearby.flags.powered_off_finding")
|
||||||
|
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
|
||||||
|
public void setPoweredOffFindingEphemeralIds(@NonNull List<byte[]> eids) {
|
||||||
|
Objects.requireNonNull(eids);
|
||||||
|
if (!isPoweredOffFindingSupported()) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Powered off finding is not supported on this device");
|
||||||
|
}
|
||||||
|
List<PoweredOffFindingEphemeralId> ephemeralIdList = eids.stream().map(
|
||||||
|
eid -> {
|
||||||
|
Preconditions.checkArgument(eid.length == POWERED_OFF_FINDING_EID_LENGTH);
|
||||||
|
PoweredOffFindingEphemeralId ephemeralId = new PoweredOffFindingEphemeralId();
|
||||||
|
ephemeralId.bytes = eid;
|
||||||
|
return ephemeralId;
|
||||||
|
}).toList();
|
||||||
|
try {
|
||||||
|
mService.setPoweredOffFindingEphemeralIds(ephemeralIdList);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns the powered off finding on or off. Power off finding will operate only if this method
|
||||||
|
* was called at least once since boot, and the value of the argument {@code
|
||||||
|
* poweredOffFindinMode} was {@link #POWERED_OFF_FINDING_MODE_ENABLED} the last time the method
|
||||||
|
* was called.
|
||||||
|
*
|
||||||
|
* <p>When an Android device with the powered off finding feature is turned off (either as part
|
||||||
|
* of a normal shutdown or due to dead battery), its Bluetooth chip starts to advertise Find My
|
||||||
|
* Device network EID frames with the EID payload that were provided by the last call to {@link
|
||||||
|
* #setPoweredOffFindingEphemeralIds(List)}. These EIDs can be sighted by other Android devices
|
||||||
|
* in BLE range that are part of the Find My Device network. The Android sighters use the EID to
|
||||||
|
* encrypt the location of the Android device and upload it to the server, in a way that only
|
||||||
|
* the owner of the advertising device, or people that the owner shared their encryption key
|
||||||
|
* with, can decrypt the location.
|
||||||
|
*
|
||||||
|
* @param poweredOffFindingMode {@link #POWERED_OFF_FINDING_MODE_ENABLED} or {@link
|
||||||
|
* #POWERED_OFF_FINDING_MODE_DISABLED}
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if called with {@link #POWERED_OFF_FINDING_MODE_ENABLED} when
|
||||||
|
* Bluetooth or location services are disabled
|
||||||
|
*/
|
||||||
|
@FlaggedApi("com.android.nearby.flags.powered_off_finding")
|
||||||
|
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
|
||||||
|
public void setPoweredOffFindingMode(@PoweredOffFindingMode int poweredOffFindingMode) {
|
||||||
|
Preconditions.checkArgument(
|
||||||
|
poweredOffFindingMode == POWERED_OFF_FINDING_MODE_ENABLED
|
||||||
|
|| poweredOffFindingMode == POWERED_OFF_FINDING_MODE_DISABLED,
|
||||||
|
"invalid poweredOffFindingMode");
|
||||||
|
if (!isPoweredOffFindingSupported()) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Powered off finding is not supported on this device");
|
||||||
|
}
|
||||||
|
if (poweredOffFindingMode == POWERED_OFF_FINDING_MODE_ENABLED) {
|
||||||
|
Preconditions.checkState(areLocationAndBluetoothEnabled(),
|
||||||
|
"Location services and Bluetooth must be on");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mService.setPoweredOffModeEnabled(
|
||||||
|
poweredOffFindingMode == POWERED_OFF_FINDING_MODE_ENABLED);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the state of the powered off finding feature.
|
||||||
|
*
|
||||||
|
* <p>{@link #POWERED_OFF_FINDING_MODE_UNSUPPORTED} if the feature is not supported by the
|
||||||
|
* device, {@link #POWERED_OFF_FINDING_MODE_DISABLED} if this was the last value set by {@link
|
||||||
|
* #setPoweredOffFindingMode(int)} or if no value was set since boot, {@link
|
||||||
|
* #POWERED_OFF_FINDING_MODE_ENABLED} if this was the last value set by {@link
|
||||||
|
* #setPoweredOffFindingMode(int)}
|
||||||
|
*/
|
||||||
|
@FlaggedApi("com.android.nearby.flags.powered_off_finding")
|
||||||
|
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
|
||||||
|
public @PoweredOffFindingMode int getPoweredOffFindingMode() {
|
||||||
|
if (!isPoweredOffFindingSupported()) {
|
||||||
|
return POWERED_OFF_FINDING_MODE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return mService.getPoweredOffModeEnabled()
|
||||||
|
? POWERED_OFF_FINDING_MODE_ENABLED : POWERED_OFF_FINDING_MODE_DISABLED;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPoweredOffFindingSupported() {
|
||||||
|
return Boolean.parseBoolean(SystemProperties.get(POWER_OFF_FINDING_SUPPORTED_PROPERTY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean areLocationAndBluetoothEnabled() {
|
||||||
|
return mContext.getSystemService(BluetoothManager.class).getAdapter().isEnabled()
|
||||||
|
&& mContext.getSystemService(LocationManager.class).isLocationEnabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024, 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find My Device network ephemeral ID for powered off finding.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
parcelable PoweredOffFindingEphemeralId {
|
||||||
|
byte[20] bytes;
|
||||||
|
}
|
||||||
|
|
@ -35,12 +35,14 @@ import android.nearby.IBroadcastListener;
|
||||||
import android.nearby.INearbyManager;
|
import android.nearby.INearbyManager;
|
||||||
import android.nearby.IScanListener;
|
import android.nearby.IScanListener;
|
||||||
import android.nearby.NearbyManager;
|
import android.nearby.NearbyManager;
|
||||||
|
import android.nearby.PoweredOffFindingEphemeralId;
|
||||||
import android.nearby.ScanRequest;
|
import android.nearby.ScanRequest;
|
||||||
import android.nearby.aidl.IOffloadCallback;
|
import android.nearby.aidl.IOffloadCallback;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.nearby.injector.Injector;
|
import com.android.server.nearby.injector.Injector;
|
||||||
|
import com.android.server.nearby.managers.BluetoothFinderManager;
|
||||||
import com.android.server.nearby.managers.BroadcastProviderManager;
|
import com.android.server.nearby.managers.BroadcastProviderManager;
|
||||||
import com.android.server.nearby.managers.DiscoveryManager;
|
import com.android.server.nearby.managers.DiscoveryManager;
|
||||||
import com.android.server.nearby.managers.DiscoveryProviderManager;
|
import com.android.server.nearby.managers.DiscoveryProviderManager;
|
||||||
|
|
@ -50,6 +52,8 @@ import com.android.server.nearby.util.identity.CallerIdentity;
|
||||||
import com.android.server.nearby.util.permissions.BroadcastPermissions;
|
import com.android.server.nearby.util.permissions.BroadcastPermissions;
|
||||||
import com.android.server.nearby.util.permissions.DiscoveryPermissions;
|
import com.android.server.nearby.util.permissions.DiscoveryPermissions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** Service implementing nearby functionality. */
|
/** Service implementing nearby functionality. */
|
||||||
public class NearbyService extends INearbyManager.Stub {
|
public class NearbyService extends INearbyManager.Stub {
|
||||||
public static final String TAG = "NearbyService";
|
public static final String TAG = "NearbyService";
|
||||||
|
|
@ -79,6 +83,7 @@ public class NearbyService extends INearbyManager.Stub {
|
||||||
};
|
};
|
||||||
private final DiscoveryManager mDiscoveryProviderManager;
|
private final DiscoveryManager mDiscoveryProviderManager;
|
||||||
private final BroadcastProviderManager mBroadcastProviderManager;
|
private final BroadcastProviderManager mBroadcastProviderManager;
|
||||||
|
private final BluetoothFinderManager mBluetoothFinderManager;
|
||||||
|
|
||||||
public NearbyService(Context context) {
|
public NearbyService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
|
@ -90,6 +95,7 @@ public class NearbyService extends INearbyManager.Stub {
|
||||||
mNearbyConfiguration.refactorDiscoveryManager()
|
mNearbyConfiguration.refactorDiscoveryManager()
|
||||||
? new DiscoveryProviderManager(context, mInjector)
|
? new DiscoveryProviderManager(context, mInjector)
|
||||||
: new DiscoveryProviderManagerLegacy(context, mInjector);
|
: new DiscoveryProviderManagerLegacy(context, mInjector);
|
||||||
|
mBluetoothFinderManager = new BluetoothFinderManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -148,6 +154,30 @@ public class NearbyService extends INearbyManager.Stub {
|
||||||
mDiscoveryProviderManager.queryOffloadCapability(callback);
|
mDiscoveryProviderManager.queryOffloadCapability(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPoweredOffFindingEphemeralIds(List<PoweredOffFindingEphemeralId> eids) {
|
||||||
|
// Permissions check
|
||||||
|
enforceBluetoothPrivilegedPermission(mContext);
|
||||||
|
|
||||||
|
mBluetoothFinderManager.sendEids(eids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPoweredOffModeEnabled(boolean enabled) {
|
||||||
|
// Permissions check
|
||||||
|
enforceBluetoothPrivilegedPermission(mContext);
|
||||||
|
|
||||||
|
mBluetoothFinderManager.setPoweredOffFinderMode(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getPoweredOffModeEnabled() {
|
||||||
|
// Permissions check
|
||||||
|
enforceBluetoothPrivilegedPermission(mContext);
|
||||||
|
|
||||||
|
return mBluetoothFinderManager.getPoweredOffFinderMode();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the service initializer.
|
* Called by the service initializer.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.managers;
|
||||||
|
|
||||||
|
import android.nearby.PoweredOffFindingEphemeralId;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Connects to {@link IBluetoothFinder} HAL and invokes its API. */
|
||||||
|
// A placeholder implementation until the HAL API can be used.
|
||||||
|
public class BluetoothFinderManager {
|
||||||
|
|
||||||
|
private boolean mPoweredOffFindingModeEnabled = false;
|
||||||
|
|
||||||
|
/** An empty implementation of the corresponding HAL API call. */
|
||||||
|
public void sendEids(List<PoweredOffFindingEphemeralId> eids) {}
|
||||||
|
|
||||||
|
/** A placeholder implementation of the corresponding HAL API call. */
|
||||||
|
public void setPoweredOffFinderMode(boolean enable) {
|
||||||
|
mPoweredOffFindingModeEnabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A placeholder implementation of the corresponding HAL API call. */
|
||||||
|
public boolean getPoweredOffFinderMode() {
|
||||||
|
return mPoweredOffFindingModeEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,7 @@ android_test {
|
||||||
"framework-bluetooth.stubs.module_lib",
|
"framework-bluetooth.stubs.module_lib",
|
||||||
"framework-configinfrastructure",
|
"framework-configinfrastructure",
|
||||||
"framework-connectivity-t.impl",
|
"framework-connectivity-t.impl",
|
||||||
|
"framework-location.stubs.module_lib",
|
||||||
],
|
],
|
||||||
srcs: ["src/**/*.java"],
|
srcs: ["src/**/*.java"],
|
||||||
test_suites: [
|
test_suites: [
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,14 @@ import static android.nearby.ScanCallback.ERROR_UNSUPPORTED;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
import android.app.UiAutomation;
|
import android.app.UiAutomation;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothManager;
|
import android.bluetooth.BluetoothManager;
|
||||||
import android.bluetooth.cts.BTAdapterUtils;
|
import android.bluetooth.cts.BTAdapterUtils;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.location.LocationManager;
|
||||||
import android.nearby.BroadcastCallback;
|
import android.nearby.BroadcastCallback;
|
||||||
import android.nearby.BroadcastRequest;
|
import android.nearby.BroadcastRequest;
|
||||||
import android.nearby.NearbyDevice;
|
import android.nearby.NearbyDevice;
|
||||||
|
|
@ -42,6 +44,8 @@ import android.nearby.PrivateCredential;
|
||||||
import android.nearby.ScanCallback;
|
import android.nearby.ScanCallback;
|
||||||
import android.nearby.ScanRequest;
|
import android.nearby.ScanRequest;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -50,6 +54,7 @@ import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SdkSuppress;
|
import androidx.test.filters.SdkSuppress;
|
||||||
|
|
||||||
|
import com.android.compatibility.common.util.SystemUtil;
|
||||||
import com.android.modules.utils.build.SdkLevel;
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
@ -57,6 +62,7 @@ import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
@ -189,6 +195,92 @@ public class NearbyManagerTest {
|
||||||
mScanCallback.onError(ERROR_UNSUPPORTED);
|
mScanCallback.onError(ERROR_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testsetPoweredOffFindingEphemeralIds() {
|
||||||
|
// Replace with minSdkVersion when Build.VERSION_CODES.VANILLA_ICE_CREAM can be used.
|
||||||
|
assumeTrue(SdkLevel.isAtLeastV());
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (mNearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return;
|
||||||
|
|
||||||
|
mNearbyManager.setPoweredOffFindingEphemeralIds(List.of(new byte[20], new byte[20]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testsetPoweredOffFindingEphemeralIds_noPrivilegedPermission() {
|
||||||
|
// Replace with minSdkVersion when Build.VERSION_CODES.VANILLA_ICE_CREAM can be used.
|
||||||
|
assumeTrue(SdkLevel.isAtLeastV());
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (mNearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return;
|
||||||
|
|
||||||
|
mUiAutomation.dropShellPermissionIdentity();
|
||||||
|
|
||||||
|
assertThrows(SecurityException.class,
|
||||||
|
() -> mNearbyManager.setPoweredOffFindingEphemeralIds(List.of(new byte[20])));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAndGetPoweredOffFindingMode_enabled() {
|
||||||
|
// Replace with minSdkVersion when Build.VERSION_CODES.VANILLA_ICE_CREAM can be used.
|
||||||
|
assumeTrue(SdkLevel.isAtLeastV());
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (mNearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return;
|
||||||
|
|
||||||
|
enableLocation();
|
||||||
|
// enableLocation() has dropped shell permission identity.
|
||||||
|
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_PRIVILEGED);
|
||||||
|
|
||||||
|
mNearbyManager.setPoweredOffFindingMode(
|
||||||
|
NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED);
|
||||||
|
assertThat(mNearbyManager.getPoweredOffFindingMode())
|
||||||
|
.isEqualTo(NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAndGetPoweredOffFindingMode_disabled() {
|
||||||
|
// Replace with minSdkVersion when Build.VERSION_CODES.VANILLA_ICE_CREAM can be used.
|
||||||
|
assumeTrue(SdkLevel.isAtLeastV());
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (mNearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return;
|
||||||
|
|
||||||
|
mNearbyManager.setPoweredOffFindingMode(
|
||||||
|
NearbyManager.POWERED_OFF_FINDING_MODE_DISABLED);
|
||||||
|
assertThat(mNearbyManager.getPoweredOffFindingMode())
|
||||||
|
.isEqualTo(NearbyManager.POWERED_OFF_FINDING_MODE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPoweredOffFindingMode_noPrivilegedPermission() {
|
||||||
|
// Replace with minSdkVersion when Build.VERSION_CODES.VANILLA_ICE_CREAM can be used.
|
||||||
|
assumeTrue(SdkLevel.isAtLeastV());
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (mNearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return;
|
||||||
|
|
||||||
|
enableLocation();
|
||||||
|
mUiAutomation.dropShellPermissionIdentity();
|
||||||
|
|
||||||
|
assertThrows(SecurityException.class, () -> mNearbyManager
|
||||||
|
.setPoweredOffFindingMode(NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPoweredOffFindingMode_noPrivilegedPermission() {
|
||||||
|
// Replace with minSdkVersion when Build.VERSION_CODES.VANILLA_ICE_CREAM can be used.
|
||||||
|
assumeTrue(SdkLevel.isAtLeastV());
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (mNearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return;
|
||||||
|
|
||||||
|
mUiAutomation.dropShellPermissionIdentity();
|
||||||
|
|
||||||
|
assertThrows(SecurityException.class, () -> mNearbyManager.getPoweredOffFindingMode());
|
||||||
|
}
|
||||||
|
|
||||||
private void enableBluetooth() {
|
private void enableBluetooth() {
|
||||||
BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
|
BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
|
||||||
BluetoothAdapter bluetoothAdapter = manager.getAdapter();
|
BluetoothAdapter bluetoothAdapter = manager.getAdapter();
|
||||||
|
|
@ -197,6 +289,13 @@ public class NearbyManagerTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enableLocation() {
|
||||||
|
LocationManager locationManager = mContext.getSystemService(LocationManager.class);
|
||||||
|
UserHandle user = Process.myUserHandle();
|
||||||
|
SystemUtil.runWithShellPermissionIdentity(
|
||||||
|
mUiAutomation, () -> locationManager.setLocationEnabledForUser(true, user));
|
||||||
|
}
|
||||||
|
|
||||||
private static class OffloadCallback implements Consumer<OffloadCapability> {
|
private static class OffloadCallback implements Consumer<OffloadCapability> {
|
||||||
@Override
|
@Override
|
||||||
public void accept(OffloadCapability aBoolean) {
|
public void accept(OffloadCapability aBoolean) {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import android.nearby.ScanRequest
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Assert.assertThrows
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
@ -96,4 +97,49 @@ class NearbyManagerTest {
|
||||||
)
|
)
|
||||||
nearbyManager.stopBroadcast(broadcastCallback)
|
nearbyManager.stopBroadcast(broadcastCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Verify privileged app can set powered off finding ephemeral IDs without exception. */
|
||||||
|
@Test
|
||||||
|
fun testNearbyManagerSetPoweredOffFindingEphemeralIds_fromPrivilegedApp_succeed() {
|
||||||
|
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (nearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return
|
||||||
|
|
||||||
|
val eid = ByteArray(20)
|
||||||
|
|
||||||
|
nearbyManager.setPoweredOffFindingEphemeralIds(listOf(eid))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that [NearbyManager.setPoweredOffFindingEphemeralIds] checkes the ephemeral ID
|
||||||
|
* length.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testNearbyManagerSetPoweredOffFindingEphemeralIds_wrongSize_throwsException() {
|
||||||
|
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (nearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException::class.java) {
|
||||||
|
nearbyManager.setPoweredOffFindingEphemeralIds(listOf(ByteArray(21)))
|
||||||
|
}
|
||||||
|
assertThrows(IllegalArgumentException::class.java) {
|
||||||
|
nearbyManager.setPoweredOffFindingEphemeralIds(listOf(ByteArray(19)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify privileged app can set and get powered off finding mode without exception. */
|
||||||
|
@Test
|
||||||
|
fun testNearbyManagerSetGetPoweredOffMode_fromPrivilegedApp_succeed() {
|
||||||
|
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
|
||||||
|
// Only test supporting devices.
|
||||||
|
if (nearbyManager.getPoweredOffFindingMode()
|
||||||
|
== NearbyManager.POWERED_OFF_FINDING_MODE_UNSUPPORTED) return
|
||||||
|
|
||||||
|
nearbyManager.setPoweredOffFindingMode(NearbyManager.POWERED_OFF_FINDING_MODE_DISABLED)
|
||||||
|
assertThat(nearbyManager.getPoweredOffFindingMode())
|
||||||
|
.isEqualTo(NearbyManager.POWERED_OFF_FINDING_MODE_DISABLED)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.uiautomator.LogcatWaitMixin
|
import androidx.test.uiautomator.LogcatWaitMixin
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import java.time.Duration
|
||||||
|
import java.util.Calendar
|
||||||
import org.junit.Assert.assertThrows
|
import org.junit.Assert.assertThrows
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import java.time.Duration
|
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class NearbyManagerTest {
|
class NearbyManagerTest {
|
||||||
|
|
@ -151,6 +151,46 @@ class NearbyManagerTest {
|
||||||
).isTrue()
|
).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify untrusted app can't set powered off finding ephemeral IDs because it needs
|
||||||
|
* BLUETOOTH_PRIVILEGED permission which is not for use by third-party applications.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testNearbyManagerSetPoweredOffFindingEphemeralIds_fromUnTrustedApp_throwsException() {
|
||||||
|
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
|
||||||
|
val eid = ByteArray(20)
|
||||||
|
|
||||||
|
assertThrows(SecurityException::class.java) {
|
||||||
|
nearbyManager.setPoweredOffFindingEphemeralIds(listOf(eid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify untrusted app can't set powered off finding mode because it needs BLUETOOTH_PRIVILEGED
|
||||||
|
* permission which is not for use by third-party applications.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testNearbyManagerSetPoweredOffFindingMode_fromUnTrustedApp_throwsException() {
|
||||||
|
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
|
||||||
|
|
||||||
|
assertThrows(SecurityException::class.java) {
|
||||||
|
nearbyManager.setPoweredOffFindingMode(NearbyManager.POWERED_OFF_FINDING_MODE_ENABLED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify untrusted app can't get powered off finding mode because it needs BLUETOOTH_PRIVILEGED
|
||||||
|
* permission which is not for use by third-party applications.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testNearbyManagerGetPoweredOffFindingMode_fromUnTrustedApp_throwsException() {
|
||||||
|
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
|
||||||
|
|
||||||
|
assertThrows(SecurityException::class.java) {
|
||||||
|
nearbyManager.getPoweredOffFindingMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT = Duration.ofSeconds(5)
|
private val WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT = Duration.ofSeconds(5)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue