Add callbacks for service offload
Components that can provide offload like IpClient (packet filter offloading) can use the API to register a callback to be notified when offload is necessary. Bug: 269240366 Test: atest CtsNetTestCases Change-Id: I8080702f5b530001b88e79e504f4722ac01bc576
This commit is contained in:
@@ -82,6 +82,17 @@ filegroup {
|
|||||||
visibility: ["//packages/modules/Connectivity:__subpackages__"],
|
visibility: ["//packages/modules/Connectivity:__subpackages__"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The filegroup lists files that are necessary for verifying building mdns as a standalone,
|
||||||
|
// for use with service-connectivity-mdns-standalone-build-test
|
||||||
|
filegroup {
|
||||||
|
name: "framework-connectivity-t-mdns-standalone-build-sources",
|
||||||
|
srcs: [
|
||||||
|
"src/android/net/nsd/OffloadEngine.java",
|
||||||
|
"src/android/net/nsd/OffloadServiceInfo.java",
|
||||||
|
],
|
||||||
|
visibility: ["//packages/modules/Connectivity:__subpackages__"],
|
||||||
|
}
|
||||||
|
|
||||||
java_library {
|
java_library {
|
||||||
name: "framework-connectivity-t-pre-jarjar",
|
name: "framework-connectivity-t-pre-jarjar",
|
||||||
defaults: ["framework-connectivity-t-defaults"],
|
defaults: ["framework-connectivity-t-defaults"],
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package android.net.nsd;
|
package android.net.nsd;
|
||||||
|
|
||||||
import android.net.nsd.INsdManagerCallback;
|
import android.net.nsd.INsdManagerCallback;
|
||||||
|
import android.net.nsd.IOffloadEngine;
|
||||||
import android.net.nsd.NsdServiceInfo;
|
import android.net.nsd.NsdServiceInfo;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
|
|
||||||
@@ -35,4 +36,6 @@ interface INsdServiceConnector {
|
|||||||
void stopResolution(int listenerKey);
|
void stopResolution(int listenerKey);
|
||||||
void registerServiceInfoCallback(int listenerKey, in NsdServiceInfo serviceInfo);
|
void registerServiceInfoCallback(int listenerKey, in NsdServiceInfo serviceInfo);
|
||||||
void unregisterServiceInfoCallback(int listenerKey);
|
void unregisterServiceInfoCallback(int listenerKey);
|
||||||
|
void registerOffloadEngine(String ifaceName, in IOffloadEngine cb, long offloadCapabilities, long offloadType);
|
||||||
|
void unregisterOffloadEngine(in IOffloadEngine cb);
|
||||||
}
|
}
|
||||||
28
framework-t/src/android/net/nsd/IOffloadEngine.aidl
Normal file
28
framework-t/src/android/net/nsd/IOffloadEngine.aidl
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2023, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.nsd;
|
||||||
|
|
||||||
|
import android.net.nsd.OffloadServiceInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callbacks from NsdService to inform providers of packet offload.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
oneway interface IOffloadEngine {
|
||||||
|
void onOffloadServiceUpdated(in OffloadServiceInfo info);
|
||||||
|
void onOffloadServiceRemoved(in OffloadServiceInfo info);
|
||||||
|
}
|
||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package android.net.nsd;
|
package android.net.nsd;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||||
|
import static android.Manifest.permission.NETWORK_STACK;
|
||||||
|
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
|
||||||
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
|
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
|
||||||
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
|
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
|
||||||
|
|
||||||
@@ -45,9 +48,11 @@ import android.util.SparseArray;
|
|||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.net.module.util.CollectionUtils;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -246,6 +251,10 @@ public final class NsdManager {
|
|||||||
public static final int UNREGISTER_SERVICE_CALLBACK = 31;
|
public static final int UNREGISTER_SERVICE_CALLBACK = 31;
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32;
|
public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32;
|
||||||
|
/** @hide */
|
||||||
|
public static final int REGISTER_OFFLOAD_ENGINE = 33;
|
||||||
|
/** @hide */
|
||||||
|
public static final int UNREGISTER_OFFLOAD_ENGINE = 34;
|
||||||
|
|
||||||
/** Dns based service discovery protocol */
|
/** Dns based service discovery protocol */
|
||||||
public static final int PROTOCOL_DNS_SD = 0x0001;
|
public static final int PROTOCOL_DNS_SD = 0x0001;
|
||||||
@@ -313,8 +322,107 @@ public final class NsdManager {
|
|||||||
private final ArrayMap<Integer, PerNetworkDiscoveryTracker>
|
private final ArrayMap<Integer, PerNetworkDiscoveryTracker>
|
||||||
mPerNetworkDiscoveryMap = new ArrayMap<>();
|
mPerNetworkDiscoveryMap = new ArrayMap<>();
|
||||||
|
|
||||||
|
@GuardedBy("mOffloadEngines")
|
||||||
|
private final ArrayList<OffloadEngineProxy> mOffloadEngines = new ArrayList<>();
|
||||||
private final ServiceHandler mHandler;
|
private final ServiceHandler mHandler;
|
||||||
|
|
||||||
|
private static class OffloadEngineProxy extends IOffloadEngine.Stub {
|
||||||
|
private final Executor mExecutor;
|
||||||
|
private final OffloadEngine mEngine;
|
||||||
|
|
||||||
|
private OffloadEngineProxy(@NonNull Executor executor, @NonNull OffloadEngine appCb) {
|
||||||
|
mExecutor = executor;
|
||||||
|
mEngine = appCb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffloadServiceUpdated(OffloadServiceInfo info) {
|
||||||
|
mExecutor.execute(() -> mEngine.onOffloadServiceUpdated(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffloadServiceRemoved(OffloadServiceInfo info) {
|
||||||
|
mExecutor.execute(() -> mEngine.onOffloadServiceRemoved(info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an OffloadEngine with NsdManager.
|
||||||
|
*
|
||||||
|
* A caller can register itself as an OffloadEngine if it supports mDns hardware offload.
|
||||||
|
* The caller must implement the {@link OffloadEngine} interface and update hardware offload
|
||||||
|
* state property when the {@link OffloadEngine#onOffloadServiceUpdated} and
|
||||||
|
* {@link OffloadEngine#onOffloadServiceRemoved} callback are called. Multiple engines may be
|
||||||
|
* registered for the same interface, and that the same engine cannot be registered twice.
|
||||||
|
*
|
||||||
|
* @param ifaceName indicates which network interface the hardware offload runs on
|
||||||
|
* @param offloadType the type of offload that the offload engine support
|
||||||
|
* @param offloadCapability the capabilities of the offload engine
|
||||||
|
* @param executor the executor on which to receive the offload callbacks
|
||||||
|
* @param engine the OffloadEngine that will receive the offload callbacks
|
||||||
|
* @throws IllegalStateException if the engine is already registered.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
//@SystemApi
|
||||||
|
@RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK,
|
||||||
|
NETWORK_STACK})
|
||||||
|
public void registerOffloadEngine(@NonNull String ifaceName,
|
||||||
|
@OffloadEngine.OffloadType long offloadType,
|
||||||
|
@OffloadEngine.OffloadCapability long offloadCapability, @NonNull Executor executor,
|
||||||
|
@NonNull OffloadEngine engine) {
|
||||||
|
Objects.requireNonNull(ifaceName);
|
||||||
|
Objects.requireNonNull(executor);
|
||||||
|
Objects.requireNonNull(engine);
|
||||||
|
final OffloadEngineProxy cbImpl = new OffloadEngineProxy(executor, engine);
|
||||||
|
synchronized (mOffloadEngines) {
|
||||||
|
if (CollectionUtils.contains(mOffloadEngines, impl -> impl.mEngine == engine)) {
|
||||||
|
throw new IllegalStateException("This engine is already registered");
|
||||||
|
}
|
||||||
|
mOffloadEngines.add(cbImpl);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mService.registerOffloadEngine(ifaceName, cbImpl, offloadCapability, offloadType);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters an OffloadEngine from NsdService.
|
||||||
|
*
|
||||||
|
* A caller can unregister itself as an OffloadEngine when it doesn't want to receive the
|
||||||
|
* callback anymore. The OffloadEngine must have been previously registered with the system
|
||||||
|
* using the {@link NsdManager#registerOffloadEngine} method.
|
||||||
|
*
|
||||||
|
* @param engine OffloadEngine object to be removed from NsdService
|
||||||
|
* @throws IllegalStateException if the engine is not registered.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
//@SystemApi
|
||||||
|
@RequiresPermission(anyOf = {NETWORK_SETTINGS, PERMISSION_MAINLINE_NETWORK_STACK,
|
||||||
|
NETWORK_STACK})
|
||||||
|
public void unregisterOffloadEngine(@NonNull OffloadEngine engine) {
|
||||||
|
Objects.requireNonNull(engine);
|
||||||
|
final OffloadEngineProxy cbImpl;
|
||||||
|
synchronized (mOffloadEngines) {
|
||||||
|
final int index = CollectionUtils.indexOf(mOffloadEngines,
|
||||||
|
impl -> impl.mEngine == engine);
|
||||||
|
if (index < 0) {
|
||||||
|
throw new IllegalStateException("This engine is not registered");
|
||||||
|
}
|
||||||
|
cbImpl = mOffloadEngines.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mService.unregisterOffloadEngine(cbImpl);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PerNetworkDiscoveryTracker {
|
private class PerNetworkDiscoveryTracker {
|
||||||
final String mServiceType;
|
final String mServiceType;
|
||||||
final int mProtocolType;
|
final int mProtocolType;
|
||||||
|
|||||||
85
framework-t/src/android/net/nsd/OffloadEngine.java
Normal file
85
framework-t/src/android/net/nsd/OffloadEngine.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.nsd;
|
||||||
|
|
||||||
|
import android.annotation.LongDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OffloadEngine is an interface for mDns hardware offloading.
|
||||||
|
*
|
||||||
|
* An offloading engine can interact with the firmware code to instruct the hardware to
|
||||||
|
* offload some of mDns network traffic before it reached android OS. This can improve the
|
||||||
|
* power consumption performance of the host system by not always waking up the OS to handle
|
||||||
|
* the mDns packet when the device is in low power mode.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
//@SystemApi
|
||||||
|
public interface OffloadEngine {
|
||||||
|
/**
|
||||||
|
* Indicates that the OffloadEngine can generate replies to mDns queries.
|
||||||
|
*
|
||||||
|
* @see OffloadServiceInfo#getOffloadPayload()
|
||||||
|
*/
|
||||||
|
int OFFLOAD_TYPE_REPLY = 1;
|
||||||
|
/**
|
||||||
|
* Indicates that the OffloadEngine can filter and drop mDns queries.
|
||||||
|
*/
|
||||||
|
int OFFLOAD_TYPE_FILTER_QUERIES = 1 << 1;
|
||||||
|
/**
|
||||||
|
* Indicates that the OffloadEngine can filter and drop mDns replies. It can allow mDns packets
|
||||||
|
* to be received even when no app holds a {@link android.net.wifi.WifiManager.MulticastLock}.
|
||||||
|
*/
|
||||||
|
int OFFLOAD_TYPE_FILTER_REPLIES = 1 << 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the OffloadEngine can bypass multicast lock.
|
||||||
|
*/
|
||||||
|
int OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK = 1;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@LongDef(flag = true, prefix = {"OFFLOAD_TYPE"}, value = {
|
||||||
|
OFFLOAD_TYPE_REPLY,
|
||||||
|
OFFLOAD_TYPE_FILTER_QUERIES,
|
||||||
|
OFFLOAD_TYPE_FILTER_REPLIES,
|
||||||
|
})
|
||||||
|
@interface OffloadType {}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@LongDef(flag = true, prefix = {"OFFLOAD_CAPABILITY"}, value = {
|
||||||
|
OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK
|
||||||
|
})
|
||||||
|
@interface OffloadCapability {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be called when the OffloadServiceInfo is added or updated.
|
||||||
|
*
|
||||||
|
* @param info The OffloadServiceInfo to add or update.
|
||||||
|
*/
|
||||||
|
void onOffloadServiceUpdated(@NonNull OffloadServiceInfo info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be called when the OffloadServiceInfo is removed.
|
||||||
|
*
|
||||||
|
* @param info The OffloadServiceInfo to remove.
|
||||||
|
*/
|
||||||
|
void onOffloadServiceRemoved(@NonNull OffloadServiceInfo info);
|
||||||
|
}
|
||||||
313
framework-t/src/android/net/nsd/OffloadServiceInfo.java
Normal file
313
framework-t/src/android/net/nsd/OffloadServiceInfo.java
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.nsd;
|
||||||
|
|
||||||
|
import android.annotation.IntRange;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import com.android.net.module.util.HexDump;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OffloadServiceInfo class contains all the necessary information the OffloadEngine needs to
|
||||||
|
* know about how to offload an mDns service. The OffloadServiceInfo is keyed on
|
||||||
|
* {@link OffloadServiceInfo.Key} which is a (serviceName, serviceType) pair.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
// @SystemApi
|
||||||
|
public final class OffloadServiceInfo implements Parcelable {
|
||||||
|
@NonNull
|
||||||
|
private final Key mKey;
|
||||||
|
@NonNull
|
||||||
|
private final String mHostname;
|
||||||
|
@NonNull final List<String> mSubtypes;
|
||||||
|
@Nullable
|
||||||
|
private final byte[] mOffloadPayload;
|
||||||
|
private final int mPriority;
|
||||||
|
private final long mOffloadType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new OffloadServiceInfo object with the specified parameters.
|
||||||
|
*
|
||||||
|
* @param key The key of the service.
|
||||||
|
* @param subtypes The list of subTypes of the service.
|
||||||
|
* @param hostname The name of the host offering the service. It is meaningful only when
|
||||||
|
* offloadType contains OFFLOAD_REPLY.
|
||||||
|
* @param offloadPayload The raw udp payload for hardware offloading.
|
||||||
|
* @param priority The priority of the service, @see #getPriority.
|
||||||
|
* @param offloadType The type of the service offload, @see #getOffloadType.
|
||||||
|
*/
|
||||||
|
public OffloadServiceInfo(@NonNull Key key,
|
||||||
|
@NonNull List<String> subtypes, @NonNull String hostname,
|
||||||
|
@Nullable byte[] offloadPayload,
|
||||||
|
@IntRange(from = 0, to = Integer.MAX_VALUE) int priority,
|
||||||
|
@OffloadEngine.OffloadType long offloadType) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
Objects.requireNonNull(subtypes);
|
||||||
|
Objects.requireNonNull(hostname);
|
||||||
|
mKey = key;
|
||||||
|
mSubtypes = subtypes;
|
||||||
|
mHostname = hostname;
|
||||||
|
mOffloadPayload = offloadPayload;
|
||||||
|
mPriority = priority;
|
||||||
|
mOffloadType = offloadType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new OffloadServiceInfo object from a Parcel.
|
||||||
|
*
|
||||||
|
* @param in The Parcel to read the object from.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public OffloadServiceInfo(@NonNull Parcel in) {
|
||||||
|
mKey = in.readParcelable(Key.class.getClassLoader(),
|
||||||
|
Key.class);
|
||||||
|
mSubtypes = in.createStringArrayList();
|
||||||
|
mHostname = in.readString();
|
||||||
|
mOffloadPayload = in.createByteArray();
|
||||||
|
mPriority = in.readInt();
|
||||||
|
mOffloadType = in.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||||
|
dest.writeParcelable(mKey, flags);
|
||||||
|
dest.writeStringList(mSubtypes);
|
||||||
|
dest.writeString(mHostname);
|
||||||
|
dest.writeByteArray(mOffloadPayload);
|
||||||
|
dest.writeInt(mPriority);
|
||||||
|
dest.writeLong(mOffloadType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static final Creator<OffloadServiceInfo> CREATOR = new Creator<OffloadServiceInfo>() {
|
||||||
|
@Override
|
||||||
|
public OffloadServiceInfo createFromParcel(Parcel in) {
|
||||||
|
return new OffloadServiceInfo(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffloadServiceInfo[] newArray(int size) {
|
||||||
|
return new OffloadServiceInfo[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Key}.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Key getKey() {
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the host name. (e.g. "Android.local" )
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public String getHostname() {
|
||||||
|
return mHostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the service subtypes. (e.g. ["_ann"] )
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public List<String> getSubtypes() {
|
||||||
|
return Collections.unmodifiableList(mSubtypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raw udp payload that the OffloadEngine can use to directly reply the incoming query.
|
||||||
|
* <p>
|
||||||
|
* It is null if the OffloadEngine can not handle transmit. The packet must be sent as-is when
|
||||||
|
* replying to query.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public byte[] getOffloadPayload() {
|
||||||
|
if (mOffloadPayload == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return mOffloadPayload.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the offloadType.
|
||||||
|
* <p>
|
||||||
|
* For example, if the {@link com.android.server.NsdService} requests the OffloadEngine to both
|
||||||
|
* filter the mDNS queries and replies, the {@link #mOffloadType} =
|
||||||
|
* ({@link OffloadEngine#OFFLOAD_TYPE_FILTER_QUERIES} |
|
||||||
|
* {@link OffloadEngine#OFFLOAD_TYPE_FILTER_REPLIES}).
|
||||||
|
*/
|
||||||
|
@OffloadEngine.OffloadType public long getOffloadType() {
|
||||||
|
return mOffloadType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the priority for the OffloadServiceInfo.
|
||||||
|
* <p>
|
||||||
|
* When OffloadEngine don't have enough resource
|
||||||
|
* (e.g. not enough memory) to offload all the OffloadServiceInfo. The OffloadServiceInfo
|
||||||
|
* having lower priority values should be handled by the OffloadEngine first.
|
||||||
|
*/
|
||||||
|
public int getPriority() {
|
||||||
|
return mPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for debug purpose, the string can be long as the raw packet is dump in the string.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"OffloadServiceInfo{ mOffloadServiceInfoKey=%s, mHostName=%s, "
|
||||||
|
+ "mOffloadPayload=%s, mPriority=%d, mOffloadType=%d, mSubTypes=%s }",
|
||||||
|
mKey,
|
||||||
|
mHostname, HexDump.dumpHexString(mOffloadPayload), mPriority,
|
||||||
|
mOffloadType, mSubtypes.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof OffloadServiceInfo)) return false;
|
||||||
|
OffloadServiceInfo that = (OffloadServiceInfo) o;
|
||||||
|
return mPriority == that.mPriority && mOffloadType == that.mOffloadType
|
||||||
|
&& mKey.equals(that.mKey)
|
||||||
|
&& mHostname.equals(
|
||||||
|
that.mHostname) && Arrays.equals(mOffloadPayload,
|
||||||
|
that.mOffloadPayload)
|
||||||
|
&& mSubtypes.equals(that.mSubtypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = Objects.hash(mKey, mHostname, mPriority,
|
||||||
|
mOffloadType, mSubtypes);
|
||||||
|
result = 31 * result + Arrays.hashCode(mOffloadPayload);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OffloadServiceInfo.Key} is the (serviceName, serviceType) pair.
|
||||||
|
*/
|
||||||
|
public static final class Key implements Parcelable {
|
||||||
|
@NonNull
|
||||||
|
private final String mServiceName;
|
||||||
|
@NonNull
|
||||||
|
private final String mServiceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new OffloadServiceInfoKey object with the specified parameters.
|
||||||
|
*
|
||||||
|
* @param serviceName The name of the service.
|
||||||
|
* @param serviceType The type of the service.
|
||||||
|
*/
|
||||||
|
public Key(@NonNull String serviceName, @NonNull String serviceType) {
|
||||||
|
Objects.requireNonNull(serviceName);
|
||||||
|
Objects.requireNonNull(serviceType);
|
||||||
|
mServiceName = serviceName;
|
||||||
|
mServiceType = serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new OffloadServiceInfoKey object from a Parcel.
|
||||||
|
*
|
||||||
|
* @param in The Parcel to read the object from.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public Key(@NonNull Parcel in) {
|
||||||
|
mServiceName = in.readString();
|
||||||
|
mServiceType = in.readString();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the service name. (e.g. "NsdChat")
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public String getServiceName() {
|
||||||
|
return mServiceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the service type. (e.g. "_http._tcp.local" )
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public String getServiceType() {
|
||||||
|
return mServiceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||||
|
dest.writeString(mServiceName);
|
||||||
|
dest.writeString(mServiceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static final Creator<Key> CREATOR =
|
||||||
|
new Creator<Key>() {
|
||||||
|
@Override
|
||||||
|
public Key createFromParcel(Parcel in) {
|
||||||
|
return new Key(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Key[] newArray(int size) {
|
||||||
|
return new Key[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof Key)) return false;
|
||||||
|
Key that = (Key) o;
|
||||||
|
return Objects.equals(mServiceName, that.mServiceName) && Objects.equals(
|
||||||
|
mServiceType, that.mServiceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(mServiceName, mServiceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("OffloadServiceInfoKey{ mServiceName=%s, mServiceType=%s }",
|
||||||
|
mServiceName, mServiceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.nsd;
|
||||||
|
|
||||||
|
@JavaOnlyStableParcelable parcelable OffloadServiceInfo;
|
||||||
@@ -27,4 +27,10 @@ android\.nearby\..+
|
|||||||
# Don't touch anything that's already under android.net.http (cronet)
|
# Don't touch anything that's already under android.net.http (cronet)
|
||||||
# This is required since android.net.http contains api classes and hidden classes.
|
# This is required since android.net.http contains api classes and hidden classes.
|
||||||
# TODO: Remove this after hidden classes are moved to different package
|
# TODO: Remove this after hidden classes are moved to different package
|
||||||
android\.net\.http\..+
|
android\.net\.http\..+
|
||||||
|
|
||||||
|
# TODO: OffloadServiceInfo is being added as an API, but wasn't an API yet in the first module
|
||||||
|
# versions targeting U. Do not jarjar it such versions so that tests do not have to cover both
|
||||||
|
# cases. This will be removed in an upcoming change marking it as API.
|
||||||
|
android\.net\.nsd\.OffloadServiceInfo(\$.+)?
|
||||||
|
android\.net\.nsd\.OffloadEngine(\$.+)?
|
||||||
|
|||||||
@@ -88,8 +88,9 @@ java_library {
|
|||||||
name: "service-connectivity-mdns-standalone-build-test",
|
name: "service-connectivity-mdns-standalone-build-test",
|
||||||
sdk_version: "core_platform",
|
sdk_version: "core_platform",
|
||||||
srcs: [
|
srcs: [
|
||||||
":service-mdns-droidstubs",
|
|
||||||
"src/com/android/server/connectivity/mdns/**/*.java",
|
"src/com/android/server/connectivity/mdns/**/*.java",
|
||||||
|
":framework-connectivity-t-mdns-standalone-build-sources",
|
||||||
|
":service-mdns-droidstubs"
|
||||||
],
|
],
|
||||||
exclude_srcs: [
|
exclude_srcs: [
|
||||||
"src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java",
|
"src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||||
import static android.net.ConnectivityManager.NETID_UNSET;
|
import static android.net.ConnectivityManager.NETID_UNSET;
|
||||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||||
@@ -46,9 +47,12 @@ import android.net.mdns.aidl.ResolutionInfo;
|
|||||||
import android.net.nsd.INsdManager;
|
import android.net.nsd.INsdManager;
|
||||||
import android.net.nsd.INsdManagerCallback;
|
import android.net.nsd.INsdManagerCallback;
|
||||||
import android.net.nsd.INsdServiceConnector;
|
import android.net.nsd.INsdServiceConnector;
|
||||||
|
import android.net.nsd.IOffloadEngine;
|
||||||
import android.net.nsd.MDnsManager;
|
import android.net.nsd.MDnsManager;
|
||||||
import android.net.nsd.NsdManager;
|
import android.net.nsd.NsdManager;
|
||||||
import android.net.nsd.NsdServiceInfo;
|
import android.net.nsd.NsdServiceInfo;
|
||||||
|
import android.net.nsd.OffloadEngine;
|
||||||
|
import android.net.nsd.OffloadServiceInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -56,6 +60,7 @@ import android.os.HandlerThread;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
@@ -98,6 +103,7 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -215,6 +221,24 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
// The number of client that ever connected.
|
// The number of client that ever connected.
|
||||||
private int mClientNumberId = 1;
|
private int mClientNumberId = 1;
|
||||||
|
|
||||||
|
private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
|
||||||
|
new RemoteCallbackList<>();
|
||||||
|
|
||||||
|
private static class OffloadEngineInfo {
|
||||||
|
@NonNull final String mInterfaceName;
|
||||||
|
final long mOffloadCapabilities;
|
||||||
|
final long mOffloadType;
|
||||||
|
@NonNull final IOffloadEngine mOffloadEngine;
|
||||||
|
|
||||||
|
OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
|
||||||
|
@NonNull String interfaceName, long capabilities, long offloadType) {
|
||||||
|
this.mOffloadEngine = offloadEngine;
|
||||||
|
this.mInterfaceName = interfaceName;
|
||||||
|
this.mOffloadCapabilities = capabilities;
|
||||||
|
this.mOffloadType = offloadType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class MdnsListener implements MdnsServiceBrowserListener {
|
private static class MdnsListener implements MdnsServiceBrowserListener {
|
||||||
protected final int mClientRequestId;
|
protected final int mClientRequestId;
|
||||||
protected final int mTransactionId;
|
protected final int mTransactionId;
|
||||||
@@ -719,6 +743,7 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
final int transactionId;
|
final int transactionId;
|
||||||
final int clientRequestId = msg.arg2;
|
final int clientRequestId = msg.arg2;
|
||||||
final ListenerArgs args;
|
final ListenerArgs args;
|
||||||
|
final OffloadEngineInfo offloadEngineInfo;
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case NsdManager.DISCOVER_SERVICES: {
|
case NsdManager.DISCOVER_SERVICES: {
|
||||||
if (DBG) Log.d(TAG, "Discover services");
|
if (DBG) Log.d(TAG, "Discover services");
|
||||||
@@ -1114,6 +1139,16 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NsdManager.REGISTER_OFFLOAD_ENGINE:
|
||||||
|
offloadEngineInfo = (OffloadEngineInfo) msg.obj;
|
||||||
|
// TODO: Limits the number of registrations created by a given class.
|
||||||
|
mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
|
||||||
|
offloadEngineInfo);
|
||||||
|
// TODO: Sends all the existing OffloadServiceInfos back.
|
||||||
|
break;
|
||||||
|
case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
|
||||||
|
mOffloadEngines.unregister((IOffloadEngine) msg.obj);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
}
|
}
|
||||||
@@ -1771,7 +1806,42 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
|
||||||
|
@NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
|
||||||
|
final int count = mOffloadEngines.beginBroadcast();
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final OffloadEngineInfo offloadEngineInfo =
|
||||||
|
(OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
|
||||||
|
final String interfaceName = offloadEngineInfo.mInterfaceName;
|
||||||
|
if (!targetInterfaceName.equals(interfaceName)
|
||||||
|
|| ((offloadEngineInfo.mOffloadType
|
||||||
|
& offloadServiceInfo.getOffloadType()) == 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (isRemove) {
|
||||||
|
mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
|
||||||
|
offloadServiceInfo);
|
||||||
|
} else {
|
||||||
|
mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
|
||||||
|
offloadServiceInfo);
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Can happen in regular cases, do not log a stacktrace
|
||||||
|
Log.i(TAG, "Failed to send offload callback, remote died", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
mOffloadEngines.finishBroadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
|
private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
|
||||||
|
// TODO: add a callback to notify when a service is being added on each interface (as soon
|
||||||
|
// as probing starts), and call mOffloadCallbacks. This callback is for
|
||||||
|
// OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
|
public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
|
||||||
mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
|
mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
|
||||||
@@ -1801,6 +1871,18 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
request.calculateRequestDurationMs());
|
request.calculateRequestDurationMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffloadStartOrUpdate(@NonNull String interfaceName,
|
||||||
|
@NonNull OffloadServiceInfo offloadServiceInfo) {
|
||||||
|
sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffloadStop(@NonNull String interfaceName,
|
||||||
|
@NonNull OffloadServiceInfo offloadServiceInfo) {
|
||||||
|
sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
|
||||||
|
}
|
||||||
|
|
||||||
private ClientInfo getClientInfoOrLog(int transactionId) {
|
private ClientInfo getClientInfoOrLog(int transactionId) {
|
||||||
final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
|
final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
|
||||||
if (clientInfo == null) {
|
if (clientInfo == null) {
|
||||||
@@ -1920,6 +2002,32 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
mNsdStateMachine.sendMessage(
|
mNsdStateMachine.sendMessage(
|
||||||
mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
|
mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
|
||||||
|
@OffloadEngine.OffloadCapability long offloadCapabilities,
|
||||||
|
@OffloadEngine.OffloadType long offloadTypes) {
|
||||||
|
// TODO: Relax the permission because NETWORK_SETTINGS is a signature permission, and
|
||||||
|
// it may not be possible for all the callers of this API to have it.
|
||||||
|
PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
|
||||||
|
Objects.requireNonNull(ifaceName);
|
||||||
|
Objects.requireNonNull(cb);
|
||||||
|
mNsdStateMachine.sendMessage(
|
||||||
|
mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
|
||||||
|
new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
|
||||||
|
offloadTypes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterOffloadEngine(IOffloadEngine cb) {
|
||||||
|
// TODO: Relax the permission because NETWORK_SETTINGS is a signature permission, and
|
||||||
|
// it may not be possible for all the callers of this API to have it.
|
||||||
|
PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
|
||||||
|
Objects.requireNonNull(cb);
|
||||||
|
mNsdStateMachine.sendMessage(
|
||||||
|
mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2003,25 +2111,41 @@ public class NsdService extends INsdManager.Stub {
|
|||||||
return IFACE_IDX_ANY;
|
return IFACE_IDX_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
|
String interfaceName = getNetworkInterfaceName(network);
|
||||||
if (cm == null) {
|
if (interfaceName == null) {
|
||||||
Log.wtf(TAG, "No ConnectivityManager for resolveService");
|
|
||||||
return IFACE_IDX_ANY;
|
return IFACE_IDX_ANY;
|
||||||
}
|
}
|
||||||
final LinkProperties lp = cm.getLinkProperties(network);
|
return getNetworkInterfaceIndexByName(interfaceName);
|
||||||
if (lp == null) return IFACE_IDX_ANY;
|
}
|
||||||
|
|
||||||
|
private String getNetworkInterfaceName(@Nullable Network network) {
|
||||||
|
if (network == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
|
||||||
|
if (cm == null) {
|
||||||
|
Log.wtf(TAG, "No ConnectivityManager");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final LinkProperties lp = cm.getLinkProperties(network);
|
||||||
|
if (lp == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// Only resolve on non-stacked interfaces
|
// Only resolve on non-stacked interfaces
|
||||||
|
return lp.getInterfaceName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNetworkInterfaceIndexByName(final String ifaceName) {
|
||||||
final NetworkInterface iface;
|
final NetworkInterface iface;
|
||||||
try {
|
try {
|
||||||
iface = NetworkInterface.getByName(lp.getInterfaceName());
|
iface = NetworkInterface.getByName(ifaceName);
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
Log.e(TAG, "Error querying interface", e);
|
Log.e(TAG, "Error querying interface", e);
|
||||||
return IFACE_IDX_ANY;
|
return IFACE_IDX_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iface == null) {
|
if (iface == null) {
|
||||||
Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
|
Log.e(TAG, "Interface not found: " + ifaceName);
|
||||||
return IFACE_IDX_ANY;
|
return IFACE_IDX_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,19 @@ import android.net.LinkAddress;
|
|||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.nsd.NsdManager;
|
import android.net.nsd.NsdManager;
|
||||||
import android.net.nsd.NsdServiceInfo;
|
import android.net.nsd.NsdServiceInfo;
|
||||||
|
import android.net.nsd.OffloadEngine;
|
||||||
|
import android.net.nsd.OffloadServiceInfo;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.net.module.util.CollectionUtils;
|
||||||
import com.android.net.module.util.SharedLog;
|
import com.android.net.module.util.SharedLog;
|
||||||
import com.android.server.connectivity.mdns.util.MdnsUtils;
|
import com.android.server.connectivity.mdns.util.MdnsUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -68,9 +72,10 @@ public class MdnsAdvertiser {
|
|||||||
new ArrayMap<>();
|
new ArrayMap<>();
|
||||||
private final SparseArray<Registration> mRegistrations = new SparseArray<>();
|
private final SparseArray<Registration> mRegistrations = new SparseArray<>();
|
||||||
private final Dependencies mDeps;
|
private final Dependencies mDeps;
|
||||||
|
|
||||||
private String[] mDeviceHostName;
|
private String[] mDeviceHostName;
|
||||||
@NonNull private final SharedLog mSharedLog;
|
@NonNull private final SharedLog mSharedLog;
|
||||||
|
private final Map<String, List<OffloadServiceInfoWrapper>> mInterfaceOffloadServices =
|
||||||
|
new ArrayMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dependencies for {@link MdnsAdvertiser}, useful for testing.
|
* Dependencies for {@link MdnsAdvertiser}, useful for testing.
|
||||||
@@ -115,18 +120,32 @@ public class MdnsAdvertiser {
|
|||||||
private final MdnsInterfaceAdvertiser.Callback mInterfaceAdvertiserCb =
|
private final MdnsInterfaceAdvertiser.Callback mInterfaceAdvertiserCb =
|
||||||
new MdnsInterfaceAdvertiser.Callback() {
|
new MdnsInterfaceAdvertiser.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onRegisterServiceSucceeded(
|
public void onServiceProbingSucceeded(
|
||||||
@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId) {
|
@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId) {
|
||||||
// Wait for all current interfaces to be done probing before notifying of success.
|
|
||||||
if (any(mAllAdvertisers, (k, a) -> a.isProbing(serviceId))) return;
|
|
||||||
// The service may still be unregistered/renamed if a conflict is found on a later added
|
|
||||||
// interface, or if a conflicting announcement/reply is detected (RFC6762 9.)
|
|
||||||
|
|
||||||
final Registration registration = mRegistrations.get(serviceId);
|
final Registration registration = mRegistrations.get(serviceId);
|
||||||
if (registration == null) {
|
if (registration == null) {
|
||||||
Log.wtf(TAG, "Register succeeded for unknown registration");
|
Log.wtf(TAG, "Register succeeded for unknown registration");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String interfaceName = advertiser.getSocketInterfaceName();
|
||||||
|
final List<OffloadServiceInfoWrapper> existingOffloadServiceInfoWrappers =
|
||||||
|
mInterfaceOffloadServices.computeIfAbsent(
|
||||||
|
interfaceName, k -> new ArrayList<>());
|
||||||
|
// Remove existing offload services from cache for update.
|
||||||
|
existingOffloadServiceInfoWrappers.removeIf(item -> item.mServiceId == serviceId);
|
||||||
|
final OffloadServiceInfoWrapper newOffloadServiceInfoWrapper = createOffloadService(
|
||||||
|
serviceId,
|
||||||
|
registration);
|
||||||
|
existingOffloadServiceInfoWrappers.add(newOffloadServiceInfoWrapper);
|
||||||
|
mCb.onOffloadStartOrUpdate(interfaceName,
|
||||||
|
newOffloadServiceInfoWrapper.mOffloadServiceInfo);
|
||||||
|
|
||||||
|
// Wait for all current interfaces to be done probing before notifying of success.
|
||||||
|
if (any(mAllAdvertisers, (k, a) -> a.isProbing(serviceId))) return;
|
||||||
|
// The service may still be unregistered/renamed if a conflict is found on a later added
|
||||||
|
// interface, or if a conflicting announcement/reply is detected (RFC6762 9.)
|
||||||
|
|
||||||
if (!registration.mNotifiedRegistrationSuccess) {
|
if (!registration.mNotifiedRegistrationSuccess) {
|
||||||
mCb.onRegisterServiceSucceeded(serviceId, registration.getServiceInfo());
|
mCb.onRegisterServiceSucceeded(serviceId, registration.getServiceInfo());
|
||||||
registration.mNotifiedRegistrationSuccess = true;
|
registration.mNotifiedRegistrationSuccess = true;
|
||||||
@@ -148,7 +167,12 @@ public class MdnsAdvertiser {
|
|||||||
registration.mNotifiedRegistrationSuccess = false;
|
registration.mNotifiedRegistrationSuccess = false;
|
||||||
|
|
||||||
// The service was done probing, just reset it to probing state (RFC6762 9.)
|
// The service was done probing, just reset it to probing state (RFC6762 9.)
|
||||||
forAllAdvertisers(a -> a.restartProbingForConflict(serviceId));
|
forAllAdvertisers(a -> {
|
||||||
|
if (!a.maybeRestartProbingForConflict(serviceId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
maybeSendOffloadStop(a.getSocketInterfaceName(), serviceId);
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +220,22 @@ public class MdnsAdvertiser {
|
|||||||
registration.updateForConflict(newInfo, renameCount);
|
registration.updateForConflict(newInfo, renameCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeSendOffloadStop(final String interfaceName, int serviceId) {
|
||||||
|
final List<OffloadServiceInfoWrapper> existingOffloadServiceInfoWrappers =
|
||||||
|
mInterfaceOffloadServices.get(interfaceName);
|
||||||
|
if (existingOffloadServiceInfoWrappers == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Stop the offloaded service by matching the service id
|
||||||
|
int idx = CollectionUtils.indexOf(existingOffloadServiceInfoWrappers,
|
||||||
|
item -> item.mServiceId == serviceId);
|
||||||
|
if (idx >= 0) {
|
||||||
|
mCb.onOffloadStop(interfaceName,
|
||||||
|
existingOffloadServiceInfoWrappers.get(idx).mOffloadServiceInfo);
|
||||||
|
existingOffloadServiceInfoWrappers.remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request for a {@link MdnsInterfaceAdvertiser}.
|
* A request for a {@link MdnsInterfaceAdvertiser}.
|
||||||
*
|
*
|
||||||
@@ -221,7 +261,22 @@ public class MdnsAdvertiser {
|
|||||||
* @return true if this {@link InterfaceAdvertiserRequest} should now be deleted.
|
* @return true if this {@link InterfaceAdvertiserRequest} should now be deleted.
|
||||||
*/
|
*/
|
||||||
boolean onAdvertiserDestroyed(@NonNull MdnsInterfaceSocket socket) {
|
boolean onAdvertiserDestroyed(@NonNull MdnsInterfaceSocket socket) {
|
||||||
mAdvertisers.remove(socket);
|
final MdnsInterfaceAdvertiser removedAdvertiser = mAdvertisers.remove(socket);
|
||||||
|
if (removedAdvertiser != null) {
|
||||||
|
final String interfaceName = removedAdvertiser.getSocketInterfaceName();
|
||||||
|
// If the interface is destroyed, stop all hardware offloading on that interface.
|
||||||
|
final List<OffloadServiceInfoWrapper> offloadServiceInfoWrappers =
|
||||||
|
mInterfaceOffloadServices.remove(
|
||||||
|
interfaceName);
|
||||||
|
if (offloadServiceInfoWrappers != null) {
|
||||||
|
for (OffloadServiceInfoWrapper offloadServiceInfoWrapper :
|
||||||
|
offloadServiceInfoWrappers) {
|
||||||
|
mCb.onOffloadStop(interfaceName,
|
||||||
|
offloadServiceInfoWrapper.mOffloadServiceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mAdvertisers.size() == 0 && mPendingRegistrations.size() == 0) {
|
if (mAdvertisers.size() == 0 && mPendingRegistrations.size() == 0) {
|
||||||
// No advertiser is using sockets from this request anymore (in particular for exit
|
// No advertiser is using sockets from this request anymore (in particular for exit
|
||||||
// announcements), and there is no registration so newer sockets will not be
|
// announcements), and there is no registration so newer sockets will not be
|
||||||
@@ -282,7 +337,10 @@ public class MdnsAdvertiser {
|
|||||||
void removeService(int id) {
|
void removeService(int id) {
|
||||||
mPendingRegistrations.remove(id);
|
mPendingRegistrations.remove(id);
|
||||||
for (int i = 0; i < mAdvertisers.size(); i++) {
|
for (int i = 0; i < mAdvertisers.size(); i++) {
|
||||||
mAdvertisers.valueAt(i).removeService(id);
|
final MdnsInterfaceAdvertiser advertiser = mAdvertisers.valueAt(i);
|
||||||
|
advertiser.removeService(id);
|
||||||
|
|
||||||
|
maybeSendOffloadStop(advertiser.getSocketInterfaceName(), id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +383,16 @@ public class MdnsAdvertiser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class OffloadServiceInfoWrapper {
|
||||||
|
private final @NonNull OffloadServiceInfo mOffloadServiceInfo;
|
||||||
|
private final int mServiceId;
|
||||||
|
|
||||||
|
OffloadServiceInfoWrapper(int serviceId, OffloadServiceInfo offloadServiceInfo) {
|
||||||
|
mOffloadServiceInfo = offloadServiceInfo;
|
||||||
|
mServiceId = serviceId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class Registration {
|
private static class Registration {
|
||||||
@NonNull
|
@NonNull
|
||||||
final String mOriginalName;
|
final String mOriginalName;
|
||||||
@@ -425,6 +493,24 @@ public class MdnsAdvertiser {
|
|||||||
|
|
||||||
// Unregistration is notified immediately as success in NsdService so no callback is needed
|
// Unregistration is notified immediately as success in NsdService so no callback is needed
|
||||||
// here.
|
// here.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a service is ready to be sent for hardware offloading.
|
||||||
|
*
|
||||||
|
* @param interfaceName the interface for sending the update to.
|
||||||
|
* @param offloadServiceInfo the offloading content.
|
||||||
|
*/
|
||||||
|
void onOffloadStartOrUpdate(@NonNull String interfaceName,
|
||||||
|
@NonNull OffloadServiceInfo offloadServiceInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a service is removed or the MdnsInterfaceAdvertiser is destroyed.
|
||||||
|
*
|
||||||
|
* @param interfaceName the interface for sending the update to.
|
||||||
|
* @param offloadServiceInfo the offloading content.
|
||||||
|
*/
|
||||||
|
void onOffloadStop(@NonNull String interfaceName,
|
||||||
|
@NonNull OffloadServiceInfo offloadServiceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
|
public MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
|
||||||
@@ -525,4 +611,28 @@ public class MdnsAdvertiser {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OffloadServiceInfoWrapper createOffloadService(int serviceId,
|
||||||
|
@NonNull Registration registration) {
|
||||||
|
final NsdServiceInfo nsdServiceInfo = registration.getServiceInfo();
|
||||||
|
List<String> subTypes = new ArrayList<>();
|
||||||
|
String subType = registration.getSubtype();
|
||||||
|
if (subType != null) {
|
||||||
|
subTypes.add(subType);
|
||||||
|
}
|
||||||
|
final OffloadServiceInfo offloadServiceInfo = new OffloadServiceInfo(
|
||||||
|
new OffloadServiceInfo.Key(nsdServiceInfo.getServiceName(),
|
||||||
|
nsdServiceInfo.getServiceType()),
|
||||||
|
subTypes,
|
||||||
|
String.join(".", mDeviceHostName),
|
||||||
|
null /* rawOffloadPacket */,
|
||||||
|
// TODO: define overlayable resources in
|
||||||
|
// ServiceConnectivityResources that set the priority based on
|
||||||
|
// service type.
|
||||||
|
0 /* priority */,
|
||||||
|
// TODO: set the offloadType based on the callback timing.
|
||||||
|
OffloadEngine.OFFLOAD_TYPE_REPLY);
|
||||||
|
return new OffloadServiceInfoWrapper(serviceId, offloadServiceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||||||
/**
|
/**
|
||||||
* Called by the advertiser after it successfully registered a service, after probing.
|
* Called by the advertiser after it successfully registered a service, after probing.
|
||||||
*/
|
*/
|
||||||
void onRegisterServiceSucceeded(@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId);
|
void onServiceProbingSucceeded(@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the advertiser when a conflict was found, during or after probing.
|
* Called by the advertiser when a conflict was found, during or after probing.
|
||||||
@@ -101,7 +101,7 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||||||
public void onFinished(MdnsProber.ProbingInfo info) {
|
public void onFinished(MdnsProber.ProbingInfo info) {
|
||||||
final MdnsAnnouncer.AnnouncementInfo announcementInfo;
|
final MdnsAnnouncer.AnnouncementInfo announcementInfo;
|
||||||
mSharedLog.i("Probing finished for service " + info.getServiceId());
|
mSharedLog.i("Probing finished for service " + info.getServiceId());
|
||||||
mCbHandler.post(() -> mCb.onRegisterServiceSucceeded(
|
mCbHandler.post(() -> mCb.onServiceProbingSucceeded(
|
||||||
MdnsInterfaceAdvertiser.this, info.getServiceId()));
|
MdnsInterfaceAdvertiser.this, info.getServiceId()));
|
||||||
try {
|
try {
|
||||||
announcementInfo = mRecordRepository.onProbingSucceeded(info);
|
announcementInfo = mRecordRepository.onProbingSucceeded(info);
|
||||||
@@ -282,11 +282,12 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||||||
/**
|
/**
|
||||||
* Reset a service to the probing state due to a conflict found on the network.
|
* Reset a service to the probing state due to a conflict found on the network.
|
||||||
*/
|
*/
|
||||||
public void restartProbingForConflict(int serviceId) {
|
public boolean maybeRestartProbingForConflict(int serviceId) {
|
||||||
final MdnsProber.ProbingInfo probingInfo = mRecordRepository.setServiceProbing(serviceId);
|
final MdnsProber.ProbingInfo probingInfo = mRecordRepository.setServiceProbing(serviceId);
|
||||||
if (probingInfo == null) return;
|
if (probingInfo == null) return false;
|
||||||
|
|
||||||
mProber.restartForConflict(probingInfo);
|
mProber.restartForConflict(probingInfo);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -346,4 +347,8 @@ public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHand
|
|||||||
if (answers == null) return;
|
if (answers == null) return;
|
||||||
mReplySender.queueReply(answers);
|
mReplySender.queueReply(answers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSocketInterfaceName() {
|
||||||
|
return mSocket.getInterface().getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package android.net.cts
|
package android.net.cts
|
||||||
|
|
||||||
import android.Manifest.permission.MANAGE_TEST_NETWORKS
|
import android.Manifest.permission.MANAGE_TEST_NETWORKS
|
||||||
|
import android.Manifest.permission.NETWORK_SETTINGS
|
||||||
import android.app.compat.CompatChanges
|
import android.app.compat.CompatChanges
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.ConnectivityManager.NetworkCallback
|
import android.net.ConnectivityManager.NetworkCallback
|
||||||
@@ -60,6 +61,8 @@ import android.net.nsd.NsdManager.DiscoveryListener
|
|||||||
import android.net.nsd.NsdManager.RegistrationListener
|
import android.net.nsd.NsdManager.RegistrationListener
|
||||||
import android.net.nsd.NsdManager.ResolveListener
|
import android.net.nsd.NsdManager.ResolveListener
|
||||||
import android.net.nsd.NsdServiceInfo
|
import android.net.nsd.NsdServiceInfo
|
||||||
|
import android.net.nsd.OffloadEngine
|
||||||
|
import android.net.nsd.OffloadServiceInfo
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
@@ -353,6 +356,22 @@ class NsdManagerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestNsdOffloadEngine : OffloadEngine,
|
||||||
|
NsdRecord<TestNsdOffloadEngine.OffloadEvent>() {
|
||||||
|
sealed class OffloadEvent : NsdEvent {
|
||||||
|
data class AddOrUpdateEvent(val info: OffloadServiceInfo) : OffloadEvent()
|
||||||
|
data class RemoveEvent(val info: OffloadServiceInfo) : OffloadEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOffloadServiceUpdated(info: OffloadServiceInfo) {
|
||||||
|
add(OffloadEvent.AddOrUpdateEvent(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOffloadServiceRemoved(info: OffloadServiceInfo) {
|
||||||
|
add(OffloadEvent.RemoveEvent(info))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
handlerThread.start()
|
handlerThread.start()
|
||||||
@@ -858,6 +877,52 @@ class NsdManagerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkOffloadServiceInfo(serviceInfo: OffloadServiceInfo) {
|
||||||
|
assertEquals(serviceName, serviceInfo.key.serviceName)
|
||||||
|
assertEquals(serviceType, serviceInfo.key.serviceType)
|
||||||
|
assertEquals(listOf<String>("_subtype"), serviceInfo.subtypes)
|
||||||
|
assertTrue(serviceInfo.hostname.startsWith("Android_"))
|
||||||
|
assertTrue(serviceInfo.hostname.endsWith("local"))
|
||||||
|
assertEquals(0, serviceInfo.priority)
|
||||||
|
assertEquals(OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(), serviceInfo.offloadType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNsdManager_registerOffloadEngine() {
|
||||||
|
val targetSdkVersion = context.packageManager
|
||||||
|
.getTargetSdkVersion(context.applicationInfo.packageName)
|
||||||
|
// The offload callbacks are only supported with the new backend,
|
||||||
|
// enabled with target SDK U+.
|
||||||
|
assumeTrue(isAtLeastU() || targetSdkVersion > Build.VERSION_CODES.TIRAMISU)
|
||||||
|
val offloadEngine = TestNsdOffloadEngine()
|
||||||
|
runAsShell(NETWORK_SETTINGS) {
|
||||||
|
nsdManager.registerOffloadEngine(testNetwork1.iface.interfaceName,
|
||||||
|
OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(),
|
||||||
|
OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK.toLong(),
|
||||||
|
{ it.run() }, offloadEngine)
|
||||||
|
}
|
||||||
|
|
||||||
|
val si = NsdServiceInfo()
|
||||||
|
si.serviceType = "$serviceType,_subtype"
|
||||||
|
si.serviceName = serviceName
|
||||||
|
si.network = testNetwork1.network
|
||||||
|
si.port = 12345
|
||||||
|
val record = NsdRegistrationRecord()
|
||||||
|
nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, record)
|
||||||
|
val addOrUpdateEvent = offloadEngine
|
||||||
|
.expectCallback<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent>()
|
||||||
|
checkOffloadServiceInfo(addOrUpdateEvent.info)
|
||||||
|
|
||||||
|
nsdManager.unregisterService(record)
|
||||||
|
val unregisterEvent = offloadEngine
|
||||||
|
.expectCallback<TestNsdOffloadEngine.OffloadEvent.RemoveEvent>()
|
||||||
|
checkOffloadServiceInfo(unregisterEvent.info)
|
||||||
|
|
||||||
|
runAsShell(NETWORK_SETTINGS) {
|
||||||
|
nsdManager.unregisterOffloadEngine(offloadEngine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkConnectSocketToMdnsd(shouldFail: Boolean) {
|
private fun checkConnectSocketToMdnsd(shouldFail: Boolean) {
|
||||||
val discoveryRecord = NsdDiscoveryRecord()
|
val discoveryRecord = NsdDiscoveryRecord()
|
||||||
val localSocket = LocalSocket()
|
val localSocket = LocalSocket()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import android.net.InetAddresses.parseNumericAddress
|
|||||||
import android.net.LinkAddress
|
import android.net.LinkAddress
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.net.nsd.NsdServiceInfo
|
import android.net.nsd.NsdServiceInfo
|
||||||
|
import android.net.nsd.OffloadEngine
|
||||||
|
import android.net.nsd.OffloadServiceInfo
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
@@ -60,6 +62,8 @@ private val TEST_SOCKETKEY_1 = SocketKey(1001 /* interfaceIndex */)
|
|||||||
private val TEST_SOCKETKEY_2 = SocketKey(1002 /* interfaceIndex */)
|
private val TEST_SOCKETKEY_2 = SocketKey(1002 /* interfaceIndex */)
|
||||||
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
|
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
|
||||||
private const val TEST_SUBTYPE = "_subtype"
|
private const val TEST_SUBTYPE = "_subtype"
|
||||||
|
private val TEST_INTERFACE1 = "test_iface1"
|
||||||
|
private val TEST_INTERFACE2 = "test_iface2"
|
||||||
|
|
||||||
private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
|
private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
|
||||||
port = 12345
|
port = 12345
|
||||||
@@ -94,6 +98,24 @@ private val LONG_ALL_NETWORKS_SERVICE =
|
|||||||
network = null
|
network = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val OFFLOAD_SERVICEINFO = OffloadServiceInfo(
|
||||||
|
OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"),
|
||||||
|
listOf(TEST_SUBTYPE),
|
||||||
|
"Android_test.local",
|
||||||
|
null, /* rawOffloadPacket */
|
||||||
|
0, /* priority */
|
||||||
|
OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
|
||||||
|
)
|
||||||
|
|
||||||
|
private val OFFLOAD_SERVICEINFO_NO_SUBTYPE = OffloadServiceInfo(
|
||||||
|
OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"),
|
||||||
|
listOf(),
|
||||||
|
"Android_test.local",
|
||||||
|
null, /* rawOffloadPacket */
|
||||||
|
0, /* priority */
|
||||||
|
OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
|
||||||
|
)
|
||||||
|
|
||||||
@RunWith(DevSdkIgnoreRunner::class)
|
@RunWith(DevSdkIgnoreRunner::class)
|
||||||
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
|
||||||
class MdnsAdvertiserTest {
|
class MdnsAdvertiserTest {
|
||||||
@@ -123,6 +145,8 @@ class MdnsAdvertiserTest {
|
|||||||
doReturn(true).`when`(mockInterfaceAdvertiser2).isProbing(anyInt())
|
doReturn(true).`when`(mockInterfaceAdvertiser2).isProbing(anyInt())
|
||||||
doReturn(createEmptyNetworkInterface()).`when`(mockSocket1).getInterface()
|
doReturn(createEmptyNetworkInterface()).`when`(mockSocket1).getInterface()
|
||||||
doReturn(createEmptyNetworkInterface()).`when`(mockSocket2).getInterface()
|
doReturn(createEmptyNetworkInterface()).`when`(mockSocket2).getInterface()
|
||||||
|
doReturn(TEST_INTERFACE1).`when`(mockInterfaceAdvertiser1).socketInterfaceName
|
||||||
|
doReturn(TEST_INTERFACE2).`when`(mockInterfaceAdvertiser2).socketInterfaceName
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -160,12 +184,15 @@ class MdnsAdvertiserTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||||
postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
|
postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
|
||||||
mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
||||||
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
|
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
|
||||||
|
verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE))
|
||||||
|
|
||||||
postSync { socketCb.onInterfaceDestroyed(TEST_SOCKETKEY_1, mockSocket1) }
|
postSync { socketCb.onInterfaceDestroyed(TEST_SOCKETKEY_1, mockSocket1) }
|
||||||
verify(mockInterfaceAdvertiser1).destroyNow()
|
verify(mockInterfaceAdvertiser1).destroyNow()
|
||||||
|
postSync { intAdvCbCaptor.value.onDestroyed(mockSocket1) }
|
||||||
|
verify(cb).onOffloadStop(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -195,14 +222,16 @@ class MdnsAdvertiserTest {
|
|||||||
anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
|
anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
|
||||||
|
|
||||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||||
postSync { intAdvCbCaptor1.value.onRegisterServiceSucceeded(
|
postSync { intAdvCbCaptor1.value.onServiceProbingSucceeded(
|
||||||
mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
||||||
|
verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO))
|
||||||
|
|
||||||
// Need both advertisers to finish probing and call onRegisterServiceSucceeded
|
// Need both advertisers to finish probing and call onRegisterServiceSucceeded
|
||||||
verify(cb, never()).onRegisterServiceSucceeded(anyInt(), any())
|
verify(cb, never()).onRegisterServiceSucceeded(anyInt(), any())
|
||||||
doReturn(false).`when`(mockInterfaceAdvertiser2).isProbing(SERVICE_ID_1)
|
doReturn(false).`when`(mockInterfaceAdvertiser2).isProbing(SERVICE_ID_1)
|
||||||
postSync { intAdvCbCaptor2.value.onRegisterServiceSucceeded(
|
postSync { intAdvCbCaptor2.value.onServiceProbingSucceeded(
|
||||||
mockInterfaceAdvertiser2, SERVICE_ID_1) }
|
mockInterfaceAdvertiser2, SERVICE_ID_1) }
|
||||||
|
verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE2), eq(OFFLOAD_SERVICEINFO))
|
||||||
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
|
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
|
||||||
argThat { it.matches(ALL_NETWORKS_SERVICE) })
|
argThat { it.matches(ALL_NETWORKS_SERVICE) })
|
||||||
|
|
||||||
@@ -210,6 +239,8 @@ class MdnsAdvertiserTest {
|
|||||||
postSync { advertiser.removeService(SERVICE_ID_1) }
|
postSync { advertiser.removeService(SERVICE_ID_1) }
|
||||||
verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1)
|
verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1)
|
||||||
verify(mockInterfaceAdvertiser2).removeService(SERVICE_ID_1)
|
verify(mockInterfaceAdvertiser2).removeService(SERVICE_ID_1)
|
||||||
|
verify(cb).onOffloadStop(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO))
|
||||||
|
verify(cb).onOffloadStop(eq(TEST_INTERFACE2), eq(OFFLOAD_SERVICEINFO))
|
||||||
|
|
||||||
// Interface advertisers call onDestroyed after sending exit announcements
|
// Interface advertisers call onDestroyed after sending exit announcements
|
||||||
postSync { intAdvCbCaptor1.value.onDestroyed(mockSocket1) }
|
postSync { intAdvCbCaptor1.value.onDestroyed(mockSocket1) }
|
||||||
@@ -285,12 +316,12 @@ class MdnsAdvertiserTest {
|
|||||||
argThat { it.matches(expectedCaseInsensitiveRenamed) }, eq(null))
|
argThat { it.matches(expectedCaseInsensitiveRenamed) }, eq(null))
|
||||||
|
|
||||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
|
||||||
postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
|
postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
|
||||||
mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
mockInterfaceAdvertiser1, SERVICE_ID_1) }
|
||||||
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
|
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
|
||||||
|
|
||||||
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_2)
|
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_2)
|
||||||
postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
|
postSync { intAdvCbCaptor.value.onServiceProbingSucceeded(
|
||||||
mockInterfaceAdvertiser1, SERVICE_ID_2) }
|
mockInterfaceAdvertiser1, SERVICE_ID_2) }
|
||||||
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_2),
|
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_2),
|
||||||
argThat { it.matches(expectedRenamed) })
|
argThat { it.matches(expectedRenamed) })
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class MdnsInterfaceAdvertiserTest {
|
|||||||
0L /* initialDelayMs */)
|
0L /* initialDelayMs */)
|
||||||
|
|
||||||
thread.waitForIdle(TIMEOUT_MS)
|
thread.waitForIdle(TIMEOUT_MS)
|
||||||
verify(cb).onRegisterServiceSucceeded(advertiser, TEST_SERVICE_ID_1)
|
verify(cb).onServiceProbingSucceeded(advertiser, TEST_SERVICE_ID_1)
|
||||||
|
|
||||||
// Remove the service: expect exit announcements
|
// Remove the service: expect exit announcements
|
||||||
val testExitInfo = mock(ExitAnnouncementInfo::class.java)
|
val testExitInfo = mock(ExitAnnouncementInfo::class.java)
|
||||||
@@ -256,7 +256,7 @@ class MdnsInterfaceAdvertiserTest {
|
|||||||
val mockProbingInfo = mock(ProbingInfo::class.java)
|
val mockProbingInfo = mock(ProbingInfo::class.java)
|
||||||
doReturn(mockProbingInfo).`when`(repository).setServiceProbing(TEST_SERVICE_ID_1)
|
doReturn(mockProbingInfo).`when`(repository).setServiceProbing(TEST_SERVICE_ID_1)
|
||||||
|
|
||||||
advertiser.restartProbingForConflict(TEST_SERVICE_ID_1)
|
advertiser.maybeRestartProbingForConflict(TEST_SERVICE_ID_1)
|
||||||
|
|
||||||
verify(prober).restartForConflict(mockProbingInfo)
|
verify(prober).restartForConflict(mockProbingInfo)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user