blob: b9acc48518451e2440d4279b84034a017bf229aa [file] [log] [blame]
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001/*
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002 * Copyright (C) 2021 The Android Open Source Project
Irfan Sheriff77ec5582012-03-22 17:01:39 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Yuyang Huang33fa4d22023-02-14 22:59:37 +090019import static android.Manifest.permission.NETWORK_SETTINGS;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090020import static android.Manifest.permission.NETWORK_STACK;
paulhu2b9ed952022-02-10 21:58:32 +080021import static android.net.ConnectivityManager.NETID_UNSET;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090022import static android.net.NetworkCapabilities.TRANSPORT_VPN;
23import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090024import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
Paul Hu019621e2023-01-13 23:26:49 +080025import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
paulhu2b9ed952022-02-10 21:58:32 +080026import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +090027import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +090028import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
paulhu2b9ed952022-02-10 21:58:32 +080029
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090030import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090031import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
Paul Hu043bcd42023-07-14 16:38:25 +080032import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
33import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
Yuyang Huangde802c82023-05-02 17:14:22 +090034import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
Paul Hucdef3532023-06-18 14:47:35 +000035import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090036
Paul Hu23fa2022023-01-13 22:57:24 +080037import android.annotation.NonNull;
Paul Hu4bd98ef2023-01-12 13:42:07 +080038import android.annotation.Nullable;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090039import android.app.ActivityManager;
paulhua262cc12019-08-12 16:25:11 +080040import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070041import android.content.Intent;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090042import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090043import android.net.INetd;
Paul Hu75069ed2023-01-14 00:31:09 +080044import android.net.InetAddresses;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090045import android.net.LinkProperties;
46import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080047import android.net.mdns.aidl.DiscoveryInfo;
48import android.net.mdns.aidl.GetAddressInfo;
49import android.net.mdns.aidl.IMDnsEventListener;
50import android.net.mdns.aidl.RegistrationInfo;
51import android.net.mdns.aidl.ResolutionInfo;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070052import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090053import android.net.nsd.INsdManagerCallback;
54import android.net.nsd.INsdServiceConnector;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090055import android.net.nsd.IOffloadEngine;
paulhu2b9ed952022-02-10 21:58:32 +080056import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070057import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080058import android.net.nsd.NsdServiceInfo;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090059import android.net.nsd.OffloadEngine;
60import android.net.nsd.OffloadServiceInfo;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090061import android.net.wifi.WifiManager;
Paul Hub2e67d32023-04-18 05:50:14 +000062import android.os.Binder;
Hugo Benichi803a2f02017-04-24 11:35:06 +090063import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080064import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090065import android.os.IBinder;
Paul Hu4bd98ef2023-01-12 13:42:07 +080066import android.os.Looper;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070067import android.os.Message;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090068import android.os.RemoteCallbackList;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090069import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070070import android.os.UserHandle;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090071import android.provider.DeviceConfig;
Paul Hu23fa2022023-01-13 22:57:24 +080072import android.text.TextUtils;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090073import android.util.ArraySet;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090074import android.util.Log;
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +090075import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070076import android.util.SparseArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070077
paulhua262cc12019-08-12 16:25:11 +080078import com.android.internal.annotations.VisibleForTesting;
Paul Hub2e67d32023-04-18 05:50:14 +000079import com.android.internal.util.IndentingPrintWriter;
paulhua262cc12019-08-12 16:25:11 +080080import com.android.internal.util.State;
81import com.android.internal.util.StateMachine;
Paul Hucdef3532023-06-18 14:47:35 +000082import com.android.metrics.NetworkNsdReportedMetrics;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090083import com.android.modules.utils.build.SdkLevel;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090084import com.android.net.module.util.CollectionUtils;
Paul Hu4bd98ef2023-01-12 13:42:07 +080085import com.android.net.module.util.DeviceConfigUtils;
Yuyang Huanga6a6ff92023-04-24 13:33:34 +090086import com.android.net.module.util.InetAddressUtils;
paulhu3ffffe72021-09-16 10:15:22 +080087import com.android.net.module.util.PermissionUtils;
Paul Hub2e67d32023-04-18 05:50:14 +000088import com.android.net.module.util.SharedLog;
Paul Hu4bd98ef2023-01-12 13:42:07 +080089import com.android.server.connectivity.mdns.ExecutorProvider;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +090090import com.android.server.connectivity.mdns.MdnsAdvertiser;
Paul Hu4bd98ef2023-01-12 13:42:07 +080091import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090092import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
Paul Hu4bd98ef2023-01-12 13:42:07 +080093import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
Paul Hu23fa2022023-01-13 22:57:24 +080094import com.android.server.connectivity.mdns.MdnsSearchOptions;
95import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
96import com.android.server.connectivity.mdns.MdnsServiceInfo;
Paul Hu4bd98ef2023-01-12 13:42:07 +080097import com.android.server.connectivity.mdns.MdnsSocketProvider;
Yuyang Huangde802c82023-05-02 17:14:22 +090098import com.android.server.connectivity.mdns.util.MdnsUtils;
paulhua262cc12019-08-12 16:25:11 +080099
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700100import java.io.FileDescriptor;
101import java.io.PrintWriter;
Yuyang Huangaa0e9602023-03-17 12:43:09 +0900102import java.net.Inet6Address;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700103import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900104import java.net.NetworkInterface;
105import java.net.SocketException;
106import java.net.UnknownHostException;
Paul Hu2b865912023-03-06 14:27:53 +0800107import java.util.ArrayList;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +0900108import java.util.Arrays;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700109import java.util.HashMap;
Paul Hu23fa2022023-01-13 22:57:24 +0800110import java.util.List;
Paul Hu75069ed2023-01-14 00:31:09 +0800111import java.util.Map;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900112import java.util.Objects;
Paul Hu812e9212023-06-20 06:24:53 +0000113import java.util.Set;
Paul Hu23fa2022023-01-13 22:57:24 +0800114import java.util.regex.Matcher;
115import java.util.regex.Pattern;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700116
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700117/**
118 * Network Service Discovery Service handles remote service discovery operation requests by
119 * implementing the INsdManager interface.
120 *
121 * @hide
122 */
123public class NsdService extends INsdManager.Stub {
124 private static final String TAG = "NsdService";
125 private static final String MDNS_TAG = "mDnsConnector";
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900126 /**
127 * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
128 * implementation.
129 */
Paul Hu4bd98ef2023-01-12 13:42:07 +0800130 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
Paul Hu23fa2022023-01-13 22:57:24 +0800131 private static final String LOCAL_DOMAIN_NAME = "local";
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700132
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900133 /**
134 * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
135 * implementation.
136 */
137 private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
138
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900139 /**
140 * Comma-separated list of type:flag mappings indicating the flags to use to allowlist
141 * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
142 *
143 * For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
144 * _mytype._tcp:mytype,_othertype._tcp.local:othertype
145 *
146 * In which case the flags:
147 * "mdns_discovery_manager_allowlist_mytype_version",
148 * "mdns_advertiser_allowlist_mytype_version",
149 * "mdns_discovery_manager_allowlist_othertype_version",
150 * "mdns_advertiser_allowlist_othertype_version"
151 * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
152 * be read with
Motomu Utsumi624aeb42023-08-15 15:52:27 +0900153 * {@link DeviceConfigUtils#isTetheringFeatureEnabled}
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900154 *
155 * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
156 * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
157 * @see #MDNS_ALLOWLIST_FLAG_SUFFIX
158 */
159 private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
160
161 private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
162 "mdns_discovery_manager_allowlist_";
163 private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
164 "mdns_advertiser_allowlist_";
165 private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
166
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900167 @VisibleForTesting
168 static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
169 "mdns_config_running_app_active_importance_cutoff";
170 @VisibleForTesting
171 static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
172 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
173 private final int mRunningAppActiveImportanceCutoff;
174
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900175 public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Luke Huang92860f92021-06-23 06:29:30 +0000176 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900177 private static final int IFACE_IDX_ANY = 0;
Paul Hu812e9212023-06-20 06:24:53 +0000178 private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
179 @VisibleForTesting
180 static final int NO_TRANSACTION = -1;
Paul Hubad6fe92023-07-24 21:25:22 +0800181 private static final int NO_SENT_QUERY_COUNT = 0;
182 private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
Paul Hu14667de2023-04-17 22:42:47 +0800183 private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700184
Hugo Benichi32be63d2017-04-05 14:06:11 +0900185 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +0900186 private final NsdStateMachine mNsdStateMachine;
paulhu2b9ed952022-02-10 21:58:32 +0800187 private final MDnsManager mMDnsManager;
188 private final MDnsEventCallback mMDnsEventCallback;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900189 @NonNull
190 private final Dependencies mDeps;
191 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800192 private final MdnsMultinetworkSocketClient mMdnsSocketClient;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900193 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800194 private final MdnsDiscoveryManager mMdnsDiscoveryManager;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900195 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800196 private final MdnsSocketProvider mMdnsSocketProvider;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900197 @NonNull
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900198 private final MdnsAdvertiser mAdvertiser;
Paul Hu777ed052023-06-19 13:35:15 +0000199 @NonNull
200 private final Clock mClock;
Paul Hu14667de2023-04-17 22:42:47 +0800201 private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
Paul Hu23fa2022023-01-13 22:57:24 +0800202 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
paulhu2b9ed952022-02-10 21:58:32 +0800203 // state machine thread. If change this outside state machine, it will need to introduce
204 // synchronization.
205 private boolean mIsDaemonStarted = false;
Paul Hu23fa2022023-01-13 22:57:24 +0800206 private boolean mIsMonitoringSocketsStarted = false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700207
208 /**
209 * Clients receiving asynchronous messages
210 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900211 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700212
Paul Hud44e1b72023-06-16 02:07:42 +0000213 /* A map from transaction(unique) id to client info */
214 private final SparseArray<ClientInfo> mTransactionIdToClientInfoMap = new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700215
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900216 // Note this is not final to avoid depending on the Wi-Fi service starting before NsdService
217 @Nullable
218 private WifiManager.MulticastLock mHeldMulticastLock;
219 // Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network
220 // (non-null), value is the requested Network (nullable)
221 @NonNull
222 private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>();
223 @NonNull
224 private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>();
225
Luke Huang05298582021-06-13 16:52:05 +0000226 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700227
Hugo Benichi32be63d2017-04-05 14:06:11 +0900228 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700229 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +0800230 // The count of the connected legacy clients.
231 private int mLegacyClientCount = 0;
Paul Hub2e67d32023-04-18 05:50:14 +0000232 // The number of client that ever connected.
233 private int mClientNumberId = 1;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700234
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900235 private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
236 new RemoteCallbackList<>();
237
238 private static class OffloadEngineInfo {
239 @NonNull final String mInterfaceName;
240 final long mOffloadCapabilities;
241 final long mOffloadType;
242 @NonNull final IOffloadEngine mOffloadEngine;
243
244 OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
245 @NonNull String interfaceName, long capabilities, long offloadType) {
246 this.mOffloadEngine = offloadEngine;
247 this.mInterfaceName = interfaceName;
248 this.mOffloadCapabilities = capabilities;
249 this.mOffloadType = offloadType;
250 }
251 }
252
Paul Hu812e9212023-06-20 06:24:53 +0000253 @VisibleForTesting
254 static class MdnsListener implements MdnsServiceBrowserListener {
Paul Hud44e1b72023-06-16 02:07:42 +0000255 protected final int mClientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800256 protected final int mTransactionId;
257 @NonNull
258 protected final NsdServiceInfo mReqServiceInfo;
259 @NonNull
260 protected final String mListenedServiceType;
261
Paul Hud44e1b72023-06-16 02:07:42 +0000262 MdnsListener(int clientRequestId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
Paul Hu23fa2022023-01-13 22:57:24 +0800263 @NonNull String listenedServiceType) {
Paul Hud44e1b72023-06-16 02:07:42 +0000264 mClientRequestId = clientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800265 mTransactionId = transactionId;
266 mReqServiceInfo = reqServiceInfo;
267 mListenedServiceType = listenedServiceType;
268 }
269
270 @NonNull
271 public String getListenedServiceType() {
272 return mListenedServiceType;
273 }
274
275 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000276 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
277 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800278
279 @Override
280 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
281
282 @Override
283 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
284
285 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000286 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
287 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800288
289 @Override
290 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
291
292 @Override
293 public void onSearchStoppedWithError(int error) { }
294
295 @Override
296 public void onSearchFailedToStart() { }
297
298 @Override
Paul Hubad6fe92023-07-24 21:25:22 +0800299 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
300 int sentQueryTransactionId) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800301
302 @Override
303 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
304 }
305
306 private class DiscoveryListener extends MdnsListener {
307
Paul Hud44e1b72023-06-16 02:07:42 +0000308 DiscoveryListener(int clientRequestId, int transactionId,
309 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
310 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu23fa2022023-01-13 22:57:24 +0800311 }
312
313 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000314 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
315 boolean isServiceFromCache) {
Paul Hu019621e2023-01-13 23:26:49 +0800316 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
317 NsdManager.SERVICE_FOUND,
Paul Hua6bc4632023-06-26 01:18:29 +0000318 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu23fa2022023-01-13 22:57:24 +0800319 }
320
321 @Override
322 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
Paul Hu319751a2023-01-13 23:56:34 +0800323 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
324 NsdManager.SERVICE_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000325 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu23fa2022023-01-13 22:57:24 +0800326 }
Paul Hubad6fe92023-07-24 21:25:22 +0800327
328 @Override
329 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
330 int sentQueryTransactionId) {
331 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
332 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
333 }
Paul Hu23fa2022023-01-13 22:57:24 +0800334 }
335
Paul Hu75069ed2023-01-14 00:31:09 +0800336 private class ResolutionListener extends MdnsListener {
337
Paul Hud44e1b72023-06-16 02:07:42 +0000338 ResolutionListener(int clientRequestId, int transactionId,
339 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
340 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +0800341 }
342
343 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000344 public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
Paul Hu75069ed2023-01-14 00:31:09 +0800345 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
346 NsdManager.RESOLVE_SERVICE_SUCCEEDED,
Paul Hua6bc4632023-06-26 01:18:29 +0000347 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu75069ed2023-01-14 00:31:09 +0800348 }
Paul Hubad6fe92023-07-24 21:25:22 +0800349
350 @Override
351 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
352 int sentQueryTransactionId) {
353 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
354 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
355 }
Paul Hu75069ed2023-01-14 00:31:09 +0800356 }
357
Paul Hu30bd70d2023-02-07 13:20:56 +0000358 private class ServiceInfoListener extends MdnsListener {
359
Paul Hud44e1b72023-06-16 02:07:42 +0000360 ServiceInfoListener(int clientRequestId, int transactionId,
361 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
362 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +0000363 }
364
365 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000366 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
367 boolean isServiceFromCache) {
Paul Hu30bd70d2023-02-07 13:20:56 +0000368 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
369 NsdManager.SERVICE_UPDATED,
Paul Hua6bc4632023-06-26 01:18:29 +0000370 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu30bd70d2023-02-07 13:20:56 +0000371 }
372
373 @Override
374 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
375 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
376 NsdManager.SERVICE_UPDATED,
Paul Hud44e1b72023-06-16 02:07:42 +0000377 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000378 }
379
380 @Override
381 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
382 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
383 NsdManager.SERVICE_UPDATED_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000384 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000385 }
Paul Hubad6fe92023-07-24 21:25:22 +0800386
387 @Override
388 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
389 int sentQueryTransactionId) {
390 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
391 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
392 }
Paul Hu30bd70d2023-02-07 13:20:56 +0000393 }
394
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900395 private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
396 @Override
397 public void onSocketRequestFulfilled(@Nullable Network socketNetwork,
398 @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {
399 // The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF
400 // filtering on such interfaces, so taking the multicast lock is not necessary to
401 // disable APF filtering of multicast.
402 if (socketNetwork == null
403 || !CollectionUtils.contains(transports, TRANSPORT_WIFI)
404 || CollectionUtils.contains(transports, TRANSPORT_VPN)) {
405 return;
406 }
407
408 if (mWifiLockRequiredNetworks.add(socketNetwork)) {
409 updateMulticastLock();
410 }
411 }
412
413 @Override
414 public void onSocketDestroyed(@Nullable Network socketNetwork,
415 @NonNull MdnsInterfaceSocket socket) {
416 if (mWifiLockRequiredNetworks.remove(socketNetwork)) {
417 updateMulticastLock();
418 }
419 }
420 }
421
422 private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
423 private final Handler mHandler;
424
425 private UidImportanceListener(Handler handler) {
426 mHandler = handler;
427 }
428
429 @Override
430 public void onUidImportance(int uid, int importance) {
431 mHandler.post(() -> handleUidImportanceChanged(uid, importance));
432 }
433 }
434
435 private void handleUidImportanceChanged(int uid, int importance) {
436 // Lower importance values are more "important"
437 final boolean modified = importance <= mRunningAppActiveImportanceCutoff
438 ? mRunningAppActiveUids.add(uid)
439 : mRunningAppActiveUids.remove(uid);
440 if (modified) {
441 updateMulticastLock();
442 }
443 }
444
445 /**
446 * Take or release the lock based on updated internal state.
447 *
448 * This determines whether the lock needs to be held based on
449 * {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and
450 * {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been
451 * updated.
452 */
453 private void updateMulticastLock() {
454 final int needsLockUid = getMulticastLockNeededUid();
455 if (needsLockUid >= 0 && mHeldMulticastLock == null) {
456 final WifiManager wm = mContext.getSystemService(WifiManager.class);
457 if (wm == null) {
458 Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager");
459 return;
460 }
461 mHeldMulticastLock = wm.createMulticastLock(TAG);
462 mHeldMulticastLock.acquire();
463 mServiceLogs.log("Taking multicast lock for uid " + needsLockUid);
464 } else if (needsLockUid < 0 && mHeldMulticastLock != null) {
465 mHeldMulticastLock.release();
466 mHeldMulticastLock = null;
467 mServiceLogs.log("Released multicast lock");
468 }
469 }
470
471 /**
472 * @return The UID of an app requiring the multicast lock, or -1 if none.
473 */
474 private int getMulticastLockNeededUid() {
475 if (mWifiLockRequiredNetworks.size() == 0) {
476 // Return early if NSD is not active, or not on any relevant network
477 return -1;
478 }
Paul Hud44e1b72023-06-16 02:07:42 +0000479 for (int i = 0; i < mTransactionIdToClientInfoMap.size(); i++) {
480 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.valueAt(i);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900481 if (!mRunningAppActiveUids.contains(clientInfo.mUid)) {
482 // Ignore non-active UIDs
483 continue;
484 }
485
486 if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) {
487 return clientInfo.mUid;
488 }
489 }
490 return -1;
491 }
492
Paul Hu019621e2023-01-13 23:26:49 +0800493 /**
494 * Data class of mdns service callback information.
495 */
496 private static class MdnsEvent {
Paul Hud44e1b72023-06-16 02:07:42 +0000497 final int mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +0800498 @Nullable
Paul Hu019621e2023-01-13 23:26:49 +0800499 final MdnsServiceInfo mMdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000500 final boolean mIsServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800501
Paul Hubad6fe92023-07-24 21:25:22 +0800502 MdnsEvent(int clientRequestId) {
503 this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */);
504 }
505
506 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) {
Paul Hua6bc4632023-06-26 01:18:29 +0000507 this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
508 }
509
Paul Hubad6fe92023-07-24 21:25:22 +0800510 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo,
Paul Hua6bc4632023-06-26 01:18:29 +0000511 boolean isServiceFromCache) {
Paul Hud44e1b72023-06-16 02:07:42 +0000512 mClientRequestId = clientRequestId;
Paul Hu019621e2023-01-13 23:26:49 +0800513 mMdnsServiceInfo = mdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000514 mIsServiceFromCache = isServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800515 }
516 }
517
Irfan Sheriff75006652012-04-17 23:15:29 -0700518 private class NsdStateMachine extends StateMachine {
519
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700520 private final DefaultState mDefaultState = new DefaultState();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700521 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700522
523 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700524 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900525 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700526 }
527
Luke Huang92860f92021-06-23 06:29:30 +0000528 private void maybeStartDaemon() {
paulhu2b9ed952022-02-10 21:58:32 +0800529 if (mIsDaemonStarted) {
530 if (DBG) Log.d(TAG, "Daemon is already started.");
531 return;
532 }
533 mMDnsManager.registerEventListener(mMDnsEventCallback);
534 mMDnsManager.startDaemon();
535 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000536 maybeScheduleStop();
Paul Hub2e67d32023-04-18 05:50:14 +0000537 mServiceLogs.log("Start mdns_responder daemon");
Luke Huang05298582021-06-13 16:52:05 +0000538 }
539
paulhu2b9ed952022-02-10 21:58:32 +0800540 private void maybeStopDaemon() {
541 if (!mIsDaemonStarted) {
542 if (DBG) Log.d(TAG, "Daemon has not been started.");
543 return;
544 }
545 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
546 mMDnsManager.stopDaemon();
547 mIsDaemonStarted = false;
Paul Hub2e67d32023-04-18 05:50:14 +0000548 mServiceLogs.log("Stop mdns_responder daemon");
paulhu2b9ed952022-02-10 21:58:32 +0800549 }
550
Luke Huang92860f92021-06-23 06:29:30 +0000551 private boolean isAnyRequestActive() {
Paul Hud44e1b72023-06-16 02:07:42 +0000552 return mTransactionIdToClientInfoMap.size() != 0;
Luke Huang92860f92021-06-23 06:29:30 +0000553 }
554
555 private void scheduleStop() {
556 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
557 }
558 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800559 // The native daemon should stay alive and can't be cleanup
560 // if any legacy client connected.
561 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000562 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000563 }
564 }
565
Luke Huang92860f92021-06-23 06:29:30 +0000566 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000567 this.removeMessages(NsdManager.DAEMON_CLEANUP);
568 }
569
Paul Hu23fa2022023-01-13 22:57:24 +0800570 private void maybeStartMonitoringSockets() {
571 if (mIsMonitoringSocketsStarted) {
572 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
573 return;
574 }
575
576 mMdnsSocketProvider.startMonitoringSockets();
577 mIsMonitoringSocketsStarted = true;
578 }
579
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900580 private void maybeStopMonitoringSocketsIfNoActiveRequest() {
581 if (!mIsMonitoringSocketsStarted) return;
582 if (isAnyRequestActive()) return;
583
Paul Hu58f20602023-02-18 11:41:07 +0800584 mMdnsSocketProvider.requestStopWhenInactive();
Paul Hu23fa2022023-01-13 22:57:24 +0800585 mIsMonitoringSocketsStarted = false;
586 }
587
Hugo Benichi803a2f02017-04-24 11:35:06 +0900588 NsdStateMachine(String name, Handler handler) {
589 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700590 addState(mDefaultState);
Irfan Sheriff75006652012-04-17 23:15:29 -0700591 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800592 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900593 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700594 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700595 }
596
597 class DefaultState extends State {
598 @Override
599 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900600 final ClientInfo cInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000601 final int clientRequestId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700602 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900603 case NsdManager.REGISTER_CLIENT:
Paul Hu2e0a88c2023-03-09 16:05:01 +0800604 final ConnectorArgs arg = (ConnectorArgs) msg.obj;
605 final INsdManagerCallback cb = arg.callback;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900606 try {
Paul Hu2e0a88c2023-03-09 16:05:01 +0800607 cb.asBinder().linkToDeath(arg.connector, 0);
Paul Hub2e67d32023-04-18 05:50:14 +0000608 final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
Paul Hu777ed052023-06-19 13:35:15 +0000609 final NetworkNsdReportedMetrics metrics =
610 mDeps.makeNetworkNsdReportedMetrics(
611 !arg.useJavaBackend, (int) mClock.elapsedRealtime());
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900612 cInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
Paul Hucdef3532023-06-18 14:47:35 +0000613 mServiceLogs.forSubComponent(tag), metrics);
Paul Hu2e0a88c2023-03-09 16:05:01 +0800614 mClients.put(arg.connector, cInfo);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900615 } catch (RemoteException e) {
Paul Hud44e1b72023-06-16 02:07:42 +0000616 Log.w(TAG, "Client request id " + clientRequestId
617 + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700618 }
619 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900620 case NsdManager.UNREGISTER_CLIENT:
621 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
622 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800623 if (cInfo != null) {
624 cInfo.expungeAllRequests();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900625 if (cInfo.isPreSClient()) {
Luke Huangf7277ed2021-07-12 21:15:10 +0800626 mLegacyClientCount -= 1;
627 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800628 }
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900629 maybeStopMonitoringSocketsIfNoActiveRequest();
Luke Huangf7277ed2021-07-12 21:15:10 +0800630 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700631 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700632 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900633 cInfo = getClientInfoForReply(msg);
634 if (cInfo != null) {
Paul Hu812e9212023-06-20 06:24:53 +0000635 cInfo.onDiscoverServicesFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +0000636 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900637 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700638 break;
639 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900640 cInfo = getClientInfoForReply(msg);
641 if (cInfo != null) {
642 cInfo.onStopDiscoveryFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000643 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900644 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700645 break;
646 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900647 cInfo = getClientInfoForReply(msg);
648 if (cInfo != null) {
Paul Hu777ed052023-06-19 13:35:15 +0000649 cInfo.onRegisterServiceFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +0000650 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900651 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700652 break;
653 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900654 cInfo = getClientInfoForReply(msg);
655 if (cInfo != null) {
656 cInfo.onUnregisterServiceFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000657 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900658 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700659 break;
660 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900661 cInfo = getClientInfoForReply(msg);
662 if (cInfo != null) {
Paul Hua6bc4632023-06-26 01:18:29 +0000663 cInfo.onResolveServiceFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +0000664 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900665 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700666 break;
Paul Hub58deb72022-12-26 09:24:42 +0000667 case NsdManager.STOP_RESOLUTION:
668 cInfo = getClientInfoForReply(msg);
669 if (cInfo != null) {
670 cInfo.onStopResolutionFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000671 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
Paul Hub58deb72022-12-26 09:24:42 +0000672 }
673 break;
Paul Hu18aeccc2022-12-27 08:48:48 +0000674 case NsdManager.REGISTER_SERVICE_CALLBACK:
675 cInfo = getClientInfoForReply(msg);
676 if (cInfo != null) {
677 cInfo.onServiceInfoCallbackRegistrationFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000678 clientRequestId, NsdManager.FAILURE_BAD_PARAMETERS);
Paul Hu18aeccc2022-12-27 08:48:48 +0000679 }
680 break;
Luke Huang05298582021-06-13 16:52:05 +0000681 case NsdManager.DAEMON_CLEANUP:
paulhu2b9ed952022-02-10 21:58:32 +0800682 maybeStopDaemon();
Luke Huang05298582021-06-13 16:52:05 +0000683 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800684 // This event should be only sent by the legacy (target SDK < S) clients.
685 // Mark the sending client as legacy.
686 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900687 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800688 if (cInfo != null) {
689 cancelStop();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900690 cInfo.setPreSClient();
Luke Huangf7277ed2021-07-12 21:15:10 +0800691 mLegacyClientCount += 1;
692 maybeStartDaemon();
693 }
694 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700695 default:
paulhub2225702021-11-17 09:35:33 +0800696 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700697 return NOT_HANDLED;
698 }
699 return HANDLED;
700 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900701
702 private ClientInfo getClientInfoForReply(Message msg) {
703 final ListenerArgs args = (ListenerArgs) msg.obj;
704 return mClients.get(args.connector);
705 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700706 }
707
Irfan Sheriff75006652012-04-17 23:15:29 -0700708 class EnabledState extends State {
709 @Override
710 public void enter() {
711 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700712 }
713
714 @Override
715 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000716 // TODO: it is incorrect to stop the daemon without expunging all requests
717 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000718 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700719 }
720
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700721 private boolean requestLimitReached(ClientInfo clientInfo) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900722 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800723 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700724 return true;
725 }
726 return false;
727 }
728
Paul Hud44e1b72023-06-16 02:07:42 +0000729 private void storeLegacyRequestMap(int clientRequestId, int transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +0000730 ClientInfo clientInfo, int what, long startTimeMs) {
731 clientInfo.mClientRequests.put(clientRequestId,
732 new LegacyClientRequest(transactionId, what, startTimeMs));
Paul Hud44e1b72023-06-16 02:07:42 +0000733 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000734 // Remove the cleanup event because here comes a new request.
735 cancelStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700736 }
737
Paul Hud44e1b72023-06-16 02:07:42 +0000738 private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900739 ClientInfo clientInfo, @Nullable Network requestedNetwork) {
Paul Hu777ed052023-06-19 13:35:15 +0000740 clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
Paul Hu812e9212023-06-20 06:24:53 +0000741 transactionId, requestedNetwork, mClock.elapsedRealtime()));
Paul Hud44e1b72023-06-16 02:07:42 +0000742 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900743 updateMulticastLock();
Paul Hu23fa2022023-01-13 22:57:24 +0800744 }
745
Paul Hud44e1b72023-06-16 02:07:42 +0000746 private void removeRequestMap(
747 int clientRequestId, int transactionId, ClientInfo clientInfo) {
748 final ClientRequest existing = clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900749 if (existing == null) return;
Paul Hud44e1b72023-06-16 02:07:42 +0000750 clientInfo.mClientRequests.remove(clientRequestId);
751 mTransactionIdToClientInfoMap.remove(transactionId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900752
753 if (existing instanceof LegacyClientRequest) {
754 maybeScheduleStop();
755 } else {
756 maybeStopMonitoringSocketsIfNoActiveRequest();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900757 updateMulticastLock();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900758 }
759 }
760
Paul Hud44e1b72023-06-16 02:07:42 +0000761 private void storeDiscoveryManagerRequestMap(int clientRequestId, int transactionId,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900762 MdnsListener listener, ClientInfo clientInfo,
763 @Nullable Network requestedNetwork) {
Paul Hu777ed052023-06-19 13:35:15 +0000764 clientInfo.mClientRequests.put(clientRequestId, new DiscoveryManagerRequest(
Paul Hu812e9212023-06-20 06:24:53 +0000765 transactionId, listener, requestedNetwork, mClock.elapsedRealtime()));
Paul Hud44e1b72023-06-16 02:07:42 +0000766 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900767 updateMulticastLock();
Paul Hu23fa2022023-01-13 22:57:24 +0800768 }
769
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900770 /**
771 * Truncate a service name to up to 63 UTF-8 bytes.
772 *
773 * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
774 * names used in registerService follows historical behavior (see mdnsresponder
775 * handle_regservice_request).
776 */
777 @NonNull
778 private String truncateServiceName(@NonNull String originalName) {
Yuyang Huangde802c82023-05-02 17:14:22 +0900779 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
Paul Hu23fa2022023-01-13 22:57:24 +0800780 }
781
Paul Hud44e1b72023-06-16 02:07:42 +0000782 private void stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId,
783 int transactionId, ClientInfo clientInfo) {
Paul Hue4f5f252023-02-16 21:13:47 +0800784 clientInfo.unregisterMdnsListenerFromRequest(request);
Paul Hud44e1b72023-06-16 02:07:42 +0000785 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hue4f5f252023-02-16 21:13:47 +0800786 }
787
Irfan Sheriff75006652012-04-17 23:15:29 -0700788 @Override
789 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900790 final ClientInfo clientInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000791 final int transactionId;
792 final int clientRequestId = msg.arg2;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900793 final ListenerArgs args;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900794 final OffloadEngineInfo offloadEngineInfo;
Irfan Sheriff75006652012-04-17 23:15:29 -0700795 switch (msg.what) {
Paul Hu75069ed2023-01-14 00:31:09 +0800796 case NsdManager.DISCOVER_SERVICES: {
paulhub2225702021-11-17 09:35:33 +0800797 if (DBG) Log.d(TAG, "Discover services");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900798 args = (ListenerArgs) msg.obj;
799 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000800 // If the binder death notification for a INsdManagerCallback was received
801 // before any calls are received by NsdService, the clientInfo would be
802 // cleared and cause NPE. Add a null check here to prevent this corner case.
803 if (clientInfo == null) {
804 Log.e(TAG, "Unknown connector in discovery");
805 break;
806 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700807
808 if (requestLimitReached(clientInfo)) {
Paul Hu812e9212023-06-20 06:24:53 +0000809 clientInfo.onDiscoverServicesFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +0000810 clientRequestId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff75006652012-04-17 23:15:29 -0700811 break;
812 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700813
Paul Hu23fa2022023-01-13 22:57:24 +0800814 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000815 transactionId = getUniqueId();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900816 final Pair<String, String> typeAndSubtype =
817 parseTypeAndSubtype(info.getServiceType());
818 final String serviceType = typeAndSubtype == null
819 ? null : typeAndSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800820 if (clientInfo.mUseJavaBackend
821 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900822 || useDiscoveryManagerForType(serviceType)) {
Paul Hu23fa2022023-01-13 22:57:24 +0800823 if (serviceType == null) {
Paul Hu812e9212023-06-20 06:24:53 +0000824 clientInfo.onDiscoverServicesFailedImmediately(
825 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu23fa2022023-01-13 22:57:24 +0800826 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700827 }
Paul Hu23fa2022023-01-13 22:57:24 +0800828
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900829 final String listenServiceType = serviceType + ".local";
Paul Hu23fa2022023-01-13 22:57:24 +0800830 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +0000831 final MdnsListener listener = new DiscoveryListener(clientRequestId,
832 transactionId, info, listenServiceType);
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900833 final MdnsSearchOptions.Builder optionsBuilder =
834 MdnsSearchOptions.newBuilder()
835 .setNetwork(info.getNetwork())
Yuyang Huangff963222023-06-01 18:42:42 +0900836 .setRemoveExpiredService(true)
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900837 .setIsPassiveMode(true);
838 if (typeAndSubtype.second != null) {
839 // The parsing ensures subtype starts with an underscore.
840 // MdnsSearchOptions expects the underscore to not be present.
841 optionsBuilder.addSubtype(typeAndSubtype.second.substring(1));
842 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900843 mMdnsDiscoveryManager.registerListener(
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900844 listenServiceType, listener, optionsBuilder.build());
Paul Hud44e1b72023-06-16 02:07:42 +0000845 storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
846 listener, clientInfo, info.getNetwork());
Paul Hu812e9212023-06-20 06:24:53 +0000847 clientInfo.onDiscoverServicesStarted(
848 clientRequestId, info, transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +0000849 clientInfo.log("Register a DiscoveryListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +0000850 + " for service type:" + listenServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -0700851 } else {
Paul Hu23fa2022023-01-13 22:57:24 +0800852 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +0000853 if (discoverServices(transactionId, info)) {
Paul Hu23fa2022023-01-13 22:57:24 +0800854 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +0000855 Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
Paul Hu23fa2022023-01-13 22:57:24 +0800856 + info.getServiceType());
857 }
Paul Hua6bc4632023-06-26 01:18:29 +0000858 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
859 msg.what, mClock.elapsedRealtime());
Paul Hu812e9212023-06-20 06:24:53 +0000860 clientInfo.onDiscoverServicesStarted(
861 clientRequestId, info, transactionId);
Paul Hu23fa2022023-01-13 22:57:24 +0800862 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000863 stopServiceDiscovery(transactionId);
Paul Hu812e9212023-06-20 06:24:53 +0000864 clientInfo.onDiscoverServicesFailedImmediately(
865 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu23fa2022023-01-13 22:57:24 +0800866 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700867 }
868 break;
Paul Hu75069ed2023-01-14 00:31:09 +0800869 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900870 case NsdManager.STOP_DISCOVERY: {
paulhub2225702021-11-17 09:35:33 +0800871 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900872 args = (ListenerArgs) msg.obj;
873 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000874 // If the binder death notification for a INsdManagerCallback was received
875 // before any calls are received by NsdService, the clientInfo would be
876 // cleared and cause NPE. Add a null check here to prevent this corner case.
877 if (clientInfo == null) {
878 Log.e(TAG, "Unknown connector in stop discovery");
879 break;
880 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700881
Paul Hud44e1b72023-06-16 02:07:42 +0000882 final ClientRequest request =
883 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900884 if (request == null) {
885 Log.e(TAG, "Unknown client request in STOP_DISCOVERY");
Irfan Sheriff75006652012-04-17 23:15:29 -0700886 break;
887 }
Paul Hud44e1b72023-06-16 02:07:42 +0000888 transactionId = request.mTransactionId;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900889 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
890 // point, so this needs to check the type of the original request to
891 // unregister instead of looking at the flag value.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900892 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +0000893 stopDiscoveryManagerRequest(
894 request, clientRequestId, transactionId, clientInfo);
Paul Hu812e9212023-06-20 06:24:53 +0000895 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000896 clientInfo.log("Unregister the DiscoveryListener " + transactionId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700897 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000898 removeRequestMap(clientRequestId, transactionId, clientInfo);
899 if (stopServiceDiscovery(transactionId)) {
Paul Hu812e9212023-06-20 06:24:53 +0000900 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800901 } else {
902 clientInfo.onStopDiscoveryFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000903 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu23fa2022023-01-13 22:57:24 +0800904 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700905 }
906 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900907 }
908 case NsdManager.REGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800909 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900910 args = (ListenerArgs) msg.obj;
911 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000912 // If the binder death notification for a INsdManagerCallback was received
913 // before any calls are received by NsdService, the clientInfo would be
914 // cleared and cause NPE. Add a null check here to prevent this corner case.
915 if (clientInfo == null) {
916 Log.e(TAG, "Unknown connector in registration");
917 break;
918 }
919
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700920 if (requestLimitReached(clientInfo)) {
Paul Hu777ed052023-06-19 13:35:15 +0000921 clientInfo.onRegisterServiceFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +0000922 clientRequestId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700923 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700924 }
925
Paul Hud44e1b72023-06-16 02:07:42 +0000926 transactionId = getUniqueId();
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900927 final NsdServiceInfo serviceInfo = args.serviceInfo;
928 final String serviceType = serviceInfo.getServiceType();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900929 final Pair<String, String> typeSubtype = parseTypeAndSubtype(serviceType);
930 final String registerServiceType = typeSubtype == null
931 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800932 if (clientInfo.mUseJavaBackend
933 || mDeps.isMdnsAdvertiserEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900934 || useAdvertiserForType(registerServiceType)) {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900935 if (registerServiceType == null) {
936 Log.e(TAG, "Invalid service type: " + serviceType);
Paul Hu777ed052023-06-19 13:35:15 +0000937 clientInfo.onRegisterServiceFailedImmediately(
938 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900939 break;
940 }
941 serviceInfo.setServiceType(registerServiceType);
942 serviceInfo.setServiceName(truncateServiceName(
943 serviceInfo.getServiceName()));
944
945 maybeStartMonitoringSockets();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900946 // TODO: pass in the subtype as well. Including the subtype in the
947 // service type would generate service instance names like
948 // Name._subtype._sub._type._tcp, which is incorrect
949 // (it should be Name._type._tcp).
Paul Hud44e1b72023-06-16 02:07:42 +0000950 mAdvertiser.addService(transactionId, serviceInfo, typeSubtype.second);
951 storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900952 serviceInfo.getNetwork());
Irfan Sheriff75006652012-04-17 23:15:29 -0700953 } else {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900954 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +0000955 if (registerService(transactionId, serviceInfo)) {
956 if (DBG) {
957 Log.d(TAG, "Register " + clientRequestId
958 + " " + transactionId);
959 }
Paul Hua6bc4632023-06-26 01:18:29 +0000960 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
961 msg.what, mClock.elapsedRealtime());
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900962 // Return success after mDns reports success
963 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000964 unregisterService(transactionId);
Paul Hu777ed052023-06-19 13:35:15 +0000965 clientInfo.onRegisterServiceFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +0000966 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900967 }
968
Irfan Sheriff75006652012-04-17 23:15:29 -0700969 }
970 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900971 }
972 case NsdManager.UNREGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800973 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900974 args = (ListenerArgs) msg.obj;
975 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000976 // If the binder death notification for a INsdManagerCallback was received
977 // before any calls are received by NsdService, the clientInfo would be
978 // cleared and cause NPE. Add a null check here to prevent this corner case.
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900979 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800980 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700981 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700982 }
Paul Hud44e1b72023-06-16 02:07:42 +0000983 final ClientRequest request =
984 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900985 if (request == null) {
986 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE");
987 break;
988 }
Paul Hud44e1b72023-06-16 02:07:42 +0000989 transactionId = request.mTransactionId;
990 removeRequestMap(clientRequestId, transactionId, clientInfo);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900991
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900992 // Note isMdnsAdvertiserEnabled may have changed to false at this point,
993 // so this needs to check the type of the original request to unregister
994 // instead of looking at the flag value.
Paul Hu812e9212023-06-20 06:24:53 +0000995 final long stopTimeMs = mClock.elapsedRealtime();
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900996 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +0800997 final AdvertiserMetrics metrics =
998 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +0000999 mAdvertiser.removeService(transactionId);
Paul Hu777ed052023-06-19 13:35:15 +00001000 clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08001001 request.calculateRequestDurationMs(stopTimeMs), metrics);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001002 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001003 if (unregisterService(transactionId)) {
Paul Hu777ed052023-06-19 13:35:15 +00001004 clientInfo.onUnregisterServiceSucceeded(clientRequestId,
Paul Hu812e9212023-06-20 06:24:53 +00001005 transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08001006 request.calculateRequestDurationMs(stopTimeMs),
1007 new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
1008 NO_PACKET /* sentPacketCount */,
1009 0 /* conflictDuringProbingCount */,
1010 0 /* conflictAfterProbingCount */));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001011 } else {
1012 clientInfo.onUnregisterServiceFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001013 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001014 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001015 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001016 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001017 }
Paul Hu75069ed2023-01-14 00:31:09 +08001018 case NsdManager.RESOLVE_SERVICE: {
paulhub2225702021-11-17 09:35:33 +08001019 if (DBG) Log.d(TAG, "Resolve service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001020 args = (ListenerArgs) msg.obj;
1021 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +00001022 // If the binder death notification for a INsdManagerCallback was received
1023 // before any calls are received by NsdService, the clientInfo would be
1024 // cleared and cause NPE. Add a null check here to prevent this corner case.
1025 if (clientInfo == null) {
1026 Log.e(TAG, "Unknown connector in resolution");
1027 break;
1028 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001029
Paul Hu75069ed2023-01-14 00:31:09 +08001030 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001031 transactionId = getUniqueId();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001032 final Pair<String, String> typeSubtype =
1033 parseTypeAndSubtype(info.getServiceType());
1034 final String serviceType = typeSubtype == null
1035 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +08001036 if (clientInfo.mUseJavaBackend
1037 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001038 || useDiscoveryManagerForType(serviceType)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001039 if (serviceType == null) {
Paul Hua6bc4632023-06-26 01:18:29 +00001040 clientInfo.onResolveServiceFailedImmediately(
1041 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu75069ed2023-01-14 00:31:09 +08001042 break;
1043 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001044 final String resolveServiceType = serviceType + ".local";
Paul Hu75069ed2023-01-14 00:31:09 +08001045
1046 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001047 final MdnsListener listener = new ResolutionListener(clientRequestId,
1048 transactionId, info, resolveServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +08001049 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1050 .setNetwork(info.getNetwork())
1051 .setIsPassiveMode(true)
Remi NGUYEN VANbb62b1d2023-02-27 12:18:27 +09001052 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001053 .setRemoveExpiredService(true)
Paul Hu75069ed2023-01-14 00:31:09 +08001054 .build();
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001055 mMdnsDiscoveryManager.registerListener(
1056 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001057 storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
1058 listener, clientInfo, info.getNetwork());
1059 clientInfo.log("Register a ResolutionListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001060 + " for service type:" + resolveServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -07001061 } else {
Paul Hu75069ed2023-01-14 00:31:09 +08001062 if (clientInfo.mResolvedService != null) {
Paul Hua6bc4632023-06-26 01:18:29 +00001063 clientInfo.onResolveServiceFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +00001064 clientRequestId, NsdManager.FAILURE_ALREADY_ACTIVE);
Paul Hu75069ed2023-01-14 00:31:09 +08001065 break;
1066 }
1067
1068 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +00001069 if (resolveService(transactionId, info)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001070 clientInfo.mResolvedService = new NsdServiceInfo();
Paul Hua6bc4632023-06-26 01:18:29 +00001071 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
1072 msg.what, mClock.elapsedRealtime());
Paul Hu75069ed2023-01-14 00:31:09 +08001073 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001074 clientInfo.onResolveServiceFailedImmediately(
Paul Hud44e1b72023-06-16 02:07:42 +00001075 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu75069ed2023-01-14 00:31:09 +08001076 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001077 }
1078 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001079 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001080 case NsdManager.STOP_RESOLUTION: {
Paul Hub58deb72022-12-26 09:24:42 +00001081 if (DBG) Log.d(TAG, "Stop service resolution");
1082 args = (ListenerArgs) msg.obj;
1083 clientInfo = mClients.get(args.connector);
1084 // If the binder death notification for a INsdManagerCallback was received
1085 // before any calls are received by NsdService, the clientInfo would be
1086 // cleared and cause NPE. Add a null check here to prevent this corner case.
1087 if (clientInfo == null) {
1088 Log.e(TAG, "Unknown connector in stop resolution");
1089 break;
1090 }
1091
Paul Hud44e1b72023-06-16 02:07:42 +00001092 final ClientRequest request =
1093 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001094 if (request == null) {
1095 Log.e(TAG, "Unknown client request in STOP_RESOLUTION");
1096 break;
1097 }
Paul Hud44e1b72023-06-16 02:07:42 +00001098 transactionId = request.mTransactionId;
Paul Hue4f5f252023-02-16 21:13:47 +08001099 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
1100 // point, so this needs to check the type of the original request to
1101 // unregister instead of looking at the flag value.
1102 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001103 stopDiscoveryManagerRequest(
1104 request, clientRequestId, transactionId, clientInfo);
Paul Hu60149052023-07-31 14:26:08 +08001105 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001106 clientInfo.log("Unregister the ResolutionListener " + transactionId);
Paul Hub58deb72022-12-26 09:24:42 +00001107 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001108 removeRequestMap(clientRequestId, transactionId, clientInfo);
1109 if (stopResolveService(transactionId)) {
Paul Hu60149052023-07-31 14:26:08 +08001110 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hue4f5f252023-02-16 21:13:47 +08001111 } else {
1112 clientInfo.onStopResolutionFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001113 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
Paul Hue4f5f252023-02-16 21:13:47 +08001114 }
1115 clientInfo.mResolvedService = null;
Paul Hub58deb72022-12-26 09:24:42 +00001116 }
Paul Hub58deb72022-12-26 09:24:42 +00001117 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001118 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001119 case NsdManager.REGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001120 if (DBG) Log.d(TAG, "Register a service callback");
1121 args = (ListenerArgs) msg.obj;
1122 clientInfo = mClients.get(args.connector);
1123 // If the binder death notification for a INsdManagerCallback was received
1124 // before any calls are received by NsdService, the clientInfo would be
1125 // cleared and cause NPE. Add a null check here to prevent this corner case.
1126 if (clientInfo == null) {
1127 Log.e(TAG, "Unknown connector in callback registration");
1128 break;
1129 }
1130
Paul Hu30bd70d2023-02-07 13:20:56 +00001131 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001132 transactionId = getUniqueId();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001133 final Pair<String, String> typeAndSubtype =
1134 parseTypeAndSubtype(info.getServiceType());
1135 final String serviceType = typeAndSubtype == null
1136 ? null : typeAndSubtype.first;
Paul Hu30bd70d2023-02-07 13:20:56 +00001137 if (serviceType == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001138 clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId,
Paul Hu30bd70d2023-02-07 13:20:56 +00001139 NsdManager.FAILURE_BAD_PARAMETERS);
Paul Hu18aeccc2022-12-27 08:48:48 +00001140 break;
1141 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001142 final String resolveServiceType = serviceType + ".local";
Paul Hu18aeccc2022-12-27 08:48:48 +00001143
Paul Hu30bd70d2023-02-07 13:20:56 +00001144 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001145 final MdnsListener listener = new ServiceInfoListener(clientRequestId,
1146 transactionId, info, resolveServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +00001147 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1148 .setNetwork(info.getNetwork())
1149 .setIsPassiveMode(true)
1150 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001151 .setRemoveExpiredService(true)
Paul Hu30bd70d2023-02-07 13:20:56 +00001152 .build();
1153 mMdnsDiscoveryManager.registerListener(
1154 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001155 storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener,
1156 clientInfo, info.getNetwork());
Paul Huddce5912023-08-01 10:26:49 +08001157 clientInfo.onServiceInfoCallbackRegistered(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00001158 clientInfo.log("Register a ServiceInfoListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001159 + " for service type:" + resolveServiceType);
Paul Hu18aeccc2022-12-27 08:48:48 +00001160 break;
Paul Hu30bd70d2023-02-07 13:20:56 +00001161 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001162 case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001163 if (DBG) Log.d(TAG, "Unregister a service callback");
1164 args = (ListenerArgs) msg.obj;
1165 clientInfo = mClients.get(args.connector);
1166 // If the binder death notification for a INsdManagerCallback was received
1167 // before any calls are received by NsdService, the clientInfo would be
1168 // cleared and cause NPE. Add a null check here to prevent this corner case.
1169 if (clientInfo == null) {
1170 Log.e(TAG, "Unknown connector in callback unregistration");
1171 break;
1172 }
1173
Paul Hud44e1b72023-06-16 02:07:42 +00001174 final ClientRequest request =
1175 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001176 if (request == null) {
Paul Hu30bd70d2023-02-07 13:20:56 +00001177 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001178 break;
1179 }
Paul Hud44e1b72023-06-16 02:07:42 +00001180 transactionId = request.mTransactionId;
Paul Hu30bd70d2023-02-07 13:20:56 +00001181 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001182 stopDiscoveryManagerRequest(
1183 request, clientRequestId, transactionId, clientInfo);
Paul Huddce5912023-08-01 10:26:49 +08001184 clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001185 clientInfo.log("Unregister the ServiceInfoListener " + transactionId);
Paul Hu18aeccc2022-12-27 08:48:48 +00001186 } else {
Paul Hu30bd70d2023-02-07 13:20:56 +00001187 loge("Unregister failed with non-DiscoveryManagerRequest.");
Paul Hu18aeccc2022-12-27 08:48:48 +00001188 }
Paul Hu18aeccc2022-12-27 08:48:48 +00001189 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001190 }
paulhu2b9ed952022-02-10 21:58:32 +08001191 case MDNS_SERVICE_EVENT:
1192 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +09001193 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001194 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001195 break;
Paul Hu019621e2023-01-13 23:26:49 +08001196 case MDNS_DISCOVERY_MANAGER_EVENT:
1197 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
1198 return NOT_HANDLED;
1199 }
1200 break;
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001201 case NsdManager.REGISTER_OFFLOAD_ENGINE:
1202 offloadEngineInfo = (OffloadEngineInfo) msg.obj;
1203 // TODO: Limits the number of registrations created by a given class.
1204 mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
1205 offloadEngineInfo);
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001206 sendAllOffloadServiceInfos(offloadEngineInfo);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001207 break;
1208 case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
1209 mOffloadEngines.unregister((IOffloadEngine) msg.obj);
1210 break;
Irfan Sheriff75006652012-04-17 23:15:29 -07001211 default:
Hugo Benichif0c84092017-04-05 14:43:29 +09001212 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001213 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001214 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001215 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001216
Paul Hud44e1b72023-06-16 02:07:42 +00001217 private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001218 NsdServiceInfo servInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001219 ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001220 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001221 Log.e(TAG, String.format(
1222 "transactionId %d for %d has no client mapping", transactionId, code));
Hugo Benichif0c84092017-04-05 14:43:29 +09001223 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001224 }
1225
1226 /* This goes in response as msg.arg2 */
Paul Hud44e1b72023-06-16 02:07:42 +00001227 int clientRequestId = clientInfo.getClientRequestId(transactionId);
1228 if (clientRequestId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -07001229 // This can happen because of race conditions. For example,
1230 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
1231 // and we may get in this situation.
Paul Hud44e1b72023-06-16 02:07:42 +00001232 Log.d(TAG, String.format("%d for transactionId %d that is no longer active",
1233 code, transactionId));
Hugo Benichif0c84092017-04-05 14:43:29 +09001234 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001235 }
Paul Hu812e9212023-06-20 06:24:53 +00001236 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1237 if (request == null) {
1238 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1239 return false;
1240 }
Hugo Benichi32be63d2017-04-05 14:06:11 +09001241 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00001242 Log.d(TAG, String.format(
1243 "MDns service event code:%d transactionId=%d", code, transactionId));
Hugo Benichi32be63d2017-04-05 14:06:11 +09001244 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001245 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +08001246 case IMDnsEventListener.SERVICE_FOUND: {
1247 final DiscoveryInfo info = (DiscoveryInfo) obj;
1248 final String name = info.serviceName;
1249 final String type = info.registrationType;
1250 servInfo = new NsdServiceInfo(name, type);
1251 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001252 if (foundNetId == 0L) {
1253 // Ignore services that do not have a Network: they are not usable
1254 // by apps, as they would need privileged permissions to use
1255 // interfaces that do not have an associated Network.
1256 break;
1257 }
Remi NGUYEN VAN643edb62023-01-23 19:14:57 +09001258 if (foundNetId == INetd.DUMMY_NET_ID) {
1259 // Ignore services on the dummy0 interface: they are only seen when
1260 // discovering locally advertised services, and are not reachable
1261 // through that interface.
1262 break;
1263 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001264 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001265
1266 clientInfo.onServiceFound(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001267 break;
paulhu2b9ed952022-02-10 21:58:32 +08001268 }
1269 case IMDnsEventListener.SERVICE_LOST: {
1270 final DiscoveryInfo info = (DiscoveryInfo) obj;
1271 final String name = info.serviceName;
1272 final String type = info.registrationType;
1273 final int lostNetId = info.netId;
1274 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001275 // The network could be set to null (netId 0) if it was torn down when the
1276 // service is lost
1277 // TODO: avoid returning null in that case, possibly by remembering
1278 // found services on the same interface index and their network at the time
1279 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001280 clientInfo.onServiceLost(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001281 break;
paulhu2b9ed952022-02-10 21:58:32 +08001282 }
1283 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Paul Hu812e9212023-06-20 06:24:53 +00001284 clientInfo.onDiscoverServicesFailed(clientRequestId,
1285 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
1286 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001287 break;
paulhu2b9ed952022-02-10 21:58:32 +08001288 case IMDnsEventListener.SERVICE_REGISTERED: {
1289 final RegistrationInfo info = (RegistrationInfo) obj;
1290 final String name = info.serviceName;
1291 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Paul Hu777ed052023-06-19 13:35:15 +00001292 clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo,
Paul Hu812e9212023-06-20 06:24:53 +00001293 transactionId,
1294 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001295 break;
paulhu2b9ed952022-02-10 21:58:32 +08001296 }
1297 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Paul Hu777ed052023-06-19 13:35:15 +00001298 clientInfo.onRegisterServiceFailed(clientRequestId,
1299 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001300 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001301 break;
paulhu2b9ed952022-02-10 21:58:32 +08001302 case IMDnsEventListener.SERVICE_RESOLVED: {
1303 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001304 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +08001305 final String fullName = info.serviceFullName;
1306 while (index < fullName.length() && fullName.charAt(index) != '.') {
1307 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001308 ++index;
1309 }
1310 ++index;
1311 }
paulhu2b9ed952022-02-10 21:58:32 +08001312 if (index >= fullName.length()) {
1313 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001314 break;
1315 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001316
paulhube186602022-04-12 07:18:23 +00001317 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +08001318 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001319 String type = rest.replace(".local.", "");
1320
Paul Hu30bd70d2023-02-07 13:20:56 +00001321 final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
Paul Hu18aeccc2022-12-27 08:48:48 +00001322 serviceInfo.setServiceName(name);
1323 serviceInfo.setServiceType(type);
1324 serviceInfo.setPort(info.port);
1325 serviceInfo.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001326 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001327
Paul Hud44e1b72023-06-16 02:07:42 +00001328 stopResolveService(transactionId);
1329 removeRequestMap(clientRequestId, transactionId, clientInfo);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001330
Paul Hud44e1b72023-06-16 02:07:42 +00001331 final int transactionId2 = getUniqueId();
1332 if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
1333 storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
Paul Hua6bc4632023-06-26 01:18:29 +00001334 NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001335 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001336 clientInfo.onResolveServiceFailed(clientRequestId,
1337 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
1338 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001339 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001340 }
1341 break;
paulhu2b9ed952022-02-10 21:58:32 +08001342 }
1343 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001344 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001345 stopResolveService(transactionId);
1346 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001347 clientInfo.onResolveServiceFailed(clientRequestId,
1348 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
1349 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001350 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001351 break;
paulhu2b9ed952022-02-10 21:58:32 +08001352 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001353 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001354 stopGetAddrInfo(transactionId);
1355 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001356 clientInfo.onResolveServiceFailed(clientRequestId,
1357 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
1358 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001359 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001360 break;
paulhu2b9ed952022-02-10 21:58:32 +08001361 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001362 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +08001363 final GetAddressInfo info = (GetAddressInfo) obj;
1364 final String address = info.address;
1365 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001366 InetAddress serviceHost = null;
1367 try {
paulhu2b9ed952022-02-10 21:58:32 +08001368 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001369 } catch (UnknownHostException e) {
1370 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
1371 }
1372
1373 // If the resolved service is on an interface without a network, consider it
1374 // as a failure: it would not be usable by apps as they would need
1375 // privileged permissions.
Paul Hu30bd70d2023-02-07 13:20:56 +00001376 if (netId != NETID_UNSET && serviceHost != null) {
1377 clientInfo.mResolvedService.setHost(serviceHost);
1378 setServiceNetworkForCallback(clientInfo.mResolvedService,
1379 netId, info.interfaceIdx);
1380 clientInfo.onResolveServiceSucceeded(
Paul Hua6bc4632023-06-26 01:18:29 +00001381 clientRequestId, clientInfo.mResolvedService, request);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001382 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001383 clientInfo.onResolveServiceFailed(clientRequestId,
1384 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
1385 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001386 }
Paul Hud44e1b72023-06-16 02:07:42 +00001387 stopGetAddrInfo(transactionId);
1388 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001389 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001390 break;
paulhu2b9ed952022-02-10 21:58:32 +08001391 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001392 default:
Hugo Benichif0c84092017-04-05 14:43:29 +09001393 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001394 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001395 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001396 }
Paul Hu019621e2023-01-13 23:26:49 +08001397
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001398 @Nullable
1399 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
1400 final MdnsEvent event, int code) {
Paul Hu019621e2023-01-13 23:26:49 +08001401 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001402 final String[] typeArray = serviceInfo.getServiceType();
1403 final String joinedType;
1404 if (typeArray.length == 0
1405 || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
1406 Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
1407 + Arrays.toString(typeArray));
1408 return null;
1409 } else {
1410 joinedType = TextUtils.join(".",
1411 Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
1412 }
1413 final String serviceType;
1414 switch (code) {
1415 case NsdManager.SERVICE_FOUND:
1416 case NsdManager.SERVICE_LOST:
1417 // For consistency with historical behavior, discovered service types have
1418 // a dot at the end.
1419 serviceType = joinedType + ".";
1420 break;
1421 case RESOLVE_SERVICE_SUCCEEDED:
1422 // For consistency with historical behavior, resolved service types have
1423 // a dot at the beginning.
1424 serviceType = "." + joinedType;
1425 break;
1426 default:
1427 serviceType = joinedType;
1428 break;
1429 }
Paul Hu019621e2023-01-13 23:26:49 +08001430 final String serviceName = serviceInfo.getServiceInstanceName();
1431 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
1432 final Network network = serviceInfo.getNetwork();
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001433 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
1434 // network for Tethering interface. In other words, the network == null means the
1435 // network has netId = INetd.LOCAL_NET_ID.
Paul Hu019621e2023-01-13 23:26:49 +08001436 setServiceNetworkForCallback(
1437 servInfo,
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001438 network == null ? INetd.LOCAL_NET_ID : network.netId,
Paul Hu019621e2023-01-13 23:26:49 +08001439 serviceInfo.getInterfaceIndex());
1440 return servInfo;
1441 }
1442
1443 private boolean handleMdnsDiscoveryManagerEvent(
1444 int transactionId, int code, Object obj) {
Paul Hud44e1b72023-06-16 02:07:42 +00001445 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Paul Hu019621e2023-01-13 23:26:49 +08001446 if (clientInfo == null) {
1447 Log.e(TAG, String.format(
1448 "id %d for %d has no client mapping", transactionId, code));
1449 return false;
1450 }
1451
1452 final MdnsEvent event = (MdnsEvent) obj;
Paul Hud44e1b72023-06-16 02:07:42 +00001453 final int clientRequestId = event.mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +08001454 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1455 if (request == null) {
1456 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1457 return false;
1458 }
1459
1460 // Deal with the discovery sent callback
1461 if (code == DISCOVERY_QUERY_SENT_CALLBACK) {
1462 request.onQuerySent();
1463 return true;
1464 }
1465
1466 // Deal with other callbacks.
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001467 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
1468 // Errors are already logged if null
1469 if (info == null) return false;
Paul Hu83ec7f42023-06-07 18:04:09 +08001470 mServiceLogs.log(String.format(
1471 "MdnsDiscoveryManager event code=%s transactionId=%d",
1472 NsdManager.nameOf(code), transactionId));
Paul Hu019621e2023-01-13 23:26:49 +08001473 switch (code) {
1474 case NsdManager.SERVICE_FOUND:
Paul Hu812e9212023-06-20 06:24:53 +00001475 clientInfo.onServiceFound(clientRequestId, info, request);
Paul Hu319751a2023-01-13 23:56:34 +08001476 break;
1477 case NsdManager.SERVICE_LOST:
Paul Hu812e9212023-06-20 06:24:53 +00001478 clientInfo.onServiceLost(clientRequestId, info, request);
Paul Hu019621e2023-01-13 23:26:49 +08001479 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001480 case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
1481 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Paul Hu75069ed2023-01-14 00:31:09 +08001482 info.setPort(serviceInfo.getPort());
1483
1484 Map<String, String> attrs = serviceInfo.getAttributes();
1485 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1486 final String key = kv.getKey();
1487 try {
1488 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1489 } catch (IllegalArgumentException e) {
1490 Log.e(TAG, "Invalid attribute", e);
1491 }
1492 }
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001493 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu2b865912023-03-06 14:27:53 +08001494 if (addresses.size() != 0) {
1495 info.setHostAddresses(addresses);
Paul Hua6bc4632023-06-26 01:18:29 +00001496 request.setServiceFromCache(event.mIsServiceFromCache);
1497 clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
Paul Hu2b865912023-03-06 14:27:53 +08001498 } else {
1499 // No address. Notify resolution failure.
Paul Hua6bc4632023-06-26 01:18:29 +00001500 clientInfo.onResolveServiceFailed(clientRequestId,
1501 NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
1502 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu75069ed2023-01-14 00:31:09 +08001503 }
1504
1505 // Unregister the listener immediately like IMDnsEventListener design
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001506 if (!(request instanceof DiscoveryManagerRequest)) {
1507 Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event");
1508 break;
1509 }
Paul Hud44e1b72023-06-16 02:07:42 +00001510 stopDiscoveryManagerRequest(
1511 request, clientRequestId, transactionId, clientInfo);
Paul Hu75069ed2023-01-14 00:31:09 +08001512 break;
1513 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001514 case NsdManager.SERVICE_UPDATED: {
1515 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1516 info.setPort(serviceInfo.getPort());
1517
1518 Map<String, String> attrs = serviceInfo.getAttributes();
1519 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1520 final String key = kv.getKey();
1521 try {
1522 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1523 } catch (IllegalArgumentException e) {
1524 Log.e(TAG, "Invalid attribute", e);
1525 }
1526 }
1527
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001528 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001529 info.setHostAddresses(addresses);
Paul Huddce5912023-08-01 10:26:49 +08001530 clientInfo.onServiceUpdated(clientRequestId, info, request);
1531 // Set the ServiceFromCache flag only if the service is actually being
1532 // retrieved from the cache. This flag should not be overridden by later
1533 // service updates, which may not be cached.
1534 if (event.mIsServiceFromCache) {
1535 request.setServiceFromCache(true);
1536 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001537 break;
1538 }
1539 case NsdManager.SERVICE_UPDATED_LOST:
Paul Huddce5912023-08-01 10:26:49 +08001540 clientInfo.onServiceUpdatedLost(clientRequestId, request);
Paul Hu30bd70d2023-02-07 13:20:56 +00001541 break;
Paul Hu019621e2023-01-13 23:26:49 +08001542 default:
1543 return false;
1544 }
1545 return true;
1546 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001547 }
1548 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001549
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001550 @NonNull
1551 private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
1552 final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
1553 final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
1554 final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
1555 for (String ipv4Address : v4Addrs) {
1556 try {
1557 addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
1558 } catch (IllegalArgumentException e) {
1559 Log.wtf(TAG, "Invalid ipv4 address", e);
1560 }
1561 }
1562 for (String ipv6Address : v6Addrs) {
1563 try {
Yuyang Huanga6a6ff92023-04-24 13:33:34 +09001564 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
1565 ipv6Address);
1566 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
1567 } catch (IllegalArgumentException e) {
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001568 Log.wtf(TAG, "Invalid ipv6 address", e);
1569 }
1570 }
1571 return addresses;
1572 }
1573
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001574 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
1575 switch (netId) {
1576 case NETID_UNSET:
1577 info.setNetwork(null);
1578 break;
1579 case INetd.LOCAL_NET_ID:
1580 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
1581 // visible / usable for apps, so do not return it. Store the interface
1582 // index instead, so at least if the client tries to resolve the service
1583 // with that NsdServiceInfo, it will be done on the same interface.
1584 // If they recreate the NsdServiceInfo themselves, resolution would be
1585 // done on all interfaces as before T, which should also work.
1586 info.setNetwork(null);
1587 info.setInterfaceIndex(ifaceIdx);
1588 break;
1589 default:
1590 info.setNetwork(new Network(netId));
1591 }
1592 }
1593
paulhube186602022-04-12 07:18:23 +00001594 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
1595 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
1596 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
1597 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
1598 private String unescape(String s) {
1599 StringBuilder sb = new StringBuilder(s.length());
1600 for (int i = 0; i < s.length(); ++i) {
1601 char c = s.charAt(i);
1602 if (c == '\\') {
1603 if (++i >= s.length()) {
1604 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1605 break;
1606 }
1607 c = s.charAt(i);
1608 if (c != '.' && c != '\\') {
1609 if (i + 2 >= s.length()) {
1610 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1611 break;
1612 }
1613 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
1614 + (s.charAt(i + 2) - '0'));
1615 i += 2;
1616 }
1617 }
1618 sb.append(c);
1619 }
1620 return sb.toString();
1621 }
1622
Paul Hu7445e3d2023-03-03 15:14:00 +08001623 /**
1624 * Check the given service type is valid and construct it to a service type
1625 * which can use for discovery / resolution service.
1626 *
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001627 * <p>The valid service type should be 2 labels, or 3 labels if the query is for a
Paul Hu7445e3d2023-03-03 15:14:00 +08001628 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
1629 * underscore; they are alphanumerical characters or dashes or underscore, except the
1630 * last one that is just alphanumerical. The last label must be _tcp or _udp.
1631 *
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001632 * <p>The subtype may also be specified with a comma after the service type, for example
1633 * _type._tcp,_subtype.
1634 *
Paul Hu7445e3d2023-03-03 15:14:00 +08001635 * @param serviceType the request service type for discovery / resolution service
1636 * @return constructed service type or null if the given service type is invalid.
1637 */
1638 @Nullable
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001639 public static Pair<String, String> parseTypeAndSubtype(String serviceType) {
Paul Hu7445e3d2023-03-03 15:14:00 +08001640 if (TextUtils.isEmpty(serviceType)) return null;
1641
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001642 final String typeOrSubtypePattern = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]";
Paul Hu7445e3d2023-03-03 15:14:00 +08001643 final Pattern serviceTypePattern = Pattern.compile(
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001644 // Optional leading subtype (_subtype._type._tcp)
1645 // (?: xxx) is a non-capturing parenthesis, don't capture the dot
1646 "^(?:(" + typeOrSubtypePattern + ")\\.)?"
1647 // Actual type (_type._tcp.local)
1648 + "(" + typeOrSubtypePattern + "\\._(?:tcp|udp))"
Paul Hu7445e3d2023-03-03 15:14:00 +08001649 // Drop '.' at the end of service type that is compatible with old backend.
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001650 // e.g. allow "_type._tcp.local."
1651 + "\\.?"
1652 // Optional subtype after comma, for "_type._tcp,_subtype" format
1653 + "(?:,(" + typeOrSubtypePattern + "))?"
1654 + "$");
Paul Hu7445e3d2023-03-03 15:14:00 +08001655 final Matcher matcher = serviceTypePattern.matcher(serviceType);
1656 if (!matcher.matches()) return null;
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001657 // Use the subtype either at the beginning or after the comma
1658 final String subtype = matcher.group(1) != null ? matcher.group(1) : matcher.group(3);
1659 return new Pair<>(matcher.group(2), subtype);
Paul Hu7445e3d2023-03-03 15:14:00 +08001660 }
1661
Hugo Benichi803a2f02017-04-24 11:35:06 +09001662 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +08001663 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Paul Hu4bd98ef2023-01-12 13:42:07 +08001664 this(ctx, handler, cleanupDelayMs, new Dependencies());
1665 }
1666
1667 @VisibleForTesting
1668 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
Luke Huang05298582021-06-13 16:52:05 +00001669 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001670 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001671 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -07001672 mNsdStateMachine.start();
paulhu2b9ed952022-02-10 21:58:32 +08001673 mMDnsManager = ctx.getSystemService(MDnsManager.class);
1674 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001675 mDeps = deps;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001676
Paul Hu14667de2023-04-17 22:42:47 +08001677 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001678 LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor());
Yuyang Huang700778b2023-03-08 16:17:05 +09001679 // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
1680 // address events are received.
1681 handler.post(mMdnsSocketProvider::startNetLinkMonitor);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001682
1683 // NsdService is started after ActivityManager (startOtherServices in SystemServer, vs.
1684 // startBootstrapServices).
1685 mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt(
1686 MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF,
1687 DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF);
1688 final ActivityManager am = ctx.getSystemService(ActivityManager.class);
1689 am.addOnUidImportanceListener(new UidImportanceListener(handler),
1690 mRunningAppActiveImportanceCutoff);
1691
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001692 mMdnsSocketClient =
Yuyang Huang7ddf2932023-08-01 18:16:30 +09001693 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
1694 LOGGER.forSubComponent("MdnsMultinetworkSocketClient"));
Paul Hu14667de2023-04-17 22:42:47 +08001695 mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
Yuyang Huang243d1a52023-05-23 17:26:52 +09001696 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001697 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
1698 mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
Paul Hu14667de2023-04-17 22:42:47 +08001699 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"));
Paul Hu777ed052023-06-19 13:35:15 +00001700 mClock = deps.makeClock();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001701 }
1702
1703 /**
1704 * Dependencies of NsdService, for injection in tests.
1705 */
1706 @VisibleForTesting
1707 public static class Dependencies {
1708 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001709 * Check whether the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001710 *
1711 * @param context The global context information about an app environment.
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001712 * @return true if the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001713 */
1714 public boolean isMdnsDiscoveryManagerEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001715 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
1716 NAMESPACE_TETHERING, MDNS_DISCOVERY_MANAGER_VERSION,
1717 DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001718 }
1719
1720 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001721 * Check whether the MdnsAdvertiser feature is enabled.
1722 *
1723 * @param context The global context information about an app environment.
1724 * @return true if the MdnsAdvertiser feature is enabled.
1725 */
1726 public boolean isMdnsAdvertiserEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001727 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
1728 NAMESPACE_TETHERING, MDNS_ADVERTISER_VERSION,
1729 DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001730 }
1731
1732 /**
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001733 * Get the type allowlist flag value.
1734 * @see #MDNS_TYPE_ALLOWLIST_FLAGS
1735 */
1736 @Nullable
1737 public String getTypeAllowlistFlags() {
1738 return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
1739 MDNS_TYPE_ALLOWLIST_FLAGS, null);
1740 }
1741
1742 /**
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001743 * @see DeviceConfigUtils#isTetheringFeatureEnabled
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001744 */
1745 public boolean isFeatureEnabled(Context context, String feature) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001746 return DeviceConfigUtils.isTetheringFeatureEnabled(context, NAMESPACE_TETHERING,
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001747 feature, DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
1748 }
1749
1750 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001751 * @see MdnsDiscoveryManager
1752 */
1753 public MdnsDiscoveryManager makeMdnsDiscoveryManager(
Paul Hu14667de2023-04-17 22:42:47 +08001754 @NonNull ExecutorProvider executorProvider,
Yuyang Huang243d1a52023-05-23 17:26:52 +09001755 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog) {
1756 return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001757 }
1758
1759 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001760 * @see MdnsAdvertiser
1761 */
1762 public MdnsAdvertiser makeMdnsAdvertiser(
1763 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
Paul Hu14667de2023-04-17 22:42:47 +08001764 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog) {
1765 return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001766 }
1767
1768 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001769 * @see MdnsSocketProvider
1770 */
Paul Hu14667de2023-04-17 22:42:47 +08001771 public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001772 @NonNull Looper looper, @NonNull SharedLog sharedLog,
1773 @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) {
1774 return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback);
1775 }
1776
1777 /**
1778 * @see DeviceConfig#getInt(String, String, int)
1779 */
1780 public int getDeviceConfigInt(@NonNull String config, int defaultValue) {
1781 return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue);
1782 }
1783
1784 /**
1785 * @see Binder#getCallingUid()
1786 */
1787 public int getCallingUid() {
1788 return Binder.getCallingUid();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001789 }
Paul Hu777ed052023-06-19 13:35:15 +00001790
1791 /**
1792 * @see NetworkNsdReportedMetrics
1793 */
1794 public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(
1795 boolean isLegacy, int clientId) {
1796 return new NetworkNsdReportedMetrics(isLegacy, clientId);
1797 }
1798
1799 /**
1800 * @see MdnsUtils.Clock
1801 */
1802 public Clock makeClock() {
1803 return new Clock();
1804 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001805 }
1806
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001807 /**
1808 * Return whether a type is allowlisted to use the Java backend.
1809 * @param type The service type
1810 * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
1811 * {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
1812 */
1813 private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
1814 @NonNull String flagPrefix) {
1815 if (type == null) return false;
1816 final String typesConfig = mDeps.getTypeAllowlistFlags();
1817 if (TextUtils.isEmpty(typesConfig)) return false;
1818
1819 final String mappingPrefix = type + ":";
1820 String mappedFlag = null;
1821 for (String mapping : TextUtils.split(typesConfig, ",")) {
1822 if (mapping.startsWith(mappingPrefix)) {
1823 mappedFlag = mapping.substring(mappingPrefix.length());
1824 break;
1825 }
1826 }
1827
1828 if (mappedFlag == null) return false;
1829
1830 return mDeps.isFeatureEnabled(mContext,
1831 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
1832 }
1833
1834 private boolean useDiscoveryManagerForType(@Nullable String type) {
1835 return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
1836 }
1837
1838 private boolean useAdvertiserForType(@Nullable String type) {
1839 return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
1840 }
1841
paulhu1b35e822022-04-08 14:48:41 +08001842 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09001843 HandlerThread thread = new HandlerThread(TAG);
1844 thread.start();
1845 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +08001846 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001847 return service;
1848 }
1849
paulhu2b9ed952022-02-10 21:58:32 +08001850 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
1851 private final StateMachine mStateMachine;
1852
1853 MDnsEventCallback(StateMachine sm) {
1854 mStateMachine = sm;
1855 }
1856
1857 @Override
1858 public void onServiceRegistrationStatus(final RegistrationInfo status) {
1859 mStateMachine.sendMessage(
1860 MDNS_SERVICE_EVENT, status.result, status.id, status);
1861 }
1862
1863 @Override
1864 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
1865 mStateMachine.sendMessage(
1866 MDNS_SERVICE_EVENT, status.result, status.id, status);
1867 }
1868
1869 @Override
1870 public void onServiceResolutionStatus(final ResolutionInfo status) {
1871 mStateMachine.sendMessage(
1872 MDNS_SERVICE_EVENT, status.result, status.id, status);
1873 }
1874
1875 @Override
1876 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
1877 mStateMachine.sendMessage(
1878 MDNS_SERVICE_EVENT, status.result, status.id, status);
1879 }
1880
1881 @Override
1882 public int getInterfaceVersion() throws RemoteException {
1883 return this.VERSION;
1884 }
1885
1886 @Override
1887 public String getInterfaceHash() throws RemoteException {
1888 return this.HASH;
1889 }
1890 }
1891
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001892 private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) {
1893 final String targetInterface = offloadEngineInfo.mInterfaceName;
1894 final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine;
1895 final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers =
1896 mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface);
1897 for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) {
1898 try {
1899 offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo);
1900 } catch (RemoteException e) {
1901 // Can happen in regular cases, do not log a stacktrace
1902 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
1903 }
1904 }
1905 }
1906
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001907 private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
1908 @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
1909 final int count = mOffloadEngines.beginBroadcast();
1910 try {
1911 for (int i = 0; i < count; i++) {
1912 final OffloadEngineInfo offloadEngineInfo =
1913 (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
1914 final String interfaceName = offloadEngineInfo.mInterfaceName;
1915 if (!targetInterfaceName.equals(interfaceName)
1916 || ((offloadEngineInfo.mOffloadType
1917 & offloadServiceInfo.getOffloadType()) == 0)) {
1918 continue;
1919 }
1920 try {
1921 if (isRemove) {
1922 mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
1923 offloadServiceInfo);
1924 } else {
1925 mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
1926 offloadServiceInfo);
1927 }
1928 } catch (RemoteException e) {
1929 // Can happen in regular cases, do not log a stacktrace
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001930 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001931 }
1932 }
1933 } finally {
1934 mOffloadEngines.finishBroadcast();
1935 }
1936 }
1937
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001938 private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001939 // TODO: add a callback to notify when a service is being added on each interface (as soon
1940 // as probing starts), and call mOffloadCallbacks. This callback is for
1941 // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
1942
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001943 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00001944 public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
1945 mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
1946 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001947 if (clientInfo == null) return;
1948
Paul Hud44e1b72023-06-16 02:07:42 +00001949 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
1950 if (clientRequestId < 0) return;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001951
1952 // onRegisterServiceSucceeded only has the service name in its info. This aligns with
1953 // historical behavior.
1954 final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
Paul Hu777ed052023-06-19 13:35:15 +00001955 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu812e9212023-06-20 06:24:53 +00001956 clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, transactionId,
1957 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001958 }
1959
1960 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00001961 public void onRegisterServiceFailed(int transactionId, int errorCode) {
1962 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001963 if (clientInfo == null) return;
1964
Paul Hud44e1b72023-06-16 02:07:42 +00001965 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
1966 if (clientRequestId < 0) return;
Paul Hu777ed052023-06-19 13:35:15 +00001967 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1968 clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001969 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001970 }
1971
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001972 @Override
1973 public void onOffloadStartOrUpdate(@NonNull String interfaceName,
1974 @NonNull OffloadServiceInfo offloadServiceInfo) {
1975 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
1976 }
1977
1978 @Override
1979 public void onOffloadStop(@NonNull String interfaceName,
1980 @NonNull OffloadServiceInfo offloadServiceInfo) {
1981 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
1982 }
1983
Paul Hud44e1b72023-06-16 02:07:42 +00001984 private ClientInfo getClientInfoOrLog(int transactionId) {
1985 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001986 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001987 Log.e(TAG, String.format("Callback for service %d has no client", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001988 }
1989 return clientInfo;
1990 }
1991
Paul Hud44e1b72023-06-16 02:07:42 +00001992 private int getClientRequestIdOrLog(@NonNull ClientInfo info, int transactionId) {
1993 final int clientRequestId = info.getClientRequestId(transactionId);
1994 if (clientRequestId < 0) {
1995 Log.e(TAG, String.format(
1996 "Client request ID not found for service %d", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001997 }
Paul Hud44e1b72023-06-16 02:07:42 +00001998 return clientRequestId;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001999 }
2000 }
2001
Paul Hu2e0a88c2023-03-09 16:05:01 +08002002 private static class ConnectorArgs {
2003 @NonNull public final NsdServiceConnector connector;
2004 @NonNull public final INsdManagerCallback callback;
2005 public final boolean useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002006 public final int uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002007
2008 ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
Paul Hub2e67d32023-04-18 05:50:14 +00002009 boolean useJavaBackend, int uid) {
Paul Hu2e0a88c2023-03-09 16:05:01 +08002010 this.connector = connector;
2011 this.callback = callback;
2012 this.useJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002013 this.uid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002014 }
2015 }
2016
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002017 @Override
Paul Hu2e0a88c2023-03-09 16:05:01 +08002018 public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09002019 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Paul Hu101dbf52023-08-09 16:05:20 +08002020 final int uid = mDeps.getCallingUid();
2021 if (cb == null) {
2022 throw new IllegalArgumentException("Unknown client callback from uid=" + uid);
2023 }
Paul Hu2e0a88c2023-03-09 16:05:01 +08002024 if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002025 final INsdServiceConnector connector = new NsdServiceConnector();
Paul Hub2e67d32023-04-18 05:50:14 +00002026 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
Paul Hu101dbf52023-08-09 16:05:20 +08002027 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002028 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -07002029 }
2030
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002031 private static class ListenerArgs {
2032 public final NsdServiceConnector connector;
2033 public final NsdServiceInfo serviceInfo;
2034 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
2035 this.connector = connector;
2036 this.serviceInfo = serviceInfo;
2037 }
2038 }
2039
2040 private class NsdServiceConnector extends INsdServiceConnector.Stub
2041 implements IBinder.DeathRecipient {
2042 @Override
2043 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
2044 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2045 NsdManager.REGISTER_SERVICE, 0, listenerKey,
2046 new ListenerArgs(this, serviceInfo)));
2047 }
2048
2049 @Override
2050 public void unregisterService(int listenerKey) {
2051 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2052 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
2053 new ListenerArgs(this, null)));
2054 }
2055
2056 @Override
2057 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
2058 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2059 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
2060 new ListenerArgs(this, serviceInfo)));
2061 }
2062
2063 @Override
2064 public void stopDiscovery(int listenerKey) {
2065 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2066 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
2067 }
2068
2069 @Override
2070 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
2071 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2072 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
2073 new ListenerArgs(this, serviceInfo)));
2074 }
2075
2076 @Override
Paul Hub58deb72022-12-26 09:24:42 +00002077 public void stopResolution(int listenerKey) {
2078 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2079 NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null)));
2080 }
2081
2082 @Override
Paul Hu18aeccc2022-12-27 08:48:48 +00002083 public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
2084 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2085 NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
2086 new ListenerArgs(this, serviceInfo)));
2087 }
2088
2089 @Override
2090 public void unregisterServiceInfoCallback(int listenerKey) {
2091 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2092 NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
2093 new ListenerArgs(this, null)));
2094 }
2095
2096 @Override
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002097 public void startDaemon() {
2098 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2099 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
2100 }
2101
2102 @Override
2103 public void binderDied() {
2104 mNsdStateMachine.sendMessage(
2105 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002106
2107 }
2108
2109 @Override
2110 public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
2111 @OffloadEngine.OffloadCapability long offloadCapabilities,
2112 @OffloadEngine.OffloadType long offloadTypes) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002113 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002114 Objects.requireNonNull(ifaceName);
2115 Objects.requireNonNull(cb);
2116 mNsdStateMachine.sendMessage(
2117 mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
2118 new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
2119 offloadTypes)));
2120 }
2121
2122 @Override
2123 public void unregisterOffloadEngine(IOffloadEngine cb) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002124 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002125 Objects.requireNonNull(cb);
2126 mNsdStateMachine.sendMessage(
2127 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002128 }
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002129
2130 private static void checkOffloadEnginePermission(Context context) {
2131 if (!SdkLevel.isAtLeastT()) {
2132 throw new SecurityException("API is not available in before API level 33");
2133 }
2134 // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V, but may
2135 // be back ported to older builds: accept it as long as it's signature-protected
2136 if (PermissionUtils.checkAnyPermissionOf(context, REGISTER_NSD_OFFLOAD_ENGINE)
2137 && (SdkLevel.isAtLeastV() || PermissionUtils.isSystemSignaturePermission(
2138 context, REGISTER_NSD_OFFLOAD_ENGINE))) {
2139 return;
2140 }
2141 if (PermissionUtils.checkAnyPermissionOf(context, NETWORK_STACK,
2142 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)) {
2143 return;
2144 }
2145 throw new SecurityException("Requires one of the following permissions: "
2146 + String.join(", ", List.of(REGISTER_NSD_OFFLOAD_ENGINE, NETWORK_STACK,
2147 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)) + ".");
2148 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002149 }
2150
Hugo Benichi912db992017-04-24 16:41:03 +09002151 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -07002152 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -07002153 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +09002154 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
2155 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -07002156 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -07002157 }
2158
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002159 private int getUniqueId() {
2160 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
2161 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002162 }
2163
Paul Hud44e1b72023-06-16 02:07:42 +00002164 private boolean registerService(int transactionId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +09002165 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002166 Log.d(TAG, "registerService: " + transactionId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002167 }
Hugo Benichi6d706442017-04-24 16:19:58 +09002168 String name = service.getServiceName();
2169 String type = service.getServiceType();
2170 int port = service.getPort();
2171 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002172 final int registerInterface = getNetworkInterfaceIndex(service);
2173 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +08002174 Log.e(TAG, "Interface to register service on not found");
2175 return false;
2176 }
Paul Hud44e1b72023-06-16 02:07:42 +00002177 return mMDnsManager.registerService(
2178 transactionId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002179 }
2180
Paul Hud44e1b72023-06-16 02:07:42 +00002181 private boolean unregisterService(int transactionId) {
2182 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002183 }
2184
Paul Hud44e1b72023-06-16 02:07:42 +00002185 private boolean discoverServices(int transactionId, NsdServiceInfo serviceInfo) {
paulhu2b9ed952022-02-10 21:58:32 +08002186 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002187 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
2188 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002189 Log.e(TAG, "Interface to discover service on not found");
2190 return false;
2191 }
Paul Hud44e1b72023-06-16 02:07:42 +00002192 return mMDnsManager.discover(transactionId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002193 }
2194
Paul Hud44e1b72023-06-16 02:07:42 +00002195 private boolean stopServiceDiscovery(int transactionId) {
2196 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002197 }
2198
Paul Hud44e1b72023-06-16 02:07:42 +00002199 private boolean resolveService(int transactionId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002200 final String name = service.getServiceName();
2201 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002202 final int resolveInterface = getNetworkInterfaceIndex(service);
2203 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002204 Log.e(TAG, "Interface to resolve service on not found");
2205 return false;
2206 }
Paul Hud44e1b72023-06-16 02:07:42 +00002207 return mMDnsManager.resolve(transactionId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002208 }
2209
2210 /**
2211 * Guess the interface to use to resolve or discover a service on a specific network.
2212 *
2213 * This is an imperfect guess, as for example the network may be gone or not yet fully
2214 * registered. This is fine as failing is correct if the network is gone, and a client
2215 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
2216 * this is to support the legacy mdnsresponder implementation, which historically resolved
2217 * services on an unspecified network.
2218 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002219 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
2220 final Network network = serviceInfo.getNetwork();
2221 if (network == null) {
2222 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
2223 // provided by NsdService from discovery results, and the service was found on an
2224 // interface that has no app-usable Network).
2225 if (serviceInfo.getInterfaceIndex() != 0) {
2226 return serviceInfo.getInterfaceIndex();
2227 }
2228 return IFACE_IDX_ANY;
2229 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002230
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002231 String interfaceName = getNetworkInterfaceName(network);
2232 if (interfaceName == null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002233 return IFACE_IDX_ANY;
2234 }
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002235 return getNetworkInterfaceIndexByName(interfaceName);
2236 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002237
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002238 private String getNetworkInterfaceName(@Nullable Network network) {
2239 if (network == null) {
2240 return null;
2241 }
2242 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
2243 if (cm == null) {
2244 Log.wtf(TAG, "No ConnectivityManager");
2245 return null;
2246 }
2247 final LinkProperties lp = cm.getLinkProperties(network);
2248 if (lp == null) {
2249 return null;
2250 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002251 // Only resolve on non-stacked interfaces
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002252 return lp.getInterfaceName();
2253 }
2254
2255 private int getNetworkInterfaceIndexByName(final String ifaceName) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002256 final NetworkInterface iface;
2257 try {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002258 iface = NetworkInterface.getByName(ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002259 } catch (SocketException e) {
2260 Log.e(TAG, "Error querying interface", e);
2261 return IFACE_IDX_ANY;
2262 }
2263
2264 if (iface == null) {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002265 Log.e(TAG, "Interface not found: " + ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002266 return IFACE_IDX_ANY;
2267 }
2268
2269 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002270 }
2271
Paul Hud44e1b72023-06-16 02:07:42 +00002272 private boolean stopResolveService(int transactionId) {
2273 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002274 }
2275
Paul Hud44e1b72023-06-16 02:07:42 +00002276 private boolean getAddrInfo(int transactionId, String hostname, int interfaceIdx) {
2277 return mMDnsManager.getServiceAddress(transactionId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002278 }
2279
Paul Hud44e1b72023-06-16 02:07:42 +00002280 private boolean stopGetAddrInfo(int transactionId) {
2281 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002282 }
2283
2284 @Override
Paul Hub2e67d32023-04-18 05:50:14 +00002285 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2286 if (!PermissionUtils.checkDumpPermission(mContext, TAG, writer)) return;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002287
Paul Hub2e67d32023-04-18 05:50:14 +00002288 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
2289 // Dump state machine logs
Irfan Sheriff75006652012-04-17 23:15:29 -07002290 mNsdStateMachine.dump(fd, pw, args);
Paul Hub2e67d32023-04-18 05:50:14 +00002291
2292 // Dump service and clients logs
2293 pw.println();
Paul Hu14667de2023-04-17 22:42:47 +08002294 pw.println("Logs:");
Paul Hub2e67d32023-04-18 05:50:14 +00002295 pw.increaseIndent();
2296 mServiceLogs.reverseDump(pw);
2297 pw.decreaseIndent();
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002298 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002299
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002300 private abstract static class ClientRequest {
Paul Hud44e1b72023-06-16 02:07:42 +00002301 private final int mTransactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002302 private final long mStartTimeMs;
Paul Hu812e9212023-06-20 06:24:53 +00002303 private int mFoundServiceCount = 0;
2304 private int mLostServiceCount = 0;
2305 private final Set<String> mServices = new ArraySet<>();
Paul Hua6bc4632023-06-26 01:18:29 +00002306 private boolean mIsServiceFromCache = false;
Paul Hubad6fe92023-07-24 21:25:22 +08002307 private int mSentQueryCount = NO_SENT_QUERY_COUNT;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002308
Paul Hu812e9212023-06-20 06:24:53 +00002309 private ClientRequest(int transactionId, long startTimeMs) {
Paul Hud44e1b72023-06-16 02:07:42 +00002310 mTransactionId = transactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002311 mStartTimeMs = startTimeMs;
2312 }
2313
Paul Hu812e9212023-06-20 06:24:53 +00002314 public long calculateRequestDurationMs(long stopTimeMs) {
Paul Hu777ed052023-06-19 13:35:15 +00002315 return stopTimeMs - mStartTimeMs;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002316 }
Paul Hu812e9212023-06-20 06:24:53 +00002317
2318 public void onServiceFound(String serviceName) {
2319 mFoundServiceCount++;
2320 if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) {
2321 mServices.add(serviceName);
2322 }
2323 }
2324
2325 public void onServiceLost() {
2326 mLostServiceCount++;
2327 }
2328
2329 public int getFoundServiceCount() {
2330 return mFoundServiceCount;
2331 }
2332
2333 public int getLostServiceCount() {
2334 return mLostServiceCount;
2335 }
2336
2337 public int getServicesCount() {
2338 return mServices.size();
2339 }
Paul Hua6bc4632023-06-26 01:18:29 +00002340
2341 public void setServiceFromCache(boolean isServiceFromCache) {
2342 mIsServiceFromCache = isServiceFromCache;
2343 }
2344
2345 public boolean isServiceFromCache() {
2346 return mIsServiceFromCache;
2347 }
Paul Hubad6fe92023-07-24 21:25:22 +08002348
2349 public void onQuerySent() {
2350 mSentQueryCount++;
2351 }
2352
2353 public int getSentQueryCount() {
2354 return mSentQueryCount;
2355 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002356 }
2357
2358 private static class LegacyClientRequest extends ClientRequest {
2359 private final int mRequestCode;
2360
Paul Hu812e9212023-06-20 06:24:53 +00002361 private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) {
2362 super(transactionId, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002363 mRequestCode = requestCode;
2364 }
2365 }
2366
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002367 private abstract static class JavaBackendClientRequest extends ClientRequest {
2368 @Nullable
2369 private final Network mRequestedNetwork;
2370
Paul Hu777ed052023-06-19 13:35:15 +00002371 private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002372 long startTimeMs) {
2373 super(transactionId, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002374 mRequestedNetwork = requestedNetwork;
2375 }
2376
2377 @Nullable
2378 public Network getRequestedNetwork() {
2379 return mRequestedNetwork;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002380 }
2381 }
2382
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002383 private static class AdvertiserClientRequest extends JavaBackendClientRequest {
Paul Hu777ed052023-06-19 13:35:15 +00002384 private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002385 long startTimeMs) {
2386 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002387 }
2388 }
2389
2390 private static class DiscoveryManagerRequest extends JavaBackendClientRequest {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002391 @NonNull
2392 private final MdnsListener mListener;
2393
Paul Hud44e1b72023-06-16 02:07:42 +00002394 private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
Paul Hu812e9212023-06-20 06:24:53 +00002395 @Nullable Network requestedNetwork, long startTimeMs) {
2396 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002397 mListener = listener;
2398 }
2399 }
2400
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002401 /* Information tracked per client */
2402 private class ClientInfo {
2403
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002404 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002405 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002406 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002407 private NsdServiceInfo mResolvedService;
2408
Paul Hud44e1b72023-06-16 02:07:42 +00002409 /* A map from client request ID (listenerKey) to the request */
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002410 private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>();
Paul Hu23fa2022023-01-13 22:57:24 +08002411
Luke Huangf7277ed2021-07-12 21:15:10 +08002412 // The target SDK of this client < Build.VERSION_CODES.S
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002413 private boolean mIsPreSClient = false;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002414 private final int mUid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002415 // The flag of using java backend if the client's target SDK >= U
2416 private final boolean mUseJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002417 // Store client logs
2418 private final SharedLog mClientLogs;
Paul Hucdef3532023-06-18 14:47:35 +00002419 // Report the nsd metrics data
2420 private final NetworkNsdReportedMetrics mMetrics;
Luke Huangf7277ed2021-07-12 21:15:10 +08002421
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002422 private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
Paul Hucdef3532023-06-18 14:47:35 +00002423 SharedLog sharedLog, NetworkNsdReportedMetrics metrics) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002424 mCb = cb;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002425 mUid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002426 mUseJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002427 mClientLogs = sharedLog;
2428 mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
Paul Hucdef3532023-06-18 14:47:35 +00002429 mMetrics = metrics;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002430 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002431
2432 @Override
2433 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -06002434 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -07002435 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002436 sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002437 sb.append("mUseJavaBackend ").append(mUseJavaBackend).append("\n");
2438 sb.append("mUid ").append(mUid).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002439 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002440 int clientRequestId = mClientRequests.keyAt(i);
2441 sb.append("clientRequestId ")
2442 .append(clientRequestId)
2443 .append(" transactionId ").append(mClientRequests.valueAt(i).mTransactionId)
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002444 .append(" type ").append(
2445 mClientRequests.valueAt(i).getClass().getSimpleName())
2446 .append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -07002447 }
2448 return sb.toString();
2449 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002450
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002451 private boolean isPreSClient() {
2452 return mIsPreSClient;
Luke Huangf7277ed2021-07-12 21:15:10 +08002453 }
2454
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002455 private void setPreSClient() {
2456 mIsPreSClient = true;
Luke Huangf7277ed2021-07-12 21:15:10 +08002457 }
2458
Paul Hu812e9212023-06-20 06:24:53 +00002459 private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) {
Paul Hue4f5f252023-02-16 21:13:47 +08002460 final MdnsListener listener =
2461 ((DiscoveryManagerRequest) request).mListener;
2462 mMdnsDiscoveryManager.unregisterListener(
2463 listener.getListenedServiceType(), listener);
Paul Hu812e9212023-06-20 06:24:53 +00002464 return listener;
Paul Hue4f5f252023-02-16 21:13:47 +08002465 }
2466
Dave Plattfeff2af2014-03-07 14:48:22 -08002467 // Remove any pending requests from the global map when we get rid of a client,
2468 // and send cancellations to the daemon.
2469 private void expungeAllRequests() {
Paul Hub2e67d32023-04-18 05:50:14 +00002470 mClientLogs.log("Client unregistered. expungeAllRequests!");
Hugo Benichid2552ae2017-04-11 14:42:47 +09002471 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002472 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002473 final int clientRequestId = mClientRequests.keyAt(i);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002474 final ClientRequest request = mClientRequests.valueAt(i);
Paul Hud44e1b72023-06-16 02:07:42 +00002475 final int transactionId = request.mTransactionId;
2476 mTransactionIdToClientInfoMap.remove(transactionId);
paulhub2225702021-11-17 09:35:33 +08002477 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002478 Log.d(TAG, "Terminating clientRequestId " + clientRequestId
2479 + " transactionId " + transactionId
2480 + " type " + mClientRequests.get(clientRequestId));
paulhub2225702021-11-17 09:35:33 +08002481 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002482
2483 if (request instanceof DiscoveryManagerRequest) {
Paul Hu812e9212023-06-20 06:24:53 +00002484 final MdnsListener listener = unregisterMdnsListenerFromRequest(request);
2485 if (listener instanceof DiscoveryListener) {
2486 mMetrics.reportServiceDiscoveryStop(transactionId,
2487 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2488 request.getFoundServiceCount(),
2489 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002490 request.getServicesCount(),
2491 request.getSentQueryCount());
Paul Hu60149052023-07-31 14:26:08 +08002492 } else if (listener instanceof ResolutionListener) {
2493 mMetrics.reportServiceResolutionStop(transactionId,
2494 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Huddce5912023-08-01 10:26:49 +08002495 } else if (listener instanceof ServiceInfoListener) {
2496 mMetrics.reportServiceInfoCallbackUnregistered(transactionId,
2497 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2498 request.getFoundServiceCount(),
2499 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002500 request.isServiceFromCache(),
2501 request.getSentQueryCount());
Paul Hu812e9212023-06-20 06:24:53 +00002502 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002503 continue;
2504 }
2505
2506 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +08002507 final AdvertiserMetrics metrics =
2508 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00002509 mAdvertiser.removeService(transactionId);
Paul Hu812e9212023-06-20 06:24:53 +00002510 mMetrics.reportServiceUnregistration(transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002511 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2512 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2513 metrics.mConflictDuringProbingCount,
2514 metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002515 continue;
2516 }
2517
2518 if (!(request instanceof LegacyClientRequest)) {
2519 throw new IllegalStateException("Unknown request type: " + request.getClass());
2520 }
2521
2522 switch (((LegacyClientRequest) request).mRequestCode) {
Dave Plattfeff2af2014-03-07 14:48:22 -08002523 case NsdManager.DISCOVER_SERVICES:
Paul Hud44e1b72023-06-16 02:07:42 +00002524 stopServiceDiscovery(transactionId);
Paul Hu812e9212023-06-20 06:24:53 +00002525 mMetrics.reportServiceDiscoveryStop(transactionId,
2526 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2527 request.getFoundServiceCount(),
2528 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002529 request.getServicesCount(),
2530 NO_SENT_QUERY_COUNT);
Dave Plattfeff2af2014-03-07 14:48:22 -08002531 break;
2532 case NsdManager.RESOLVE_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002533 stopResolveService(transactionId);
Paul Hu60149052023-07-31 14:26:08 +08002534 mMetrics.reportServiceResolutionStop(transactionId,
2535 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Dave Plattfeff2af2014-03-07 14:48:22 -08002536 break;
2537 case NsdManager.REGISTER_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002538 unregisterService(transactionId);
Paul Hu812e9212023-06-20 06:24:53 +00002539 mMetrics.reportServiceUnregistration(transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002540 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2541 NO_PACKET /* repliedRequestsCount */,
2542 NO_PACKET /* sentPacketCount */,
2543 0 /* conflictDuringProbingCount */,
2544 0 /* conflictAfterProbingCount */);
Dave Plattfeff2af2014-03-07 14:48:22 -08002545 break;
2546 default:
2547 break;
2548 }
2549 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002550 mClientRequests.clear();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002551 updateMulticastLock();
2552 }
2553
2554 /**
2555 * Returns true if this client has any Java backend request that requests one of the given
2556 * networks.
2557 */
2558 boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) {
2559 for (int i = 0; i < mClientRequests.size(); i++) {
2560 final ClientRequest req = mClientRequests.valueAt(i);
2561 if (!(req instanceof JavaBackendClientRequest)) {
2562 continue;
2563 }
2564 final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i))
2565 .getRequestedNetwork();
2566 if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) {
2567 return true;
2568 }
2569 }
2570 return false;
Dave Plattfeff2af2014-03-07 14:48:22 -08002571 }
2572
Paul Hud44e1b72023-06-16 02:07:42 +00002573 // mClientRequests is a sparse array of client request id -> ClientRequest. For a given
2574 // transaction id, return the corresponding client request id.
2575 private int getClientRequestId(final int transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002576 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002577 if (mClientRequests.valueAt(i).mTransactionId == transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002578 return mClientRequests.keyAt(i);
2579 }
Christopher Lane74411222014-04-25 18:39:07 -07002580 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002581 return -1;
Christopher Lane74411222014-04-25 18:39:07 -07002582 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002583
Paul Hub2e67d32023-04-18 05:50:14 +00002584 private void log(String message) {
2585 mClientLogs.log(message);
2586 }
2587
Paul Hu812e9212023-06-20 06:24:53 +00002588 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info, int transactionId) {
2589 mMetrics.reportServiceDiscoveryStarted(transactionId);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002590 try {
2591 mCb.onDiscoverServicesStarted(listenerKey, info);
2592 } catch (RemoteException e) {
2593 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
2594 }
2595 }
Paul Hu812e9212023-06-20 06:24:53 +00002596 void onDiscoverServicesFailedImmediately(int listenerKey, int error) {
2597 onDiscoverServicesFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
2598 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002599
Paul Hu812e9212023-06-20 06:24:53 +00002600 void onDiscoverServicesFailed(int listenerKey, int error, int transactionId,
2601 long durationMs) {
2602 mMetrics.reportServiceDiscoveryFailed(transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002603 try {
2604 mCb.onDiscoverServicesFailed(listenerKey, error);
2605 } catch (RemoteException e) {
2606 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
2607 }
2608 }
2609
Paul Hu812e9212023-06-20 06:24:53 +00002610 void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2611 request.onServiceFound(info.getServiceName());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002612 try {
2613 mCb.onServiceFound(listenerKey, info);
2614 } catch (RemoteException e) {
2615 Log.e(TAG, "Error calling onServiceFound(", e);
2616 }
2617 }
2618
Paul Hu812e9212023-06-20 06:24:53 +00002619 void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2620 request.onServiceLost();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002621 try {
2622 mCb.onServiceLost(listenerKey, info);
2623 } catch (RemoteException e) {
2624 Log.e(TAG, "Error calling onServiceLost(", e);
2625 }
2626 }
2627
2628 void onStopDiscoveryFailed(int listenerKey, int error) {
2629 try {
2630 mCb.onStopDiscoveryFailed(listenerKey, error);
2631 } catch (RemoteException e) {
2632 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
2633 }
2634 }
2635
Paul Hu812e9212023-06-20 06:24:53 +00002636 void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) {
2637 mMetrics.reportServiceDiscoveryStop(
2638 request.mTransactionId,
2639 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2640 request.getFoundServiceCount(),
2641 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002642 request.getServicesCount(),
2643 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002644 try {
2645 mCb.onStopDiscoverySucceeded(listenerKey);
2646 } catch (RemoteException e) {
2647 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
2648 }
2649 }
2650
Paul Hu777ed052023-06-19 13:35:15 +00002651 void onRegisterServiceFailedImmediately(int listenerKey, int error) {
Paul Hu812e9212023-06-20 06:24:53 +00002652 onRegisterServiceFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
Paul Hu777ed052023-06-19 13:35:15 +00002653 }
2654
2655 void onRegisterServiceFailed(int listenerKey, int error, int transactionId,
2656 long durationMs) {
2657 mMetrics.reportServiceRegistrationFailed(transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002658 try {
2659 mCb.onRegisterServiceFailed(listenerKey, error);
2660 } catch (RemoteException e) {
2661 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
2662 }
2663 }
2664
Paul Hu777ed052023-06-19 13:35:15 +00002665 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info, int transactionId,
2666 long durationMs) {
2667 mMetrics.reportServiceRegistrationSucceeded(transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002668 try {
2669 mCb.onRegisterServiceSucceeded(listenerKey, info);
2670 } catch (RemoteException e) {
2671 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
2672 }
2673 }
2674
2675 void onUnregisterServiceFailed(int listenerKey, int error) {
2676 try {
2677 mCb.onUnregisterServiceFailed(listenerKey, error);
2678 } catch (RemoteException e) {
2679 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
2680 }
2681 }
2682
Paul Hu043bcd42023-07-14 16:38:25 +08002683 void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs,
2684 AdvertiserMetrics metrics) {
2685 mMetrics.reportServiceUnregistration(transactionId, durationMs,
2686 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2687 metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002688 try {
2689 mCb.onUnregisterServiceSucceeded(listenerKey);
2690 } catch (RemoteException e) {
2691 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
2692 }
2693 }
2694
Paul Hua6bc4632023-06-26 01:18:29 +00002695 void onResolveServiceFailedImmediately(int listenerKey, int error) {
2696 onResolveServiceFailed(listenerKey, error, NO_TRANSACTION, 0L /* durationMs */);
2697 }
2698
2699 void onResolveServiceFailed(int listenerKey, int error, int transactionId,
2700 long durationMs) {
2701 mMetrics.reportServiceResolutionFailed(transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002702 try {
2703 mCb.onResolveServiceFailed(listenerKey, error);
2704 } catch (RemoteException e) {
2705 Log.e(TAG, "Error calling onResolveServiceFailed", e);
2706 }
2707 }
2708
Paul Hua6bc4632023-06-26 01:18:29 +00002709 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
2710 ClientRequest request) {
2711 mMetrics.reportServiceResolved(
2712 request.mTransactionId,
2713 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hubad6fe92023-07-24 21:25:22 +08002714 request.isServiceFromCache(),
2715 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002716 try {
2717 mCb.onResolveServiceSucceeded(listenerKey, info);
2718 } catch (RemoteException e) {
2719 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
2720 }
2721 }
Paul Hub58deb72022-12-26 09:24:42 +00002722
2723 void onStopResolutionFailed(int listenerKey, int error) {
2724 try {
2725 mCb.onStopResolutionFailed(listenerKey, error);
2726 } catch (RemoteException e) {
2727 Log.e(TAG, "Error calling onStopResolutionFailed", e);
2728 }
2729 }
2730
Paul Hu60149052023-07-31 14:26:08 +08002731 void onStopResolutionSucceeded(int listenerKey, ClientRequest request) {
2732 mMetrics.reportServiceResolutionStop(
2733 request.mTransactionId,
2734 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hub58deb72022-12-26 09:24:42 +00002735 try {
2736 mCb.onStopResolutionSucceeded(listenerKey);
2737 } catch (RemoteException e) {
2738 Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
2739 }
2740 }
Paul Hu18aeccc2022-12-27 08:48:48 +00002741
2742 void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
Paul Huddce5912023-08-01 10:26:49 +08002743 mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
Paul Hu18aeccc2022-12-27 08:48:48 +00002744 try {
2745 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
2746 } catch (RemoteException e) {
2747 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
2748 }
2749 }
2750
Paul Huddce5912023-08-01 10:26:49 +08002751 void onServiceInfoCallbackRegistered(int transactionId) {
2752 mMetrics.reportServiceInfoCallbackRegistered(transactionId);
2753 }
2754
2755 void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2756 request.onServiceFound(info.getServiceName());
Paul Hu18aeccc2022-12-27 08:48:48 +00002757 try {
2758 mCb.onServiceUpdated(listenerKey, info);
2759 } catch (RemoteException e) {
2760 Log.e(TAG, "Error calling onServiceUpdated", e);
2761 }
2762 }
2763
Paul Huddce5912023-08-01 10:26:49 +08002764 void onServiceUpdatedLost(int listenerKey, ClientRequest request) {
2765 request.onServiceLost();
Paul Hu18aeccc2022-12-27 08:48:48 +00002766 try {
2767 mCb.onServiceUpdatedLost(listenerKey);
2768 } catch (RemoteException e) {
2769 Log.e(TAG, "Error calling onServiceUpdatedLost", e);
2770 }
2771 }
2772
Paul Huddce5912023-08-01 10:26:49 +08002773 void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) {
2774 mMetrics.reportServiceInfoCallbackUnregistered(
2775 request.mTransactionId,
2776 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2777 request.getFoundServiceCount(),
2778 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002779 request.isServiceFromCache(),
2780 request.getSentQueryCount());
Paul Hu18aeccc2022-12-27 08:48:48 +00002781 try {
2782 mCb.onServiceInfoCallbackUnregistered(listenerKey);
2783 } catch (RemoteException e) {
2784 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
2785 }
2786 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002787 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002788}