Merge "[CLATJ#28] ClatCoordinator: spawn clatd with flag POSIX_SPAWN_CLOEXEC_DEFAULT" into tm-dev
diff --git a/nearby/framework/java/android/nearby/INearbyManager.aidl b/nearby/framework/java/android/nearby/INearbyManager.aidl
index 62e109e..3fd5ecc 100644
--- a/nearby/framework/java/android/nearby/INearbyManager.aidl
+++ b/nearby/framework/java/android/nearby/INearbyManager.aidl
@@ -28,12 +28,13 @@
*/
interface INearbyManager {
- int registerScanListener(in ScanRequest scanRequest, in IScanListener listener);
+ int registerScanListener(in ScanRequest scanRequest, in IScanListener listener,
+ String packageName, @nullable String attributionTag);
void unregisterScanListener(in IScanListener listener);
void startBroadcast(in BroadcastRequestParcelable broadcastRequest,
- in IBroadcastListener callback);
+ in IBroadcastListener callback, String packageName, @nullable String attributionTag);
void stopBroadcast(in IBroadcastListener callback);
}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/IScanListener.aidl b/nearby/framework/java/android/nearby/IScanListener.aidl
index 54033aa..3e3b107 100644
--- a/nearby/framework/java/android/nearby/IScanListener.aidl
+++ b/nearby/framework/java/android/nearby/IScanListener.aidl
@@ -32,4 +32,7 @@
/** Reports a {@link NearbyDevice} is no longer within range. */
void onLost(in NearbyDeviceParcelable nearbyDeviceParcelable);
+
+ /** Reports when there is an error during scanning. */
+ void onError();
}
diff --git a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
index 1ad3571..a9d7cf7 100644
--- a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
+++ b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.le.ScanRecord;
-import android.bluetooth.le.ScanResult;
import android.os.Parcel;
import android.os.Parcelable;
@@ -89,6 +88,7 @@
@Nullable private final String mBluetoothAddress;
@Nullable private final String mFastPairModelId;
@Nullable private final byte[] mData;
+ @Nullable private final byte[] mSalt;
private NearbyDeviceParcelable(
@ScanRequest.ScanType int scanType,
@@ -100,7 +100,8 @@
PublicCredential publicCredential,
@Nullable String fastPairModelId,
@Nullable String bluetoothAddress,
- @Nullable byte[] data) {
+ @Nullable byte[] data,
+ @Nullable byte[] salt) {
mScanType = scanType;
mName = name;
mMedium = medium;
@@ -111,6 +112,7 @@
mFastPairModelId = fastPairModelId;
mBluetoothAddress = bluetoothAddress;
mData = data;
+ mSalt = salt;
}
/** No special parcel contents. */
@@ -149,6 +151,11 @@
dest.writeInt(mData.length);
dest.writeByteArray(mData);
}
+ dest.writeInt(mSalt == null ? 0 : 1);
+ if (mSalt != null) {
+ dest.writeInt(mSalt.length);
+ dest.writeByteArray(mSalt);
+ }
}
/** Returns a string representation of this ScanRequest. */
@@ -171,6 +178,8 @@
+ mFastPairModelId
+ ", data="
+ Arrays.toString(mData)
+ + ", salt="
+ + Arrays.toString(mSalt)
+ "]";
}
@@ -189,7 +198,8 @@
mBluetoothAddress, otherNearbyDeviceParcelable.mBluetoothAddress))
&& (Objects.equals(
mFastPairModelId, otherNearbyDeviceParcelable.mFastPairModelId))
- && (Arrays.equals(mData, otherNearbyDeviceParcelable.mData));
+ && (Arrays.equals(mData, otherNearbyDeviceParcelable.mData))
+ && (Arrays.equals(mSalt, otherNearbyDeviceParcelable.mSalt));
}
return false;
}
@@ -204,7 +214,8 @@
mPublicCredential.hashCode(),
mBluetoothAddress,
mFastPairModelId,
- Arrays.hashCode(mData));
+ Arrays.hashCode(mData),
+ Arrays.hashCode(mSalt));
}
/**
@@ -217,7 +228,11 @@
return mScanType;
}
- /** Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name. */
+ /**
+ * Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name.
+ *
+ * Used in Fast Pair.
+ */
@Nullable
public String getName() {
return mName;
@@ -226,6 +241,8 @@
/**
* Gets the {@link android.nearby.NearbyDevice.Medium} of the NearbyDeviceParcelable over which
* it is discovered.
+ *
+ * Used in Fast Pair and Nearby Presence.
*/
@NearbyDevice.Medium
public int getMedium() {
@@ -235,6 +252,8 @@
/**
* Gets the transmission power in dBm.
*
+ * Used in Fast Pair.
+ *
* @hide
*/
@IntRange(from = -127, to = 126)
@@ -242,7 +261,11 @@
return mTxPower;
}
- /** Gets the received signal strength in dBm. */
+ /**
+ * Gets the received signal strength in dBm.
+ *
+ * Used in Fast Pair and Nearby Presence.
+ */
@IntRange(from = -127, to = 126)
public int getRssi() {
return mRssi;
@@ -251,6 +274,8 @@
/**
* Gets the Action.
*
+ * Used in Nearby Presence.
+ *
* @hide
*/
@IntRange(from = -127, to = 126)
@@ -261,6 +286,8 @@
/**
* Gets the public credential.
*
+ * Used in Nearby Presence.
+ *
* @hide
*/
@NonNull
@@ -271,6 +298,8 @@
/**
* Gets the Fast Pair identifier. Returns {@code null} if there is no Model ID or this is not a
* Fast Pair device.
+ *
+ * Used in Fast Pair.
*/
@Nullable
public String getFastPairModelId() {
@@ -280,18 +309,36 @@
/**
* Gets the Bluetooth device hardware address. Returns {@code null} if the device is not
* discovered by Bluetooth.
+ *
+ * Used in Fast Pair.
*/
@Nullable
public String getBluetoothAddress() {
return mBluetoothAddress;
}
- /** Gets the raw data from the scanning. Returns {@code null} if there is no extra data. */
+ /**
+ * Gets the raw data from the scanning.
+ * Returns {@code null} if there is no extra data or this is not a Fast Pair device.
+ *
+ * Used in Fast Pair.
+ */
@Nullable
public byte[] getData() {
return mData;
}
+ /**
+ * Gets the salt in the advertisement from the Nearby Presence device.
+ * Returns {@code null} if this is not a Nearby Presence device.
+ *
+ * Used in Nearby Presence.
+ */
+ @Nullable
+ public byte[] getSalt() {
+ return mSalt;
+ }
+
/** Builder class for {@link NearbyDeviceParcelable}. */
public static final class Builder {
@Nullable private String mName;
@@ -304,6 +351,7 @@
@Nullable private String mFastPairModelId;
@Nullable private String mBluetoothAddress;
@Nullable private byte[] mData;
+ @Nullable private byte[] mSalt;
/**
* Sets the scan type of the NearbyDeviceParcelable.
@@ -410,7 +458,7 @@
* Sets the scanned raw data.
*
* @param data Data the scan. For example, {@link ScanRecord#getServiceData()} if scanned by
- * Bluetooth.
+ * Bluetooth.
*/
@NonNull
public Builder setData(@Nullable byte[] data) {
@@ -418,6 +466,17 @@
return this;
}
+ /**
+ * Sets the slat in the advertisement from the Nearby Presence device.
+ *
+ * @param salt in the advertisement from the Nearby Presence device.
+ */
+ @NonNull
+ public Builder setSalt(@Nullable byte[] salt) {
+ mSalt = salt;
+ return this;
+ }
+
/** Builds a ScanResult. */
@NonNull
public NearbyDeviceParcelable build() {
@@ -431,7 +490,8 @@
mPublicCredential,
mFastPairModelId,
mBluetoothAddress,
- mData);
+ mData,
+ mSalt);
}
}
}
diff --git a/nearby/framework/java/android/nearby/NearbyFrameworkInitializer.java b/nearby/framework/java/android/nearby/NearbyFrameworkInitializer.java
index 3780fbb..b732d67 100644
--- a/nearby/framework/java/android/nearby/NearbyFrameworkInitializer.java
+++ b/nearby/framework/java/android/nearby/NearbyFrameworkInitializer.java
@@ -43,7 +43,7 @@
NearbyManager.class,
(context, serviceBinder) -> {
INearbyManager service = INearbyManager.Stub.asInterface(serviceBinder);
- return new NearbyManager(service);
+ return new NearbyManager(context, service);
}
);
}
diff --git a/nearby/framework/java/android/nearby/NearbyManager.java b/nearby/framework/java/android/nearby/NearbyManager.java
index 3dd08da..9073f78 100644
--- a/nearby/framework/java/android/nearby/NearbyManager.java
+++ b/nearby/framework/java/android/nearby/NearbyManager.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.os.RemoteException;
import android.provider.Settings;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -85,6 +86,7 @@
private static final WeakHashMap<BroadcastCallback, WeakReference<BroadcastListenerTransport>>
sBroadcastListeners = new WeakHashMap<>();
+ private final Context mContext;
private final INearbyManager mService;
/**
@@ -92,7 +94,10 @@
*
* @param service the service object
*/
- NearbyManager(@NonNull INearbyManager service) {
+ NearbyManager(@NonNull Context context, @NonNull INearbyManager service) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(service);
+ mContext = context;
mService = service;
}
@@ -109,6 +114,26 @@
.setBluetoothAddress(nearbyDeviceParcelable.getBluetoothAddress())
.setData(nearbyDeviceParcelable.getData()).build();
}
+
+ if (scanType == ScanRequest.SCAN_TYPE_NEARBY_PRESENCE) {
+ PublicCredential publicCredential = nearbyDeviceParcelable.getPublicCredential();
+ if (publicCredential == null) {
+ return null;
+ }
+ byte[] salt = nearbyDeviceParcelable.getSalt();
+ if (salt == null) {
+ salt = new byte[0];
+ }
+ return new PresenceDevice.Builder(
+ // Use the public credential hash as the device Id.
+ String.valueOf(publicCredential.hashCode()),
+ salt,
+ publicCredential.getSecretId(),
+ publicCredential.getEncryptedMetadata())
+ .setRssi(nearbyDeviceParcelable.getRssi())
+ .addMedium(nearbyDeviceParcelable.getMedium())
+ .build();
+ }
return null;
}
@@ -143,7 +168,8 @@
Preconditions.checkState(transport.isRegistered());
transport.setExecutor(executor);
}
- @ScanStatus int status = mService.registerScanListener(scanRequest, transport);
+ @ScanStatus int status = mService.registerScanListener(scanRequest, transport,
+ mContext.getPackageName(), mContext.getAttributionTag());
if (status != ScanStatus.SUCCESS) {
return status;
}
@@ -208,8 +234,8 @@
Preconditions.checkState(transport.isRegistered());
transport.setExecutor(executor);
}
- mService.startBroadcast(new BroadcastRequestParcelable(broadcastRequest),
- transport);
+ mService.startBroadcast(new BroadcastRequestParcelable(broadcastRequest), transport,
+ mContext.getPackageName(), mContext.getAttributionTag());
sBroadcastListeners.put(callback, new WeakReference<>(transport));
}
} catch (RemoteException e) {
@@ -330,6 +356,15 @@
}
});
}
+
+ @Override
+ public void onError() {
+ mExecutor.execute(() -> {
+ if (mScanCallback != null) {
+ Log.e("NearbyManager", "onError: There is an error in scan.");
+ }
+ });
+ }
}
private static class BroadcastListenerTransport extends IBroadcastListener.Stub {
diff --git a/nearby/framework/java/android/nearby/PresenceDevice.java b/nearby/framework/java/android/nearby/PresenceDevice.java
index 12fc2a3..cb406e4 100644
--- a/nearby/framework/java/android/nearby/PresenceDevice.java
+++ b/nearby/framework/java/android/nearby/PresenceDevice.java
@@ -268,6 +268,11 @@
*/
public Builder(@NonNull String deviceId, @NonNull byte[] salt, @NonNull byte[] secretId,
@NonNull byte[] encryptedIdentity) {
+ Objects.requireNonNull(deviceId);
+ Objects.requireNonNull(salt);
+ Objects.requireNonNull(secretId);
+ Objects.requireNonNull(encryptedIdentity);
+
mDeviceId = deviceId;
mSalt = salt;
mSecretId = secretId;
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index e3e5b5d..2dee835 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -17,9 +17,12 @@
package com.android.server.nearby;
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.SystemService.PHASE_THIRD_PARTY_APPS_CAN_START;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
@@ -35,6 +38,7 @@
import android.nearby.ScanRequest;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.nearby.common.locator.LocatorContextWrapper;
import com.android.server.nearby.fastpair.FastPairManager;
import com.android.server.nearby.injector.ContextHubManagerAdapter;
@@ -43,13 +47,16 @@
import com.android.server.nearby.provider.BroadcastProviderManager;
import com.android.server.nearby.provider.DiscoveryProviderManager;
import com.android.server.nearby.provider.FastPairDataProvider;
+import com.android.server.nearby.util.identity.CallerIdentity;
+import com.android.server.nearby.util.permissions.BroadcastPermissions;
+import com.android.server.nearby.util.permissions.DiscoveryPermissions;
/** 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 Injector mInjector;
private final FastPairManager mFastPairManager;
private final PresenceManager mPresenceManager;
private final BroadcastReceiver mBluetoothReceiver =
@@ -60,11 +67,11 @@
intent.getIntExtra(
BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
- if (mSystemInjector != null) {
+ if (mInjector != null && mInjector instanceof SystemInjector) {
// 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();
+ ((SystemInjector) mInjector).initializeBluetoothAdapter();
}
}
}
@@ -74,18 +81,29 @@
public NearbyService(Context context) {
mContext = context;
- mSystemInjector = new SystemInjector(context);
- mProviderManager = new DiscoveryProviderManager(context, mSystemInjector);
- mBroadcastProviderManager = new BroadcastProviderManager(context, mSystemInjector);
+ mInjector = new SystemInjector(context);
+ mProviderManager = new DiscoveryProviderManager(context, mInjector);
+ mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector);
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
mFastPairManager = new FastPairManager(lcw);
mPresenceManager = new PresenceManager(lcw);
}
+ @VisibleForTesting
+ void setInjector(Injector injector) {
+ this.mInjector = injector;
+ }
+
@Override
@NearbyManager.ScanStatus
- public int registerScanListener(ScanRequest scanRequest, IScanListener listener) {
- if (mProviderManager.registerScanListener(scanRequest, listener)) {
+ public int registerScanListener(ScanRequest scanRequest, IScanListener listener,
+ String packageName, @Nullable String attributionTag) {
+ // Permissions check
+ enforceBluetoothPrivilegedPermission(mContext);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
+
+ if (mProviderManager.registerScanListener(scanRequest, listener, identity)) {
return NearbyManager.ScanStatus.SUCCESS;
}
return NearbyManager.ScanStatus.ERROR;
@@ -98,10 +116,12 @@
@Override
public void startBroadcast(BroadcastRequestParcelable broadcastRequestParcelable,
- IBroadcastListener listener) {
+ IBroadcastListener listener, String packageName, @Nullable String attributionTag) {
+ enforceBluetoothPrivilegedPermission(mContext);
+ BroadcastPermissions.enforceBroadcastPermission(
+ mContext, CallerIdentity.fromBinder(mContext, packageName, attributionTag));
mBroadcastProviderManager.startBroadcast(
- broadcastRequestParcelable.getBroadcastRequest(),
- listener);
+ broadcastRequestParcelable.getBroadcastRequest(), listener);
}
@Override
@@ -116,28 +136,48 @@
*/
public void onBootPhase(int phase) {
switch (phase) {
+ case PHASE_SYSTEM_SERVICES_READY:
+ if (mInjector instanceof SystemInjector) {
+ ((SystemInjector) mInjector).initializeAppOpsManager();
+ }
+ break;
case PHASE_THIRD_PARTY_APPS_CAN_START:
// Ensures that a fast pair data provider exists which will work in direct boot.
FastPairDataProvider.init(mContext);
break;
case PHASE_BOOT_COMPLETED:
- // The nearby service must be functioning after this boot phase.
- mSystemInjector.initializeBluetoothAdapter();
+ if (mInjector instanceof SystemInjector) {
+ // The nearby service must be functioning after this boot phase.
+ ((SystemInjector) mInjector).initializeBluetoothAdapter();
+ // Initialize ContextManager for CHRE scan.
+ ((SystemInjector) mInjector).initializeContextHubManagerAdapter();
+ }
mContext.registerReceiver(
mBluetoothReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mFastPairManager.initiate();
- // Initialize ContextManager for CHRE scan.
- mSystemInjector.initializeContextHubManagerAdapter();
mPresenceManager.initiate();
break;
}
}
+ /**
+ * If the calling process of has not been granted
+ * {@link android.Manifest.permission.BLUETOOTH_PRIVILEGED} permission,
+ * throw a {@link SecurityException}.
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ private static void enforceBluetoothPrivilegedPermission(Context context) {
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ "Need BLUETOOTH PRIVILEGED permission");
+ }
+
private static final class SystemInjector implements Injector {
private final Context mContext;
@Nullable private BluetoothAdapter mBluetoothAdapter;
@Nullable private ContextHubManagerAdapter mContextHubManagerAdapter;
+ @Nullable private AppOpsManager mAppOpsManager;
SystemInjector(Context context) {
mContext = context;
@@ -155,6 +195,12 @@
return mContextHubManagerAdapter;
}
+ @Override
+ @Nullable
+ public AppOpsManager getAppOpsManager() {
+ return mAppOpsManager;
+ }
+
synchronized void initializeBluetoothAdapter() {
if (mBluetoothAdapter != null) {
return;
@@ -176,5 +222,12 @@
}
mContextHubManagerAdapter = new ContextHubManagerAdapter(manager);
}
+
+ synchronized void initializeAppOpsManager() {
+ if (mAppOpsManager != null) {
+ return;
+ }
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ }
}
}
diff --git a/nearby/service/java/com/android/server/nearby/injector/Injector.java b/nearby/service/java/com/android/server/nearby/injector/Injector.java
index f990dc9..57784a9 100644
--- a/nearby/service/java/com/android/server/nearby/injector/Injector.java
+++ b/nearby/service/java/com/android/server/nearby/injector/Injector.java
@@ -16,6 +16,7 @@
package com.android.server.nearby.injector;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
/**
@@ -29,4 +30,7 @@
/** Get the ContextHubManagerAdapter for ChreDiscoveryProvider to scan. */
ContextHubManagerAdapter getContextHubManagerAdapter();
+
+ /** Get the AppOpsManager to control access. */
+ AppOpsManager getAppOpsManager();
}
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
index 80ad88d..d1c72ae 100644
--- a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
@@ -30,9 +30,14 @@
/** Creates a {@link PresenceDiscoveryResult} from the scan data. */
public static PresenceDiscoveryResult fromDevice(NearbyDeviceParcelable device) {
+ byte[] salt = device.getSalt();
+ if (salt == null) {
+ salt = new byte[0];
+ }
return new PresenceDiscoveryResult.Builder()
.setTxPower(device.getTxPower())
.setRssi(device.getRssi())
+ .setSalt(salt)
.addPresenceAction(device.getAction())
.setPublicCredential(device.getPublicCredential())
.build();
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
index 3602787..67392ad 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
@@ -25,8 +25,8 @@
import android.os.ParcelUuid;
import com.android.server.nearby.injector.Injector;
-import com.android.server.nearby.presence.PresenceConstants;
+import java.util.UUID;
import java.util.concurrent.Executor;
/**
@@ -69,11 +69,13 @@
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.setConnectable(true)
.build();
+
+ // TODO(b/230538655) Use empty data until Presence V1 protocol is implemented.
+ ParcelUuid emptyParcelUuid = new ParcelUuid(new UUID(0L, 0L));
+ byte[] emptyAdvertisementPackets = new byte[0];
AdvertiseData advertiseData =
new AdvertiseData.Builder()
- .addServiceData(new ParcelUuid(PresenceConstants.PRESENCE_UUID),
- advertisementPackets).build();
-
+ .addServiceData(emptyParcelUuid, emptyAdvertisementPackets).build();
try {
mBroadcastListener = listener;
bluetoothLeAdvertiser.startAdvertising(settings, advertiseData, this);
diff --git a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
index a70ef13..f20c6d8 100644
--- a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
@@ -33,11 +33,11 @@
import com.google.protobuf.InvalidProtocolBufferException;
-import service.proto.Blefilter;
-
import java.util.Collections;
import java.util.concurrent.Executor;
+import service.proto.Blefilter;
+
/** Discovery provider that uses CHRE Nearby Nanoapp to do scanning. */
public class ChreDiscoveryProvider extends AbstractDiscoveryProvider {
// Nanoapp ID reserved for Nearby Presence.
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
index 53d61c2..bdeab51 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
@@ -21,6 +21,7 @@
import static com.android.server.nearby.NearbyService.TAG;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.nearby.IScanListener;
import android.nearby.NearbyDeviceParcelable;
@@ -35,6 +36,8 @@
import com.android.server.nearby.injector.Injector;
import com.android.server.nearby.metrics.NearbyMetrics;
import com.android.server.nearby.presence.PresenceDiscoveryResult;
+import com.android.server.nearby.util.identity.CallerIdentity;
+import com.android.server.nearby.util.permissions.DiscoveryPermissions;
import java.util.ArrayList;
import java.util.HashMap;
@@ -53,6 +56,7 @@
private final BleDiscoveryProvider mBleDiscoveryProvider;
@Nullable private final ChreDiscoveryProvider mChreDiscoveryProvider;
private @ScanRequest.ScanMode int mScanMode;
+ private final Injector mInjector;
@GuardedBy("mLock")
private Map<IBinder, ScanListenerRecord> mScanTypeScanListenerRecordMap;
@@ -60,12 +64,26 @@
@Override
public void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice) {
synchronized (mLock) {
+ AppOpsManager appOpsManager = Objects.requireNonNull(mInjector.getAppOpsManager());
for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
if (record == null) {
Log.w(TAG, "DiscoveryProviderManager cannot find the scan record.");
continue;
}
+ CallerIdentity callerIdentity = record.getCallerIdentity();
+ if (!DiscoveryPermissions.noteDiscoveryResultDelivery(
+ appOpsManager, callerIdentity)) {
+ Log.w(TAG, "[DiscoveryProviderManager] scan permission revoked "
+ + "- not forwarding results");
+ try {
+ record.getScanListener().onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "DiscoveryProviderManager failed to report error.", e);
+ }
+ return;
+ }
+
if (nearbyDevice.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
List<ScanFilter> presenceFilters =
record.getScanRequest().getScanFilters().stream()
@@ -103,13 +121,14 @@
new ChreDiscoveryProvider(
mContext, new ChreCommunication(injector, executor), executor);
mScanTypeScanListenerRecordMap = new HashMap<>();
+ mInjector = injector;
}
/**
* Registers the listener in the manager and starts scan according to the requested scan mode.
*/
- public boolean registerScanListener(ScanRequest scanRequest, IScanListener listener) {
- Log.i(TAG, "DiscoveryProviderManager registerScanListener");
+ public boolean registerScanListener(ScanRequest scanRequest, IScanListener listener,
+ CallerIdentity callerIdentity) {
synchronized (mLock) {
IBinder listenerBinder = listener.asBinder();
if (mScanTypeScanListenerRecordMap.containsKey(listener.asBinder())) {
@@ -120,7 +139,8 @@
return true;
}
}
- ScanListenerRecord scanListenerRecord = new ScanListenerRecord(scanRequest, listener);
+ ScanListenerRecord scanListenerRecord =
+ new ScanListenerRecord(scanRequest, listener, callerIdentity);
mScanTypeScanListenerRecordMap.put(listenerBinder, scanListenerRecord);
if (!startProviders(scanRequest)) {
@@ -273,9 +293,13 @@
private final IScanListener mScanListener;
- ScanListenerRecord(ScanRequest scanRequest, IScanListener iScanListener) {
+ private final CallerIdentity mCallerIdentity;
+
+ ScanListenerRecord(ScanRequest scanRequest, IScanListener iScanListener,
+ CallerIdentity callerIdentity) {
mScanListener = iScanListener;
mScanRequest = scanRequest;
+ mCallerIdentity = callerIdentity;
}
IScanListener getScanListener() {
@@ -286,6 +310,10 @@
return mScanRequest;
}
+ CallerIdentity getCallerIdentity() {
+ return mCallerIdentity;
+ }
+
@Override
public boolean equals(Object other) {
if (other instanceof ScanListenerRecord) {
diff --git a/nearby/service/java/com/android/server/nearby/util/identity/CallerIdentity.java b/nearby/service/java/com/android/server/nearby/util/identity/CallerIdentity.java
new file mode 100644
index 0000000..b5c80b9
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/identity/CallerIdentity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util.identity;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Process;
+
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Identifying information on a caller.
+ *
+ * @hide
+ */
+public final class CallerIdentity {
+
+ /**
+ * Creates a CallerIdentity from the current binder identity, using the given package, feature
+ * id, and listener id. The package will be checked to enforce it belongs to the calling uid,
+ * and a security exception will be thrown if it is invalid.
+ */
+ public static CallerIdentity fromBinder(Context context, String packageName,
+ @Nullable String attributionTag) {
+ int uid = Binder.getCallingUid();
+ if (!contains(context.getPackageManager().getPackagesForUid(uid), packageName)) {
+ throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
+ }
+ return fromBinderUnsafe(packageName, attributionTag);
+ }
+
+ /**
+ * Construct a CallerIdentity for test purposes.
+ */
+ @VisibleForTesting
+ public static CallerIdentity forTest(int uid, int pid, String packageName,
+ @Nullable String attributionTag) {
+ return new CallerIdentity(uid, pid, packageName, attributionTag);
+ }
+
+ /**
+ * Creates a CallerIdentity from the current binder identity, using the given package, feature
+ * id, and listener id. The package will not be checked to enforce that it belongs to the
+ * calling uid - this method should only be used if the package will be validated by some other
+ * means, such as an appops call.
+ */
+ public static CallerIdentity fromBinderUnsafe(String packageName,
+ @Nullable String attributionTag) {
+ return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(),
+ packageName, attributionTag);
+ }
+
+ private final int mUid;
+
+ private final int mPid;
+
+ private final String mPackageName;
+
+ private final @Nullable String mAttributionTag;
+
+
+ private CallerIdentity(int uid, int pid, String packageName,
+ @Nullable String attributionTag) {
+ this.mUid = uid;
+ this.mPid = pid;
+ this.mPackageName = Objects.requireNonNull(packageName);
+ this.mAttributionTag = attributionTag;
+ }
+
+ /** The calling UID. */
+ public int getUid() {
+ return mUid;
+ }
+
+ /** The calling PID. */
+ public int getPid() {
+ return mPid;
+ }
+
+ /** The calling package name. */
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /** The calling attribution tag. */
+ public String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ /** Returns true if this represents a system server identity. */
+ public boolean isSystemServer() {
+ return mUid == Process.SYSTEM_UID;
+ }
+
+ @Override
+ public String toString() {
+ int length = 10 + mPackageName.length();
+ if (mAttributionTag != null) {
+ length += mAttributionTag.length();
+ }
+
+ StringBuilder builder = new StringBuilder(length);
+ builder.append(mUid).append("/").append(mPackageName);
+ if (mAttributionTag != null) {
+ builder.append("[");
+ if (mAttributionTag.startsWith(mPackageName)) {
+ builder.append(mAttributionTag.substring(mPackageName.length()));
+ } else {
+ builder.append(mAttributionTag);
+ }
+ builder.append("]");
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CallerIdentity)) {
+ return false;
+ }
+ CallerIdentity that = (CallerIdentity) o;
+ return mUid == that.mUid
+ && mPid == that.mPid
+ && mPackageName.equals(that.mPackageName)
+ && Objects.equals(mAttributionTag, that.mAttributionTag);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUid, mPid, mPackageName, mAttributionTag);
+ }
+
+ private static <T> boolean contains(@Nullable T[] array, T value) {
+ return indexOf(array, value) != -1;
+ }
+
+ /**
+ * Return first index of {@code value} in {@code array}, or {@code -1} if
+ * not found.
+ */
+ private static <T> int indexOf(@Nullable T[] array, T value) {
+ if (array == null) return -1;
+ for (int i = 0; i < array.length; i++) {
+ if (Objects.equals(array[i], value)) return i;
+ }
+ return -1;
+ }
+}
diff --git a/nearby/service/java/com/android/server/nearby/util/permissions/BroadcastPermissions.java b/nearby/service/java/com/android/server/nearby/util/permissions/BroadcastPermissions.java
new file mode 100644
index 0000000..c11c234
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/permissions/BroadcastPermissions.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util.permissions;
+
+import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static java.lang.annotation.ElementType.TYPE_USE;
+
+import android.content.Context;
+
+import androidx.annotation.IntDef;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.nearby.util.identity.CallerIdentity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Utilities for handling presence broadcast runtime permissions. */
+public class BroadcastPermissions {
+
+ /** Indicates no permissions are present, or no permissions are required. */
+ public static final int PERMISSION_NONE = 0;
+
+ /** Indicates only the Bluetooth advertise permission is present, or is required. */
+ public static final int PERMISSION_BLUETOOTH_ADVERTISE = 1;
+
+ /** Broadcast permission levels. */
+ @IntDef({
+ PERMISSION_NONE,
+ PERMISSION_BLUETOOTH_ADVERTISE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({TYPE_USE})
+ public @interface BroadcastPermissionLevel {}
+
+ /**
+ * Throws a security exception if the caller does not hold the required broadcast permissions.
+ */
+ public static void enforceBroadcastPermission(Context context, CallerIdentity callerIdentity) {
+ if (!checkCallerBroadcastPermission(context, callerIdentity)) {
+ throw new SecurityException("uid " + callerIdentity.getUid()
+ + " does not have " + BLUETOOTH_ADVERTISE + ".");
+ }
+ }
+
+ /**
+ * Checks if the app has the permission to broadcast.
+ *
+ * @return true if the app does have the permission, false otherwise.
+ */
+ public static boolean checkCallerBroadcastPermission(Context context,
+ CallerIdentity callerIdentity) {
+ int uid = callerIdentity.getUid();
+ int pid = callerIdentity.getPid();
+
+ if (!checkBroadcastPermission(
+ getPermissionLevel(context, uid, pid), PERMISSION_BLUETOOTH_ADVERTISE)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Returns the permission level of the caller. */
+ @VisibleForTesting
+ @BroadcastPermissionLevel
+ public static int getPermissionLevel(
+ Context context, int uid, int pid) {
+ boolean isBluetoothAdvertiseGranted =
+ context.checkPermission(BLUETOOTH_ADVERTISE, pid, uid)
+ == PERMISSION_GRANTED;
+ if (isBluetoothAdvertiseGranted) {
+ return PERMISSION_BLUETOOTH_ADVERTISE;
+ }
+
+ return PERMISSION_NONE;
+ }
+
+ /** Returns false if the given permission level does not meet the required permission level. */
+ private static boolean checkBroadcastPermission(
+ @BroadcastPermissionLevel int permissionLevel,
+ @BroadcastPermissionLevel int requiredPermissionLevel) {
+ return permissionLevel >= requiredPermissionLevel;
+ }
+
+ private BroadcastPermissions() {}
+}
+
diff --git a/nearby/service/java/com/android/server/nearby/util/permissions/DiscoveryPermissions.java b/nearby/service/java/com/android/server/nearby/util/permissions/DiscoveryPermissions.java
new file mode 100644
index 0000000..b0888ba
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/permissions/DiscoveryPermissions.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util.permissions;
+
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static java.lang.annotation.ElementType.TYPE_USE;
+
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import androidx.annotation.IntDef;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.nearby.util.identity.CallerIdentity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Utilities for handling presence discovery runtime permissions. */
+public class DiscoveryPermissions {
+
+ /** Indicates no permissions are present, or no permissions are required. */
+ public static final int PERMISSION_NONE = 0;
+
+ /** Indicates only the Bluetooth scan permission is present, or is required. */
+ public static final int PERMISSION_BLUETOOTH_SCAN = 1;
+
+ // String in AppOpsManager
+ @VisibleForTesting
+ public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
+
+ /** Discovery permission levels. */
+ @IntDef({
+ PERMISSION_NONE,
+ PERMISSION_BLUETOOTH_SCAN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({TYPE_USE})
+ public @interface DiscoveryPermissionLevel {}
+
+ /**
+ * Throws a security exception if the caller does not hold the required scan permissions.
+ */
+ public static void enforceDiscoveryPermission(Context context, CallerIdentity callerIdentity) {
+ if (!checkCallerDiscoveryPermission(context, callerIdentity)) {
+ throw new SecurityException("uid " + callerIdentity.getUid() + " does not have "
+ + BLUETOOTH_SCAN + ".");
+ }
+ }
+
+ /**
+ * Checks if the caller has the permission to scan.
+ */
+ public static boolean checkCallerDiscoveryPermission(Context context,
+ CallerIdentity callerIdentity) {
+ int uid = callerIdentity.getUid();
+ int pid = callerIdentity.getPid();
+
+ return checkDiscoveryPermission(
+ getPermissionLevel(context, uid, pid), PERMISSION_BLUETOOTH_SCAN);
+ }
+
+ /**
+ * Checks if the caller is allowed by AppOpsManager to scan.
+ */
+ public static boolean noteDiscoveryResultDelivery(AppOpsManager appOpsManager,
+ CallerIdentity callerIdentity) {
+ return noteAppOpAllowed(appOpsManager, callerIdentity, /* message= */ null);
+ }
+
+ private static boolean noteAppOpAllowed(AppOpsManager appOpsManager,
+ CallerIdentity identity, @Nullable String message) {
+ return appOpsManager.noteOp(asAppOp(PERMISSION_BLUETOOTH_SCAN),
+ identity.getUid(), identity.getPackageName(), identity.getAttributionTag(), message)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /** Returns the permission level of the caller. */
+ public static @DiscoveryPermissionLevel int getPermissionLevel(
+ Context context, int uid, int pid) {
+ boolean isBluetoothScanGranted =
+ context.checkPermission(BLUETOOTH_SCAN, pid, uid) == PERMISSION_GRANTED;
+ if (isBluetoothScanGranted) {
+ return PERMISSION_BLUETOOTH_SCAN;
+ }
+ return PERMISSION_NONE;
+ }
+
+ /** Returns false if the given permission lev`el does not meet the required permission level. */
+ private static boolean checkDiscoveryPermission(
+ @DiscoveryPermissionLevel int permissionLevel,
+ @DiscoveryPermissionLevel int requiredPermissionLevel) {
+ return permissionLevel >= requiredPermissionLevel;
+ }
+
+ /** Returns the app op string according to the permission level. */
+ private static String asAppOp(@DiscoveryPermissionLevel int permissionLevel) {
+ if (permissionLevel == PERMISSION_BLUETOOTH_SCAN) {
+ return "android:bluetooth_scan";
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private DiscoveryPermissions() {}
+}
diff --git a/nearby/tests/cts/fastpair/AndroidManifest.xml b/nearby/tests/cts/fastpair/AndroidManifest.xml
index ce841f2..96e2783 100644
--- a/nearby/tests/cts/fastpair/AndroidManifest.xml
+++ b/nearby/tests/cts/fastpair/AndroidManifest.xml
@@ -19,6 +19,8 @@
package="android.nearby.cts">
<uses-sdk android:minSdkVersion="32" android:targetSdkVersion="32" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
index b9ab95f..6b9bce9 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
@@ -80,7 +80,7 @@
"NearbyDeviceParcelable[name=testDevice, medium=BLE, txPower=0, rssi=-60,"
+ " action=0, bluetoothAddress="
+ BLUETOOTH_ADDRESS
- + ", fastPairModelId=null, data=null]");
+ + ", fastPairModelId=null, data=null, salt=null]");
}
@Test
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
index 9720865..6824ca6 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -16,6 +16,7 @@
package android.nearby.cts;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.READ_DEVICE_CONFIG;
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
@@ -23,6 +24,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.app.UiAutomation;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
@@ -51,6 +54,7 @@
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -73,10 +77,32 @@
private UiAutomation mUiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ private ScanRequest mScanRequest = new ScanRequest.Builder()
+ .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR)
+ .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY)
+ .setBleEnabled(true)
+ .build();
+ private ScanCallback mScanCallback = new ScanCallback() {
+ @Override
+ public void onDiscovered(@NonNull NearbyDevice device) {
+ }
+
+ @Override
+ public void onUpdated(@NonNull NearbyDevice device) {
+ }
+
+ @Override
+ public void onLost(@NonNull NearbyDevice device) {
+ }
+ };
+ private static final Executor EXECUTOR = Executors.newSingleThreadExecutor();
+
@Before
public void setUp() {
- mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG);
- DeviceConfig.setProperty(NAMESPACE_TETHERING, "nearby_enable_presence_broadcast_legacy",
+ mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG,
+ BLUETOOTH_PRIVILEGED);
+ DeviceConfig.setProperty(NAMESPACE_TETHERING,
+ "nearby_enable_presence_broadcast_legacy",
"true", false);
mContext = InstrumentationRegistry.getContext();
@@ -88,28 +114,16 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_startAndStopScan() {
- ScanRequest scanRequest = new ScanRequest.Builder()
- .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR)
- .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY)
- .setBleEnabled(true)
- .build();
- ScanCallback scanCallback = new ScanCallback() {
- @Override
- public void onDiscovered(@NonNull NearbyDevice device) {
- }
+ mNearbyManager.startScan(mScanRequest, EXECUTOR, mScanCallback);
+ mNearbyManager.stopScan(mScanCallback);
+ }
- @Override
- public void onUpdated(@NonNull NearbyDevice device) {
-
- }
-
- @Override
- public void onLost(@NonNull NearbyDevice device) {
-
- }
- };
- mNearbyManager.startScan(scanRequest, Executors.newSingleThreadExecutor(), scanCallback);
- mNearbyManager.stopScan(scanCallback);
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_startScan_noPrivilegedPermission() {
+ mUiAutomation.dropShellPermissionIdentity();
+ assertThrows(SecurityException.class, () -> mNearbyManager
+ .startScan(mScanRequest, EXECUTOR, mScanCallback));
}
@Test
diff --git a/nearby/tests/unit/AndroidManifest.xml b/nearby/tests/unit/AndroidManifest.xml
index 88c0f5f..9f58baf 100644
--- a/nearby/tests/unit/AndroidManifest.xml
+++ b/nearby/tests/unit/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
index 31965a4..e250254 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
@@ -16,10 +16,18 @@
package com.android.server.nearby;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.READ_DEVICE_CONFIG;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
+import android.app.AppOpsManager;
import android.app.UiAutomation;
import android.content.Context;
import android.nearby.IScanListener;
@@ -27,12 +35,17 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.util.permissions.DiscoveryPermissions;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
public final class NearbyServiceTest {
+ private static final String PACKAGE_NAME = "android.nearby.test";
private Context mContext;
private NearbyService mService;
private ScanRequest mScanRequest;
@@ -41,19 +54,36 @@
@Mock
private IScanListener mScanListener;
+ @Mock
+ private AppOpsManager mMockAppOpsManager;
@Before
- public void setup() {
+ public void setUp() {
initMocks(this);
- mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG);
+ mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, BLUETOOTH_PRIVILEGED);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mService = new NearbyService(mContext);
mScanRequest = createScanRequest();
}
+ @After
+ public void tearDown() {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
@Test
public void test_register() {
- mService.registerScanListener(mScanRequest, mScanListener);
+ setMockInjector(/* isMockOpsAllowed= */ true);
+ mService.registerScanListener(mScanRequest, mScanListener, PACKAGE_NAME,
+ /* attributionTag= */ null);
+ }
+
+ @Test
+ public void test_register_noPrivilegedPermission_throwsException() {
+ mUiAutomation.dropShellPermissionIdentity();
+ assertThrows(java.lang.SecurityException.class,
+ () -> mService.registerScanListener(mScanRequest, mScanListener, PACKAGE_NAME,
+ /* attributionTag= */ null));
}
@Test
@@ -67,4 +97,14 @@
.setBleEnabled(true)
.build();
}
+
+ private void setMockInjector(boolean isMockOpsAllowed) {
+ Injector injector = mock(Injector.class);
+ when(injector.getAppOpsManager()).thenReturn(mMockAppOpsManager);
+ when(mMockAppOpsManager.noteOp(eq(DiscoveryPermissions.OPSTR_BLUETOOTH_SCAN),
+ anyInt(), eq(PACKAGE_NAME), nullable(String.class), nullable(String.class)))
+ .thenReturn(isMockOpsAllowed
+ ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
+ mService.setInjector(injector);
+ }
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
index d32e325..3b34655 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
@@ -16,9 +16,6 @@
package com.android.server.nearby.presence;
-
-
-
import androidx.test.filters.SdkSuppress;
import org.junit.Before;
@@ -26,7 +23,6 @@
import org.mockito.MockitoAnnotations;
public class PresenceManagerTest {
- private PresenceManager mPresenceManager;
@Before
public void setup() {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
index f485e18..d06a785 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseSettings;
@@ -61,7 +62,6 @@
public void testOnStatus_success() {
byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
mBleBroadcastProvider.start(advertiseBytes, mBroadcastListener);
- verify(mBroadcastListener).onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
AdvertiseSettings settings = new AdvertiseSettings.Builder().build();
mBleBroadcastProvider.onStartSuccess(settings);
@@ -74,7 +74,8 @@
mBleBroadcastProvider.start(advertiseBytes, mBroadcastListener);
mBleBroadcastProvider.onStartFailure(BroadcastCallback.STATUS_FAILURE);
- verify(mBroadcastListener, times(2)).onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
+ verify(mBroadcastListener, times(1))
+ .onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
}
private static class TestInjector implements Injector {
@@ -90,5 +91,10 @@
public ContextHubManagerAdapter getContextHubManagerAdapter() {
return null;
}
+
+ @Override
+ public AppOpsManager getAppOpsManager() {
+ return null;
+ }
}
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
index 8e97443..902cc33 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
@@ -87,6 +88,11 @@
public ContextHubManagerAdapter getContextHubManagerAdapter() {
return null;
}
+
+ @Override
+ public AppOpsManager getAppOpsManager() {
+ return null;
+ }
}
private ScanResult createScanResult() {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java
new file mode 100644
index 0000000..1a22412
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util;
+
+import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.server.nearby.util.permissions.BroadcastPermissions.PERMISSION_BLUETOOTH_ADVERTISE;
+import static com.android.server.nearby.util.permissions.BroadcastPermissions.PERMISSION_NONE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+
+import com.android.server.nearby.util.identity.CallerIdentity;
+import com.android.server.nearby.util.permissions.BroadcastPermissions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+/**
+ * Unit test for {@link BroadcastPermissions}
+ */
+public final class BroadcastPermissionsTest {
+
+ private static final String PACKAGE_NAME = "android.nearby.test";
+ private static final int UID = 1234;
+ private static final int PID = 5678;
+ private CallerIdentity mCallerIdentity;
+
+ @Mock private Context mMockContext;
+
+ @Before
+ public void setup() {
+ initMocks(this);
+ mCallerIdentity = CallerIdentity
+ .forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
+ }
+
+ @Test
+ public void test_checkCallerBroadcastPermission_granted() {
+ when(mMockContext.checkPermission(BLUETOOTH_ADVERTISE, PID, UID))
+ .thenReturn(PERMISSION_GRANTED);
+
+ assertThat(BroadcastPermissions
+ .checkCallerBroadcastPermission(mMockContext, mCallerIdentity))
+ .isTrue();
+ }
+
+ @Test
+ public void test_checkCallerBroadcastPermission_deniedPermission() {
+ when(mMockContext.checkPermission(BLUETOOTH_ADVERTISE, PID, UID))
+ .thenReturn(PERMISSION_DENIED);
+
+ assertThat(BroadcastPermissions
+ .checkCallerBroadcastPermission(mMockContext, mCallerIdentity))
+ .isFalse();
+ }
+
+ @Test
+ public void test_getPermissionLevel_none() {
+ when(mMockContext.checkPermission(BLUETOOTH_ADVERTISE, PID, UID))
+ .thenReturn(PERMISSION_DENIED);
+
+ assertThat(BroadcastPermissions.getPermissionLevel(mMockContext, UID, PID))
+ .isEqualTo(PERMISSION_NONE);
+ }
+
+ @Test
+ public void test_getPermissionLevel_advertising() {
+ when(mMockContext.checkPermission(BLUETOOTH_ADVERTISE, PID, UID))
+ .thenReturn(PERMISSION_GRANTED);
+
+ assertThat(BroadcastPermissions.getPermissionLevel(mMockContext, UID, PID))
+ .isEqualTo(PERMISSION_BLUETOOTH_ADVERTISE);
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/DiscoveryPermissionsTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/DiscoveryPermissionsTest.java
new file mode 100644
index 0000000..d953a60
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/DiscoveryPermissionsTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util;
+
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.server.nearby.util.permissions.DiscoveryPermissions.PERMISSION_BLUETOOTH_SCAN;
+import static com.android.server.nearby.util.permissions.DiscoveryPermissions.PERMISSION_NONE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.server.nearby.util.identity.CallerIdentity;
+import com.android.server.nearby.util.permissions.DiscoveryPermissions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+/**
+ * Unit test for {@link DiscoveryPermissions}
+ */
+public final class DiscoveryPermissionsTest {
+
+ private static final String PACKAGE_NAME = "android.nearby.test";
+ private static final int UID = 1234;
+ private static final int PID = 5678;
+ private CallerIdentity mCallerIdentity;
+
+ @Mock
+ private Context mMockContext;
+ @Mock private AppOpsManager mMockAppOps;
+
+ @Before
+ public void setup() {
+ initMocks(this);
+ mCallerIdentity = CallerIdentity
+ .forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
+ }
+
+ @Test
+ public void test_enforceCallerDiscoveryPermission_exception() {
+ when(mMockContext.checkPermission(BLUETOOTH_SCAN, PID, UID)).thenReturn(PERMISSION_DENIED);
+
+ assertThrows(SecurityException.class,
+ () -> DiscoveryPermissions
+ .enforceDiscoveryPermission(mMockContext, mCallerIdentity));
+ }
+
+ @Test
+ public void test_checkCallerDiscoveryPermission_granted() {
+ when(mMockContext.checkPermission(BLUETOOTH_SCAN, PID, UID)).thenReturn(PERMISSION_GRANTED);
+
+ assertThat(DiscoveryPermissions
+ .checkCallerDiscoveryPermission(mMockContext, mCallerIdentity))
+ .isTrue();
+ }
+
+ @Test
+ public void test_checkCallerDiscoveryPermission_denied() {
+ when(mMockContext.checkPermission(BLUETOOTH_SCAN, PID, UID)).thenReturn(PERMISSION_DENIED);
+
+ assertThat(DiscoveryPermissions
+ .checkCallerDiscoveryPermission(mMockContext, mCallerIdentity))
+ .isFalse();
+ }
+
+ @Test
+ public void test_checkNoteOpPermission_granted() {
+ when(mMockAppOps.noteOp(DiscoveryPermissions.OPSTR_BLUETOOTH_SCAN, UID, PACKAGE_NAME,
+ null, null)).thenReturn(AppOpsManager.MODE_ALLOWED);
+
+ assertThat(DiscoveryPermissions
+ .noteDiscoveryResultDelivery(mMockAppOps, mCallerIdentity))
+ .isTrue();
+ }
+
+ @Test
+ public void test_checkNoteOpPermission_denied() {
+ when(mMockAppOps.noteOp(DiscoveryPermissions.OPSTR_BLUETOOTH_SCAN, UID, PACKAGE_NAME,
+ null, null)).thenReturn(AppOpsManager.MODE_ERRORED);
+
+ assertThat(DiscoveryPermissions
+ .noteDiscoveryResultDelivery(mMockAppOps, mCallerIdentity))
+ .isFalse();
+ }
+
+ @Test
+ public void test_getPermissionLevel_none() {
+ when(mMockContext.checkPermission(BLUETOOTH_SCAN, PID, UID)).thenReturn(PERMISSION_DENIED);
+
+ assertThat(DiscoveryPermissions
+ .getPermissionLevel(mMockContext, UID, PID))
+ .isEqualTo(PERMISSION_NONE);
+ }
+
+ @Test
+ public void test_getPermissionLevel_scan() {
+ when(mMockContext.checkPermission(BLUETOOTH_SCAN, PID, UID))
+ .thenReturn(PERMISSION_GRANTED);
+
+ assertThat(DiscoveryPermissions
+ .getPermissionLevel(mMockContext, UID, PID)).isEqualTo(PERMISSION_BLUETOOTH_SCAN);
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index eb7d1ea..e4a9ebe 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -916,7 +916,7 @@
final Intent intent = new Intent();
if (type == TYPE_COMPONENT_ACTIVTIY) {
intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
} else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
.setFlags(1);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
new file mode 100644
index 0000000..098f295
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.cts.net.hostside;
+
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getUiDevice;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private static final int TEST_ITERATION_COUNT = 5;
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+ resetDeviceState();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+ resetDeviceState();
+ }
+
+ private void resetDeviceState() throws Exception {
+ resetBatteryState();
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+
+
+ @Test
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ public void testStartActivity_batterySaver() throws Exception {
+ setBatterySaverMode(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver");
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+ public void testStartActivity_dataSaver() throws Exception {
+ setRestrictBackground(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver");
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE})
+ public void testStartActivity_doze() throws Exception {
+ setDozeMode(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_doze");
+ }
+
+ @Test
+ @RequiredProperties({APP_STANDBY_MODE})
+ public void testStartActivity_appStandby() throws Exception {
+ turnBatteryOn();
+ setAppIdle(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby");
+ }
+
+ private void assertLaunchedActivityHasNetworkAccess(String testName) throws Exception {
+ for (int i = 0; i < TEST_ITERATION_COUNT; ++i) {
+ Log.i(TAG, testName + " start #" + i);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ getUiDevice().pressHome();
+ assertBackgroundState();
+ Log.i(TAG, testName + " end #" + i);
+ }
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index 0a0f24b..7842eec 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -57,6 +57,7 @@
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import com.android.compatibility.common.util.AppStandbyUtils;
import com.android.compatibility.common.util.BatteryUtils;
@@ -438,6 +439,10 @@
return InstrumentationRegistry.getInstrumentation();
}
+ public static UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+
// When power saver mode or restrict background enabled or adding any white/black list into
// those modes, NetworkPolicy may need to take some time to update the rules of uids. So having
// this function and using PollingCheck to try to make sure the uid has updated and reduce the
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
index eb7dca7..a337fe2 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -39,6 +39,33 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MyActivity.onCreate()");
+ }
+
+ @Override
+ public void finish() {
+ if (finishCommandReceiver != null) {
+ unregisterReceiver(finishCommandReceiver);
+ }
+ super.finish();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "MyActivity.onStart()");
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Log.d(TAG, "MyActivity.onNewIntent()");
+ setIntent(intent);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "MyActivity.onResume(): " + getIntent());
Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
finishCommandReceiver = new BroadcastReceiver() {
@Override
@@ -57,20 +84,6 @@
}
@Override
- public void finish() {
- if (finishCommandReceiver != null) {
- unregisterReceiver(finishCommandReceiver);
- }
- super.finish();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- Log.d(TAG, "MyActivity.onStart()");
- }
-
- @Override
protected void onDestroy() {
Log.d(TAG, "MyActivity.onDestroy()");
super.onDestroy();
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
new file mode 100644
index 0000000..3387fd7
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cts.net;
+
+public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase {
+ private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ public void testStartActivity_batterySaver() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
+ }
+
+ public void testStartActivity_dataSaver() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
+ }
+
+ public void testStartActivity_doze() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
+ }
+
+ public void testStartActivity_appStandby() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index c6fc38f..0c53411 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -49,6 +49,7 @@
import android.os.Handler;
import android.os.Looper;
import android.platform.test.annotations.AppModeFull;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.util.Log;
@@ -727,6 +728,18 @@
@Test
public void testPrivateDnsBypass() throws InterruptedException {
+ final String dataStallSetting = Settings.Global.getString(mCR,
+ Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK);
+ Settings.Global.putInt(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0);
+ try {
+ doTestPrivateDnsBypass();
+ } finally {
+ Settings.Global.putString(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK,
+ dataStallSetting);
+ }
+ }
+
+ private void doTestPrivateDnsBypass() throws InterruptedException {
final Network[] testNetworks = getTestableNetworks();
// Set an invalid private DNS server