blob: 76481c8081016e3c186548c52fcab0aceb6cc3fa [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 Huangd5896e72023-11-28 13:23:59 +090019import static android.Manifest.permission.DEVICE_POWER;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090020import static android.Manifest.permission.NETWORK_SETTINGS;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090021import static android.Manifest.permission.NETWORK_STACK;
paulhu2b9ed952022-02-10 21:58:32 +080022import static android.net.ConnectivityManager.NETID_UNSET;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090023import static android.net.NetworkCapabilities.TRANSPORT_VPN;
24import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090025import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
Paul Hu019621e2023-01-13 23:26:49 +080026import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
paulhu2b9ed952022-02-10 21:58:32 +080027import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +090028import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
Yuyang Huang86d083f2023-12-12 19:56:41 +090029import static android.net.nsd.NsdManager.TYPE_REGEX;
30import static android.net.nsd.NsdManager.TYPE_SUBTYPE_LABEL_REGEX;
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +090031import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
Motomu Utsumied4e7ec2023-09-13 14:58:32 +090032
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090033import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090034import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
Paul Hu043bcd42023-07-14 16:38:25 +080035import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
36import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
Yuyang Huangde802c82023-05-02 17:14:22 +090037import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
Paul Hucdef3532023-06-18 14:47:35 +000038import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090039
Paul Hu23fa2022023-01-13 22:57:24 +080040import android.annotation.NonNull;
Paul Hu4bd98ef2023-01-12 13:42:07 +080041import android.annotation.Nullable;
Yuyang Huangfc831702023-08-21 17:48:48 +090042import android.annotation.RequiresApi;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090043import android.app.ActivityManager;
paulhua262cc12019-08-12 16:25:11 +080044import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070045import android.content.Intent;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090046import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090047import android.net.INetd;
Paul Hu75069ed2023-01-14 00:31:09 +080048import android.net.InetAddresses;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090049import android.net.LinkProperties;
50import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080051import android.net.mdns.aidl.DiscoveryInfo;
52import android.net.mdns.aidl.GetAddressInfo;
53import android.net.mdns.aidl.IMDnsEventListener;
54import android.net.mdns.aidl.RegistrationInfo;
55import android.net.mdns.aidl.ResolutionInfo;
Yuyang Huang86d083f2023-12-12 19:56:41 +090056import android.net.nsd.AdvertisingRequest;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070057import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090058import android.net.nsd.INsdManagerCallback;
59import android.net.nsd.INsdServiceConnector;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090060import android.net.nsd.IOffloadEngine;
paulhu2b9ed952022-02-10 21:58:32 +080061import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070062import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080063import android.net.nsd.NsdServiceInfo;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090064import android.net.nsd.OffloadEngine;
65import android.net.nsd.OffloadServiceInfo;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090066import android.net.wifi.WifiManager;
Paul Hub2e67d32023-04-18 05:50:14 +000067import android.os.Binder;
Yuyang Huangfc831702023-08-21 17:48:48 +090068import android.os.Build;
Hugo Benichi803a2f02017-04-24 11:35:06 +090069import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080070import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090071import android.os.IBinder;
Paul Hu4bd98ef2023-01-12 13:42:07 +080072import android.os.Looper;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070073import android.os.Message;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090074import android.os.RemoteCallbackList;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090075import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070076import android.os.UserHandle;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090077import android.provider.DeviceConfig;
Paul Hu23fa2022023-01-13 22:57:24 +080078import android.text.TextUtils;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090079import android.util.ArraySet;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090080import android.util.Log;
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +090081import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070082import android.util.SparseArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070083
paulhua262cc12019-08-12 16:25:11 +080084import com.android.internal.annotations.VisibleForTesting;
Paul Hub2e67d32023-04-18 05:50:14 +000085import com.android.internal.util.IndentingPrintWriter;
paulhua262cc12019-08-12 16:25:11 +080086import com.android.internal.util.State;
87import com.android.internal.util.StateMachine;
Paul Hucdef3532023-06-18 14:47:35 +000088import com.android.metrics.NetworkNsdReportedMetrics;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090089import com.android.modules.utils.build.SdkLevel;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090090import com.android.net.module.util.CollectionUtils;
Paul Hu4bd98ef2023-01-12 13:42:07 +080091import com.android.net.module.util.DeviceConfigUtils;
Yuyang Huanga6a6ff92023-04-24 13:33:34 +090092import com.android.net.module.util.InetAddressUtils;
paulhu3ffffe72021-09-16 10:15:22 +080093import com.android.net.module.util.PermissionUtils;
Paul Hub2e67d32023-04-18 05:50:14 +000094import com.android.net.module.util.SharedLog;
Paul Hu4bd98ef2023-01-12 13:42:07 +080095import com.android.server.connectivity.mdns.ExecutorProvider;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +090096import com.android.server.connectivity.mdns.MdnsAdvertiser;
Yuyang Huange5cba9c2023-11-02 18:05:47 +090097import com.android.server.connectivity.mdns.MdnsAdvertisingOptions;
Paul Hu4bd98ef2023-01-12 13:42:07 +080098import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
Yuyang Huangb96a0712023-09-07 15:13:15 +090099import com.android.server.connectivity.mdns.MdnsFeatureFlags;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900100import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800101import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
Paul Hu23fa2022023-01-13 22:57:24 +0800102import com.android.server.connectivity.mdns.MdnsSearchOptions;
103import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
104import com.android.server.connectivity.mdns.MdnsServiceInfo;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800105import com.android.server.connectivity.mdns.MdnsSocketProvider;
Yuyang Huangde802c82023-05-02 17:14:22 +0900106import com.android.server.connectivity.mdns.util.MdnsUtils;
paulhua262cc12019-08-12 16:25:11 +0800107
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700108import java.io.FileDescriptor;
109import java.io.PrintWriter;
Yuyang Huangaa0e9602023-03-17 12:43:09 +0900110import java.net.Inet6Address;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700111import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900112import java.net.NetworkInterface;
113import java.net.SocketException;
114import java.net.UnknownHostException;
Paul Hu2b865912023-03-06 14:27:53 +0800115import java.util.ArrayList;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +0900116import java.util.Arrays;
Kangping Dong5af24b62023-12-10 21:41:16 +0800117import java.util.Collection;
Yuyang Huang170d42f2023-12-09 15:26:16 +0900118import java.util.Collections;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700119import java.util.HashMap;
Kangping Dong5af24b62023-12-10 21:41:16 +0800120import java.util.LinkedHashMap;
Paul Hu23fa2022023-01-13 22:57:24 +0800121import java.util.List;
Paul Hu75069ed2023-01-14 00:31:09 +0800122import java.util.Map;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900123import java.util.Objects;
Paul Hu812e9212023-06-20 06:24:53 +0000124import java.util.Set;
Paul Hu23fa2022023-01-13 22:57:24 +0800125import java.util.regex.Matcher;
126import java.util.regex.Pattern;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700127
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700128/**
129 * Network Service Discovery Service handles remote service discovery operation requests by
130 * implementing the INsdManager interface.
131 *
132 * @hide
133 */
Yuyang Huangfc831702023-08-21 17:48:48 +0900134@RequiresApi(Build.VERSION_CODES.TIRAMISU)
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700135public class NsdService extends INsdManager.Stub {
136 private static final String TAG = "NsdService";
137 private static final String MDNS_TAG = "mDnsConnector";
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900138 /**
139 * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
140 * implementation.
141 */
Paul Hu4bd98ef2023-01-12 13:42:07 +0800142 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
Paul Hu23fa2022023-01-13 22:57:24 +0800143 private static final String LOCAL_DOMAIN_NAME = "local";
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700144
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900145 /**
146 * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
147 * implementation.
148 */
149 private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
150
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900151 /**
152 * Comma-separated list of type:flag mappings indicating the flags to use to allowlist
153 * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
154 *
155 * For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
156 * _mytype._tcp:mytype,_othertype._tcp.local:othertype
157 *
158 * In which case the flags:
159 * "mdns_discovery_manager_allowlist_mytype_version",
160 * "mdns_advertiser_allowlist_mytype_version",
161 * "mdns_discovery_manager_allowlist_othertype_version",
162 * "mdns_advertiser_allowlist_othertype_version"
163 * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
164 * be read with
Motomu Utsumi624aeb42023-08-15 15:52:27 +0900165 * {@link DeviceConfigUtils#isTetheringFeatureEnabled}
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900166 *
167 * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
168 * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
169 * @see #MDNS_ALLOWLIST_FLAG_SUFFIX
170 */
171 private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
172
173 private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
174 "mdns_discovery_manager_allowlist_";
175 private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
176 "mdns_advertiser_allowlist_";
177 private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
178
Yuyang Huang86d083f2023-12-12 19:56:41 +0900179
Kangping Dong5af24b62023-12-10 21:41:16 +0800180
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900181 @VisibleForTesting
182 static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
183 "mdns_config_running_app_active_importance_cutoff";
184 @VisibleForTesting
185 static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
186 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
187 private final int mRunningAppActiveImportanceCutoff;
188
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900189 public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Luke Huang92860f92021-06-23 06:29:30 +0000190 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900191 private static final int IFACE_IDX_ANY = 0;
Paul Hu812e9212023-06-20 06:24:53 +0000192 private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
193 @VisibleForTesting
194 static final int NO_TRANSACTION = -1;
Paul Hubad6fe92023-07-24 21:25:22 +0800195 private static final int NO_SENT_QUERY_COUNT = 0;
196 private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
Kangping Dong5af24b62023-12-10 21:41:16 +0800197 private static final int MAX_SUBTYPE_COUNT = 100;
Paul Hu14667de2023-04-17 22:42:47 +0800198 private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700199
Hugo Benichi32be63d2017-04-05 14:06:11 +0900200 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +0900201 private final NsdStateMachine mNsdStateMachine;
Ken Chen80c9f6f2023-11-15 18:24:54 +0800202 // It can be null on V+ device since mdns native service provided by netd is removed.
203 private final @Nullable MDnsManager mMDnsManager;
paulhu2b9ed952022-02-10 21:58:32 +0800204 private final MDnsEventCallback mMDnsEventCallback;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900205 @NonNull
206 private final Dependencies mDeps;
207 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800208 private final MdnsMultinetworkSocketClient mMdnsSocketClient;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900209 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800210 private final MdnsDiscoveryManager mMdnsDiscoveryManager;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900211 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800212 private final MdnsSocketProvider mMdnsSocketProvider;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900213 @NonNull
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900214 private final MdnsAdvertiser mAdvertiser;
Paul Hu777ed052023-06-19 13:35:15 +0000215 @NonNull
216 private final Clock mClock;
Paul Hu14667de2023-04-17 22:42:47 +0800217 private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
Paul Hu23fa2022023-01-13 22:57:24 +0800218 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
paulhu2b9ed952022-02-10 21:58:32 +0800219 // state machine thread. If change this outside state machine, it will need to introduce
220 // synchronization.
221 private boolean mIsDaemonStarted = false;
Paul Hu23fa2022023-01-13 22:57:24 +0800222 private boolean mIsMonitoringSocketsStarted = false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700223
224 /**
225 * Clients receiving asynchronous messages
226 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900227 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700228
Paul Hud44e1b72023-06-16 02:07:42 +0000229 /* A map from transaction(unique) id to client info */
230 private final SparseArray<ClientInfo> mTransactionIdToClientInfoMap = new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700231
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900232 // Note this is not final to avoid depending on the Wi-Fi service starting before NsdService
233 @Nullable
234 private WifiManager.MulticastLock mHeldMulticastLock;
235 // Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network
236 // (non-null), value is the requested Network (nullable)
237 @NonNull
238 private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>();
239 @NonNull
240 private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>();
241
Luke Huang05298582021-06-13 16:52:05 +0000242 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700243
Hugo Benichi32be63d2017-04-05 14:06:11 +0900244 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700245 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +0800246 // The count of the connected legacy clients.
247 private int mLegacyClientCount = 0;
Paul Hub2e67d32023-04-18 05:50:14 +0000248 // The number of client that ever connected.
249 private int mClientNumberId = 1;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700250
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900251 private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
252 new RemoteCallbackList<>();
253
254 private static class OffloadEngineInfo {
255 @NonNull final String mInterfaceName;
256 final long mOffloadCapabilities;
257 final long mOffloadType;
258 @NonNull final IOffloadEngine mOffloadEngine;
259
260 OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
261 @NonNull String interfaceName, long capabilities, long offloadType) {
262 this.mOffloadEngine = offloadEngine;
263 this.mInterfaceName = interfaceName;
264 this.mOffloadCapabilities = capabilities;
265 this.mOffloadType = offloadType;
266 }
267 }
268
Paul Hu812e9212023-06-20 06:24:53 +0000269 @VisibleForTesting
270 static class MdnsListener implements MdnsServiceBrowserListener {
Paul Hud44e1b72023-06-16 02:07:42 +0000271 protected final int mClientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800272 protected final int mTransactionId;
273 @NonNull
274 protected final NsdServiceInfo mReqServiceInfo;
275 @NonNull
276 protected final String mListenedServiceType;
277
Paul Hud44e1b72023-06-16 02:07:42 +0000278 MdnsListener(int clientRequestId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
Paul Hu23fa2022023-01-13 22:57:24 +0800279 @NonNull String listenedServiceType) {
Paul Hud44e1b72023-06-16 02:07:42 +0000280 mClientRequestId = clientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800281 mTransactionId = transactionId;
282 mReqServiceInfo = reqServiceInfo;
283 mListenedServiceType = listenedServiceType;
284 }
285
286 @NonNull
287 public String getListenedServiceType() {
288 return mListenedServiceType;
289 }
290
291 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000292 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
293 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800294
295 @Override
296 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
297
298 @Override
299 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
300
301 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000302 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
303 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800304
305 @Override
306 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
307
308 @Override
309 public void onSearchStoppedWithError(int error) { }
310
311 @Override
312 public void onSearchFailedToStart() { }
313
314 @Override
Paul Hubad6fe92023-07-24 21:25:22 +0800315 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
316 int sentQueryTransactionId) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800317
318 @Override
319 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
320 }
321
322 private class DiscoveryListener extends MdnsListener {
323
Paul Hud44e1b72023-06-16 02:07:42 +0000324 DiscoveryListener(int clientRequestId, int transactionId,
325 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
326 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu23fa2022023-01-13 22:57:24 +0800327 }
328
329 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000330 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
331 boolean isServiceFromCache) {
Paul Hu019621e2023-01-13 23:26:49 +0800332 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
333 NsdManager.SERVICE_FOUND,
Paul Hua6bc4632023-06-26 01:18:29 +0000334 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu23fa2022023-01-13 22:57:24 +0800335 }
336
337 @Override
338 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
Paul Hu319751a2023-01-13 23:56:34 +0800339 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
340 NsdManager.SERVICE_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000341 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu23fa2022023-01-13 22:57:24 +0800342 }
Paul Hubad6fe92023-07-24 21:25:22 +0800343
344 @Override
345 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
346 int sentQueryTransactionId) {
347 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
348 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
349 }
Paul Hu23fa2022023-01-13 22:57:24 +0800350 }
351
Paul Hu75069ed2023-01-14 00:31:09 +0800352 private class ResolutionListener extends MdnsListener {
353
Paul Hud44e1b72023-06-16 02:07:42 +0000354 ResolutionListener(int clientRequestId, int transactionId,
355 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
356 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +0800357 }
358
359 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000360 public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
Paul Hu75069ed2023-01-14 00:31:09 +0800361 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
362 NsdManager.RESOLVE_SERVICE_SUCCEEDED,
Paul Hua6bc4632023-06-26 01:18:29 +0000363 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu75069ed2023-01-14 00:31:09 +0800364 }
Paul Hubad6fe92023-07-24 21:25:22 +0800365
366 @Override
367 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
368 int sentQueryTransactionId) {
369 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
370 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
371 }
Paul Hu75069ed2023-01-14 00:31:09 +0800372 }
373
Paul Hu30bd70d2023-02-07 13:20:56 +0000374 private class ServiceInfoListener extends MdnsListener {
375
Paul Hud44e1b72023-06-16 02:07:42 +0000376 ServiceInfoListener(int clientRequestId, int transactionId,
377 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
378 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +0000379 }
380
381 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000382 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
383 boolean isServiceFromCache) {
Paul Hu30bd70d2023-02-07 13:20:56 +0000384 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
385 NsdManager.SERVICE_UPDATED,
Paul Hua6bc4632023-06-26 01:18:29 +0000386 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu30bd70d2023-02-07 13:20:56 +0000387 }
388
389 @Override
390 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
391 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
392 NsdManager.SERVICE_UPDATED,
Paul Hud44e1b72023-06-16 02:07:42 +0000393 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000394 }
395
396 @Override
397 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
398 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
399 NsdManager.SERVICE_UPDATED_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000400 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000401 }
Paul Hubad6fe92023-07-24 21:25:22 +0800402
403 @Override
404 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
405 int sentQueryTransactionId) {
406 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
407 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
408 }
Paul Hu30bd70d2023-02-07 13:20:56 +0000409 }
410
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900411 private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
412 @Override
413 public void onSocketRequestFulfilled(@Nullable Network socketNetwork,
414 @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {
415 // The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF
416 // filtering on such interfaces, so taking the multicast lock is not necessary to
417 // disable APF filtering of multicast.
418 if (socketNetwork == null
419 || !CollectionUtils.contains(transports, TRANSPORT_WIFI)
420 || CollectionUtils.contains(transports, TRANSPORT_VPN)) {
421 return;
422 }
423
424 if (mWifiLockRequiredNetworks.add(socketNetwork)) {
425 updateMulticastLock();
426 }
427 }
428
429 @Override
430 public void onSocketDestroyed(@Nullable Network socketNetwork,
431 @NonNull MdnsInterfaceSocket socket) {
432 if (mWifiLockRequiredNetworks.remove(socketNetwork)) {
433 updateMulticastLock();
434 }
435 }
436 }
437
438 private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
439 private final Handler mHandler;
440
441 private UidImportanceListener(Handler handler) {
442 mHandler = handler;
443 }
444
445 @Override
446 public void onUidImportance(int uid, int importance) {
447 mHandler.post(() -> handleUidImportanceChanged(uid, importance));
448 }
449 }
450
451 private void handleUidImportanceChanged(int uid, int importance) {
452 // Lower importance values are more "important"
453 final boolean modified = importance <= mRunningAppActiveImportanceCutoff
454 ? mRunningAppActiveUids.add(uid)
455 : mRunningAppActiveUids.remove(uid);
456 if (modified) {
457 updateMulticastLock();
458 }
459 }
460
461 /**
462 * Take or release the lock based on updated internal state.
463 *
464 * This determines whether the lock needs to be held based on
465 * {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and
466 * {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been
467 * updated.
468 */
469 private void updateMulticastLock() {
470 final int needsLockUid = getMulticastLockNeededUid();
471 if (needsLockUid >= 0 && mHeldMulticastLock == null) {
472 final WifiManager wm = mContext.getSystemService(WifiManager.class);
473 if (wm == null) {
474 Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager");
475 return;
476 }
477 mHeldMulticastLock = wm.createMulticastLock(TAG);
478 mHeldMulticastLock.acquire();
479 mServiceLogs.log("Taking multicast lock for uid " + needsLockUid);
480 } else if (needsLockUid < 0 && mHeldMulticastLock != null) {
481 mHeldMulticastLock.release();
482 mHeldMulticastLock = null;
483 mServiceLogs.log("Released multicast lock");
484 }
485 }
486
487 /**
488 * @return The UID of an app requiring the multicast lock, or -1 if none.
489 */
490 private int getMulticastLockNeededUid() {
491 if (mWifiLockRequiredNetworks.size() == 0) {
492 // Return early if NSD is not active, or not on any relevant network
493 return -1;
494 }
Paul Hud44e1b72023-06-16 02:07:42 +0000495 for (int i = 0; i < mTransactionIdToClientInfoMap.size(); i++) {
496 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.valueAt(i);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900497 if (!mRunningAppActiveUids.contains(clientInfo.mUid)) {
498 // Ignore non-active UIDs
499 continue;
500 }
501
502 if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) {
503 return clientInfo.mUid;
504 }
505 }
506 return -1;
507 }
508
Paul Hu019621e2023-01-13 23:26:49 +0800509 /**
510 * Data class of mdns service callback information.
511 */
512 private static class MdnsEvent {
Paul Hud44e1b72023-06-16 02:07:42 +0000513 final int mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +0800514 @Nullable
Paul Hu019621e2023-01-13 23:26:49 +0800515 final MdnsServiceInfo mMdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000516 final boolean mIsServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800517
Paul Hubad6fe92023-07-24 21:25:22 +0800518 MdnsEvent(int clientRequestId) {
519 this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */);
520 }
521
522 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) {
Paul Hua6bc4632023-06-26 01:18:29 +0000523 this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
524 }
525
Paul Hubad6fe92023-07-24 21:25:22 +0800526 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo,
Paul Hua6bc4632023-06-26 01:18:29 +0000527 boolean isServiceFromCache) {
Paul Hud44e1b72023-06-16 02:07:42 +0000528 mClientRequestId = clientRequestId;
Paul Hu019621e2023-01-13 23:26:49 +0800529 mMdnsServiceInfo = mdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000530 mIsServiceFromCache = isServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800531 }
532 }
533
Paul Hu77c11182023-10-23 16:17:32 +0800534 // TODO: Use a Handler instead of a StateMachine since there are no state changes.
Irfan Sheriff75006652012-04-17 23:15:29 -0700535 private class NsdStateMachine extends StateMachine {
536
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700537 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700538
539 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700540 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900541 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700542 }
543
Luke Huang92860f92021-06-23 06:29:30 +0000544 private void maybeStartDaemon() {
Ken Chen80c9f6f2023-11-15 18:24:54 +0800545 if (mMDnsManager == null) {
546 Log.wtf(TAG, "maybeStartDaemon: mMDnsManager is null");
547 return;
548 }
549
paulhu2b9ed952022-02-10 21:58:32 +0800550 if (mIsDaemonStarted) {
551 if (DBG) Log.d(TAG, "Daemon is already started.");
552 return;
553 }
554 mMDnsManager.registerEventListener(mMDnsEventCallback);
555 mMDnsManager.startDaemon();
556 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000557 maybeScheduleStop();
Paul Hub2e67d32023-04-18 05:50:14 +0000558 mServiceLogs.log("Start mdns_responder daemon");
Luke Huang05298582021-06-13 16:52:05 +0000559 }
560
paulhu2b9ed952022-02-10 21:58:32 +0800561 private void maybeStopDaemon() {
Ken Chen80c9f6f2023-11-15 18:24:54 +0800562 if (mMDnsManager == null) {
563 Log.wtf(TAG, "maybeStopDaemon: mMDnsManager is null");
564 return;
565 }
566
paulhu2b9ed952022-02-10 21:58:32 +0800567 if (!mIsDaemonStarted) {
568 if (DBG) Log.d(TAG, "Daemon has not been started.");
569 return;
570 }
571 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
572 mMDnsManager.stopDaemon();
573 mIsDaemonStarted = false;
Paul Hub2e67d32023-04-18 05:50:14 +0000574 mServiceLogs.log("Stop mdns_responder daemon");
paulhu2b9ed952022-02-10 21:58:32 +0800575 }
576
Luke Huang92860f92021-06-23 06:29:30 +0000577 private boolean isAnyRequestActive() {
Paul Hud44e1b72023-06-16 02:07:42 +0000578 return mTransactionIdToClientInfoMap.size() != 0;
Luke Huang92860f92021-06-23 06:29:30 +0000579 }
580
581 private void scheduleStop() {
582 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
583 }
584 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800585 // The native daemon should stay alive and can't be cleanup
586 // if any legacy client connected.
587 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000588 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000589 }
590 }
591
Luke Huang92860f92021-06-23 06:29:30 +0000592 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000593 this.removeMessages(NsdManager.DAEMON_CLEANUP);
594 }
595
Paul Hu23fa2022023-01-13 22:57:24 +0800596 private void maybeStartMonitoringSockets() {
597 if (mIsMonitoringSocketsStarted) {
598 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
599 return;
600 }
601
602 mMdnsSocketProvider.startMonitoringSockets();
603 mIsMonitoringSocketsStarted = true;
604 }
605
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900606 private void maybeStopMonitoringSocketsIfNoActiveRequest() {
607 if (!mIsMonitoringSocketsStarted) return;
608 if (isAnyRequestActive()) return;
609
Paul Hu58f20602023-02-18 11:41:07 +0800610 mMdnsSocketProvider.requestStopWhenInactive();
Paul Hu23fa2022023-01-13 22:57:24 +0800611 mIsMonitoringSocketsStarted = false;
612 }
613
Hugo Benichi803a2f02017-04-24 11:35:06 +0900614 NsdStateMachine(String name, Handler handler) {
615 super(name, handler);
Paul Hu77c11182023-10-23 16:17:32 +0800616 addState(mEnabledState);
paulhu5568f452021-11-30 13:31:29 +0800617 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900618 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700619 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700620 }
621
Irfan Sheriff75006652012-04-17 23:15:29 -0700622 class EnabledState extends State {
623 @Override
624 public void enter() {
625 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700626 }
627
628 @Override
629 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000630 // TODO: it is incorrect to stop the daemon without expunging all requests
631 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000632 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700633 }
634
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700635 private boolean requestLimitReached(ClientInfo clientInfo) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900636 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800637 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700638 return true;
639 }
640 return false;
641 }
642
Paul Hu508a0122023-09-11 15:31:33 +0800643 private ClientRequest storeLegacyRequestMap(int clientRequestId, int transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +0000644 ClientInfo clientInfo, int what, long startTimeMs) {
Paul Hu508a0122023-09-11 15:31:33 +0800645 final LegacyClientRequest request =
646 new LegacyClientRequest(transactionId, what, startTimeMs);
647 clientInfo.mClientRequests.put(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000648 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000649 // Remove the cleanup event because here comes a new request.
650 cancelStop();
Paul Hu508a0122023-09-11 15:31:33 +0800651 return request;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700652 }
653
Paul Hud44e1b72023-06-16 02:07:42 +0000654 private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900655 ClientInfo clientInfo, @Nullable Network requestedNetwork) {
Paul Hu777ed052023-06-19 13:35:15 +0000656 clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
Paul Hu812e9212023-06-20 06:24:53 +0000657 transactionId, requestedNetwork, mClock.elapsedRealtime()));
Paul Hud44e1b72023-06-16 02:07:42 +0000658 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900659 updateMulticastLock();
Paul Hu23fa2022023-01-13 22:57:24 +0800660 }
661
Paul Hud44e1b72023-06-16 02:07:42 +0000662 private void removeRequestMap(
663 int clientRequestId, int transactionId, ClientInfo clientInfo) {
664 final ClientRequest existing = clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900665 if (existing == null) return;
Paul Hud44e1b72023-06-16 02:07:42 +0000666 clientInfo.mClientRequests.remove(clientRequestId);
667 mTransactionIdToClientInfoMap.remove(transactionId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900668
669 if (existing instanceof LegacyClientRequest) {
670 maybeScheduleStop();
671 } else {
672 maybeStopMonitoringSocketsIfNoActiveRequest();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900673 updateMulticastLock();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900674 }
675 }
676
Paul Hu508a0122023-09-11 15:31:33 +0800677 private ClientRequest storeDiscoveryManagerRequestMap(int clientRequestId,
678 int transactionId, MdnsListener listener, ClientInfo clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900679 @Nullable Network requestedNetwork) {
Paul Hu508a0122023-09-11 15:31:33 +0800680 final DiscoveryManagerRequest request = new DiscoveryManagerRequest(transactionId,
681 listener, requestedNetwork, mClock.elapsedRealtime());
682 clientInfo.mClientRequests.put(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000683 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900684 updateMulticastLock();
Paul Hu508a0122023-09-11 15:31:33 +0800685 return request;
Paul Hu23fa2022023-01-13 22:57:24 +0800686 }
687
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900688 /**
689 * Truncate a service name to up to 63 UTF-8 bytes.
690 *
691 * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
692 * names used in registerService follows historical behavior (see mdnsresponder
693 * handle_regservice_request).
694 */
695 @NonNull
696 private String truncateServiceName(@NonNull String originalName) {
Yuyang Huangde802c82023-05-02 17:14:22 +0900697 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
Paul Hu23fa2022023-01-13 22:57:24 +0800698 }
699
Paul Hud44e1b72023-06-16 02:07:42 +0000700 private void stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId,
701 int transactionId, ClientInfo clientInfo) {
Paul Hue4f5f252023-02-16 21:13:47 +0800702 clientInfo.unregisterMdnsListenerFromRequest(request);
Paul Hud44e1b72023-06-16 02:07:42 +0000703 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hue4f5f252023-02-16 21:13:47 +0800704 }
705
Paul Hu77c11182023-10-23 16:17:32 +0800706 private ClientInfo getClientInfoForReply(Message msg) {
707 final ListenerArgs args = (ListenerArgs) msg.obj;
708 return mClients.get(args.connector);
709 }
710
Kangping Dong5af24b62023-12-10 21:41:16 +0800711 /**
712 * Returns {@code false} if {@code subtypes} exceeds the maximum number limit or
713 * contains invalid subtype label.
714 */
715 private boolean checkSubtypeLabels(Set<String> subtypes) {
716 if (subtypes.size() > MAX_SUBTYPE_COUNT) {
717 mServiceLogs.e(
718 "Too many subtypes: " + subtypes.size() + " (max = "
719 + MAX_SUBTYPE_COUNT + ")");
720 return false;
721 }
722
723 for (String subtype : subtypes) {
724 if (!checkSubtypeLabel(subtype)) {
725 mServiceLogs.e("Subtype " + subtype + " is invalid");
726 return false;
727 }
728 }
729 return true;
730 }
731
732 private Set<String> dedupSubtypeLabels(Collection<String> subtypes) {
733 final Map<String, String> subtypeMap = new LinkedHashMap<>(subtypes.size());
734 for (String subtype : subtypes) {
735 subtypeMap.put(MdnsUtils.toDnsLowerCase(subtype), subtype);
736 }
737 return new ArraySet<>(subtypeMap.values());
738 }
739
Irfan Sheriff75006652012-04-17 23:15:29 -0700740 @Override
741 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900742 final ClientInfo clientInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000743 final int transactionId;
744 final int clientRequestId = msg.arg2;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900745 final OffloadEngineInfo offloadEngineInfo;
Irfan Sheriff75006652012-04-17 23:15:29 -0700746 switch (msg.what) {
Paul Hu75069ed2023-01-14 00:31:09 +0800747 case NsdManager.DISCOVER_SERVICES: {
paulhub2225702021-11-17 09:35:33 +0800748 if (DBG) Log.d(TAG, "Discover services");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900749 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900750 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000751 // If the binder death notification for a INsdManagerCallback was received
752 // before any calls are received by NsdService, the clientInfo would be
753 // cleared and cause NPE. Add a null check here to prevent this corner case.
754 if (clientInfo == null) {
755 Log.e(TAG, "Unknown connector in discovery");
756 break;
757 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700758
759 if (requestLimitReached(clientInfo)) {
Paul Hu508a0122023-09-11 15:31:33 +0800760 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
761 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
Irfan Sheriff75006652012-04-17 23:15:29 -0700762 break;
763 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700764
Paul Hu23fa2022023-01-13 22:57:24 +0800765 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000766 transactionId = getUniqueId();
Yuyang Huang170d42f2023-12-09 15:26:16 +0900767 final Pair<String, List<String>> typeAndSubtype =
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900768 parseTypeAndSubtype(info.getServiceType());
769 final String serviceType = typeAndSubtype == null
770 ? null : typeAndSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800771 if (clientInfo.mUseJavaBackend
772 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900773 || useDiscoveryManagerForType(serviceType)) {
Yuyang Huang170d42f2023-12-09 15:26:16 +0900774 if (serviceType == null || typeAndSubtype.second.size() > 1) {
Paul Hu508a0122023-09-11 15:31:33 +0800775 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
776 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Paul Hu23fa2022023-01-13 22:57:24 +0800777 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700778 }
Paul Hu23fa2022023-01-13 22:57:24 +0800779
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900780 final String listenServiceType = serviceType + ".local";
Paul Hu23fa2022023-01-13 22:57:24 +0800781 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +0000782 final MdnsListener listener = new DiscoveryListener(clientRequestId,
783 transactionId, info, listenServiceType);
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900784 final MdnsSearchOptions.Builder optionsBuilder =
785 MdnsSearchOptions.newBuilder()
786 .setNetwork(info.getNetwork())
Yuyang Huangff963222023-06-01 18:42:42 +0900787 .setRemoveExpiredService(true)
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900788 .setIsPassiveMode(true);
Yuyang Huang170d42f2023-12-09 15:26:16 +0900789 if (!typeAndSubtype.second.isEmpty()) {
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900790 // The parsing ensures subtype starts with an underscore.
791 // MdnsSearchOptions expects the underscore to not be present.
Yuyang Huang170d42f2023-12-09 15:26:16 +0900792 optionsBuilder.addSubtype(
793 typeAndSubtype.second.get(0).substring(1));
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900794 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900795 mMdnsDiscoveryManager.registerListener(
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900796 listenServiceType, listener, optionsBuilder.build());
Paul Hu508a0122023-09-11 15:31:33 +0800797 final ClientRequest request = storeDiscoveryManagerRequestMap(
798 clientRequestId, transactionId, listener, clientInfo,
799 info.getNetwork());
800 clientInfo.onDiscoverServicesStarted(clientRequestId, info, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000801 clientInfo.log("Register a DiscoveryListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +0000802 + " for service type:" + listenServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -0700803 } else {
Paul Hu23fa2022023-01-13 22:57:24 +0800804 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +0000805 if (discoverServices(transactionId, info)) {
Paul Hu23fa2022023-01-13 22:57:24 +0800806 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +0000807 Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
Paul Hu23fa2022023-01-13 22:57:24 +0800808 + info.getServiceType());
809 }
Paul Hu508a0122023-09-11 15:31:33 +0800810 final ClientRequest request = storeLegacyRequestMap(clientRequestId,
811 transactionId, clientInfo, msg.what,
812 mClock.elapsedRealtime());
Paul Hu812e9212023-06-20 06:24:53 +0000813 clientInfo.onDiscoverServicesStarted(
Paul Hu508a0122023-09-11 15:31:33 +0800814 clientRequestId, info, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800815 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000816 stopServiceDiscovery(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800817 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
818 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Paul Hu23fa2022023-01-13 22:57:24 +0800819 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700820 }
821 break;
Paul Hu75069ed2023-01-14 00:31:09 +0800822 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900823 case NsdManager.STOP_DISCOVERY: {
paulhub2225702021-11-17 09:35:33 +0800824 if (DBG) Log.d(TAG, "Stop service discovery");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900825 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900826 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000827 // If the binder death notification for a INsdManagerCallback was received
828 // before any calls are received by NsdService, the clientInfo would be
829 // cleared and cause NPE. Add a null check here to prevent this corner case.
830 if (clientInfo == null) {
831 Log.e(TAG, "Unknown connector in stop discovery");
832 break;
833 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700834
Paul Hud44e1b72023-06-16 02:07:42 +0000835 final ClientRequest request =
836 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900837 if (request == null) {
838 Log.e(TAG, "Unknown client request in STOP_DISCOVERY");
Irfan Sheriff75006652012-04-17 23:15:29 -0700839 break;
840 }
Paul Hud44e1b72023-06-16 02:07:42 +0000841 transactionId = request.mTransactionId;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900842 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
843 // point, so this needs to check the type of the original request to
844 // unregister instead of looking at the flag value.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900845 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +0000846 stopDiscoveryManagerRequest(
847 request, clientRequestId, transactionId, clientInfo);
Paul Hu812e9212023-06-20 06:24:53 +0000848 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000849 clientInfo.log("Unregister the DiscoveryListener " + transactionId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700850 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000851 removeRequestMap(clientRequestId, transactionId, clientInfo);
852 if (stopServiceDiscovery(transactionId)) {
Paul Hu812e9212023-06-20 06:24:53 +0000853 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800854 } else {
855 clientInfo.onStopDiscoveryFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000856 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu23fa2022023-01-13 22:57:24 +0800857 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700858 }
859 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900860 }
861 case NsdManager.REGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800862 if (DBG) Log.d(TAG, "Register service");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900863 final AdvertisingArgs args = (AdvertisingArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900864 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000865 // If the binder death notification for a INsdManagerCallback was received
866 // before any calls are received by NsdService, the clientInfo would be
867 // cleared and cause NPE. Add a null check here to prevent this corner case.
868 if (clientInfo == null) {
869 Log.e(TAG, "Unknown connector in registration");
870 break;
871 }
872
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700873 if (requestLimitReached(clientInfo)) {
Paul Hu508a0122023-09-11 15:31:33 +0800874 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
875 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700876 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700877 }
Yuyang Huang86d083f2023-12-12 19:56:41 +0900878 final AdvertisingRequest advertisingRequest = args.advertisingRequest;
879 if (advertisingRequest == null) {
880 Log.e(TAG, "Unknown advertisingRequest in registration");
881 break;
882 }
883 final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo();
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900884 final String serviceType = serviceInfo.getServiceType();
Yuyang Huang170d42f2023-12-09 15:26:16 +0900885 final Pair<String, List<String>> typeSubtype = parseTypeAndSubtype(
886 serviceType);
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900887 final String registerServiceType = typeSubtype == null
888 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800889 if (clientInfo.mUseJavaBackend
890 || mDeps.isMdnsAdvertiserEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900891 || useAdvertiserForType(registerServiceType)) {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900892 if (registerServiceType == null) {
893 Log.e(TAG, "Invalid service type: " + serviceType);
Paul Hu508a0122023-09-11 15:31:33 +0800894 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
895 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900896 break;
897 }
Yuyang Huang86d083f2023-12-12 19:56:41 +0900898 boolean isUpdateOnly = (advertisingRequest.getAdvertisingConfig()
899 & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0;
900 // If it is an update request, then reuse the old transactionId
901 if (isUpdateOnly) {
902 final ClientRequest existingClientRequest =
903 clientInfo.mClientRequests.get(clientRequestId);
904 if (existingClientRequest == null) {
905 Log.e(TAG, "Invalid update on requestId: " + clientRequestId);
906 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
907 NsdManager.FAILURE_INTERNAL_ERROR,
908 false /* isLegacy */);
909 break;
910 }
911 transactionId = existingClientRequest.mTransactionId;
912 } else {
913 transactionId = getUniqueId();
914 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900915 serviceInfo.setServiceType(registerServiceType);
916 serviceInfo.setServiceName(truncateServiceName(
917 serviceInfo.getServiceName()));
918
Kangping Dong5af24b62023-12-10 21:41:16 +0800919 Set<String> subtypes = new ArraySet<>(serviceInfo.getSubtypes());
Yuyang Huang170d42f2023-12-09 15:26:16 +0900920 for (String subType: typeSubtype.second) {
921 if (!TextUtils.isEmpty(subType)) {
922 subtypes.add(subType);
923 }
Kangping Dong5af24b62023-12-10 21:41:16 +0800924 }
Kangping Dong5af24b62023-12-10 21:41:16 +0800925 subtypes = dedupSubtypeLabels(subtypes);
926
927 if (!checkSubtypeLabels(subtypes)) {
928 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
929 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
930 break;
931 }
932
933 serviceInfo.setSubtypes(subtypes);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900934 maybeStartMonitoringSockets();
Yuyang Huang86d083f2023-12-12 19:56:41 +0900935 final MdnsAdvertisingOptions mdnsAdvertisingOptions =
936 MdnsAdvertisingOptions.newBuilder().setIsOnlyUpdate(
937 isUpdateOnly).build();
Yuyang Huange5cba9c2023-11-02 18:05:47 +0900938 mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
Yuyang Huang86d083f2023-12-12 19:56:41 +0900939 mdnsAdvertisingOptions);
Paul Hud44e1b72023-06-16 02:07:42 +0000940 storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900941 serviceInfo.getNetwork());
Irfan Sheriff75006652012-04-17 23:15:29 -0700942 } else {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900943 maybeStartDaemon();
Yuyang Huang86d083f2023-12-12 19:56:41 +0900944 transactionId = getUniqueId();
Paul Hud44e1b72023-06-16 02:07:42 +0000945 if (registerService(transactionId, serviceInfo)) {
946 if (DBG) {
947 Log.d(TAG, "Register " + clientRequestId
948 + " " + transactionId);
949 }
Paul Hua6bc4632023-06-26 01:18:29 +0000950 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
951 msg.what, mClock.elapsedRealtime());
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900952 // Return success after mDns reports success
953 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000954 unregisterService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800955 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
956 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900957 }
958
Irfan Sheriff75006652012-04-17 23:15:29 -0700959 }
960 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900961 }
962 case NsdManager.UNREGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800963 if (DBG) Log.d(TAG, "unregister service");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900964 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900965 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000966 // If the binder death notification for a INsdManagerCallback was received
967 // before any calls are received by NsdService, the clientInfo would be
968 // cleared and cause NPE. Add a null check here to prevent this corner case.
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900969 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800970 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700971 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700972 }
Paul Hud44e1b72023-06-16 02:07:42 +0000973 final ClientRequest request =
974 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900975 if (request == null) {
976 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE");
977 break;
978 }
Paul Hud44e1b72023-06-16 02:07:42 +0000979 transactionId = request.mTransactionId;
980 removeRequestMap(clientRequestId, transactionId, clientInfo);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900981
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900982 // Note isMdnsAdvertiserEnabled may have changed to false at this point,
983 // so this needs to check the type of the original request to unregister
984 // instead of looking at the flag value.
985 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +0800986 final AdvertiserMetrics metrics =
987 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +0000988 mAdvertiser.removeService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800989 clientInfo.onUnregisterServiceSucceeded(
990 clientRequestId, request, metrics);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700991 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000992 if (unregisterService(transactionId)) {
Paul Hu508a0122023-09-11 15:31:33 +0800993 clientInfo.onUnregisterServiceSucceeded(clientRequestId, request,
Paul Hu043bcd42023-07-14 16:38:25 +0800994 new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
995 NO_PACKET /* sentPacketCount */,
996 0 /* conflictDuringProbingCount */,
997 0 /* conflictAfterProbingCount */));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900998 } else {
999 clientInfo.onUnregisterServiceFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001000 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001001 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001002 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001003 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001004 }
Paul Hu75069ed2023-01-14 00:31:09 +08001005 case NsdManager.RESOLVE_SERVICE: {
paulhub2225702021-11-17 09:35:33 +08001006 if (DBG) Log.d(TAG, "Resolve service");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001007 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001008 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +00001009 // If the binder death notification for a INsdManagerCallback was received
1010 // before any calls are received by NsdService, the clientInfo would be
1011 // cleared and cause NPE. Add a null check here to prevent this corner case.
1012 if (clientInfo == null) {
1013 Log.e(TAG, "Unknown connector in resolution");
1014 break;
1015 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001016
Paul Hu75069ed2023-01-14 00:31:09 +08001017 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001018 transactionId = getUniqueId();
Yuyang Huang170d42f2023-12-09 15:26:16 +09001019 final Pair<String, List<String>> typeSubtype =
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001020 parseTypeAndSubtype(info.getServiceType());
1021 final String serviceType = typeSubtype == null
1022 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +08001023 if (clientInfo.mUseJavaBackend
1024 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001025 || useDiscoveryManagerForType(serviceType)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001026 if (serviceType == null) {
Paul Hu508a0122023-09-11 15:31:33 +08001027 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1028 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001029 break;
1030 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001031 final String resolveServiceType = serviceType + ".local";
Paul Hu75069ed2023-01-14 00:31:09 +08001032
1033 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001034 final MdnsListener listener = new ResolutionListener(clientRequestId,
1035 transactionId, info, resolveServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +08001036 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1037 .setNetwork(info.getNetwork())
1038 .setIsPassiveMode(true)
Remi NGUYEN VANbb62b1d2023-02-27 12:18:27 +09001039 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001040 .setRemoveExpiredService(true)
Paul Hu75069ed2023-01-14 00:31:09 +08001041 .build();
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001042 mMdnsDiscoveryManager.registerListener(
1043 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001044 storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
1045 listener, clientInfo, info.getNetwork());
1046 clientInfo.log("Register a ResolutionListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001047 + " for service type:" + resolveServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -07001048 } else {
Paul Hu75069ed2023-01-14 00:31:09 +08001049 if (clientInfo.mResolvedService != null) {
Paul Hu508a0122023-09-11 15:31:33 +08001050 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1051 NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001052 break;
1053 }
1054
1055 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +00001056 if (resolveService(transactionId, info)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001057 clientInfo.mResolvedService = new NsdServiceInfo();
Paul Hua6bc4632023-06-26 01:18:29 +00001058 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
1059 msg.what, mClock.elapsedRealtime());
Paul Hu75069ed2023-01-14 00:31:09 +08001060 } else {
Paul Hu508a0122023-09-11 15:31:33 +08001061 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1062 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001063 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001064 }
1065 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001066 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001067 case NsdManager.STOP_RESOLUTION: {
Paul Hub58deb72022-12-26 09:24:42 +00001068 if (DBG) Log.d(TAG, "Stop service resolution");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001069 final ListenerArgs args = (ListenerArgs) msg.obj;
Paul Hub58deb72022-12-26 09:24:42 +00001070 clientInfo = mClients.get(args.connector);
1071 // If the binder death notification for a INsdManagerCallback was received
1072 // before any calls are received by NsdService, the clientInfo would be
1073 // cleared and cause NPE. Add a null check here to prevent this corner case.
1074 if (clientInfo == null) {
1075 Log.e(TAG, "Unknown connector in stop resolution");
1076 break;
1077 }
1078
Paul Hud44e1b72023-06-16 02:07:42 +00001079 final ClientRequest request =
1080 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001081 if (request == null) {
1082 Log.e(TAG, "Unknown client request in STOP_RESOLUTION");
1083 break;
1084 }
Paul Hud44e1b72023-06-16 02:07:42 +00001085 transactionId = request.mTransactionId;
Paul Hue4f5f252023-02-16 21:13:47 +08001086 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
1087 // point, so this needs to check the type of the original request to
1088 // unregister instead of looking at the flag value.
1089 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001090 stopDiscoveryManagerRequest(
1091 request, clientRequestId, transactionId, clientInfo);
Paul Hu60149052023-07-31 14:26:08 +08001092 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001093 clientInfo.log("Unregister the ResolutionListener " + transactionId);
Paul Hub58deb72022-12-26 09:24:42 +00001094 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001095 removeRequestMap(clientRequestId, transactionId, clientInfo);
1096 if (stopResolveService(transactionId)) {
Paul Hu60149052023-07-31 14:26:08 +08001097 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hue4f5f252023-02-16 21:13:47 +08001098 } else {
1099 clientInfo.onStopResolutionFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001100 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
Paul Hue4f5f252023-02-16 21:13:47 +08001101 }
1102 clientInfo.mResolvedService = null;
Paul Hub58deb72022-12-26 09:24:42 +00001103 }
Paul Hub58deb72022-12-26 09:24:42 +00001104 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001105 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001106 case NsdManager.REGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001107 if (DBG) Log.d(TAG, "Register a service callback");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001108 final ListenerArgs args = (ListenerArgs) msg.obj;
Paul Hu18aeccc2022-12-27 08:48:48 +00001109 clientInfo = mClients.get(args.connector);
1110 // If the binder death notification for a INsdManagerCallback was received
1111 // before any calls are received by NsdService, the clientInfo would be
1112 // cleared and cause NPE. Add a null check here to prevent this corner case.
1113 if (clientInfo == null) {
1114 Log.e(TAG, "Unknown connector in callback registration");
1115 break;
1116 }
1117
Paul Hu30bd70d2023-02-07 13:20:56 +00001118 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001119 transactionId = getUniqueId();
Yuyang Huang170d42f2023-12-09 15:26:16 +09001120 final Pair<String, List<String>> typeAndSubtype =
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001121 parseTypeAndSubtype(info.getServiceType());
1122 final String serviceType = typeAndSubtype == null
1123 ? null : typeAndSubtype.first;
Paul Hu30bd70d2023-02-07 13:20:56 +00001124 if (serviceType == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001125 clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId,
Paul Hu30bd70d2023-02-07 13:20:56 +00001126 NsdManager.FAILURE_BAD_PARAMETERS);
Paul Hu18aeccc2022-12-27 08:48:48 +00001127 break;
1128 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001129 final String resolveServiceType = serviceType + ".local";
Paul Hu18aeccc2022-12-27 08:48:48 +00001130
Paul Hu30bd70d2023-02-07 13:20:56 +00001131 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001132 final MdnsListener listener = new ServiceInfoListener(clientRequestId,
1133 transactionId, info, resolveServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +00001134 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1135 .setNetwork(info.getNetwork())
1136 .setIsPassiveMode(true)
1137 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001138 .setRemoveExpiredService(true)
Paul Hu30bd70d2023-02-07 13:20:56 +00001139 .build();
1140 mMdnsDiscoveryManager.registerListener(
1141 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001142 storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener,
1143 clientInfo, info.getNetwork());
Paul Huddce5912023-08-01 10:26:49 +08001144 clientInfo.onServiceInfoCallbackRegistered(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00001145 clientInfo.log("Register a ServiceInfoListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001146 + " for service type:" + resolveServiceType);
Paul Hu18aeccc2022-12-27 08:48:48 +00001147 break;
Paul Hu30bd70d2023-02-07 13:20:56 +00001148 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001149 case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001150 if (DBG) Log.d(TAG, "Unregister a service callback");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001151 final ListenerArgs args = (ListenerArgs) msg.obj;
Paul Hu18aeccc2022-12-27 08:48:48 +00001152 clientInfo = mClients.get(args.connector);
1153 // If the binder death notification for a INsdManagerCallback was received
1154 // before any calls are received by NsdService, the clientInfo would be
1155 // cleared and cause NPE. Add a null check here to prevent this corner case.
1156 if (clientInfo == null) {
1157 Log.e(TAG, "Unknown connector in callback unregistration");
1158 break;
1159 }
1160
Paul Hud44e1b72023-06-16 02:07:42 +00001161 final ClientRequest request =
1162 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001163 if (request == null) {
Paul Hu30bd70d2023-02-07 13:20:56 +00001164 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001165 break;
1166 }
Paul Hud44e1b72023-06-16 02:07:42 +00001167 transactionId = request.mTransactionId;
Paul Hu30bd70d2023-02-07 13:20:56 +00001168 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001169 stopDiscoveryManagerRequest(
1170 request, clientRequestId, transactionId, clientInfo);
Paul Huddce5912023-08-01 10:26:49 +08001171 clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001172 clientInfo.log("Unregister the ServiceInfoListener " + transactionId);
Paul Hu18aeccc2022-12-27 08:48:48 +00001173 } else {
Paul Hu30bd70d2023-02-07 13:20:56 +00001174 loge("Unregister failed with non-DiscoveryManagerRequest.");
Paul Hu18aeccc2022-12-27 08:48:48 +00001175 }
Paul Hu18aeccc2022-12-27 08:48:48 +00001176 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001177 }
paulhu2b9ed952022-02-10 21:58:32 +08001178 case MDNS_SERVICE_EVENT:
1179 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +09001180 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001181 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001182 break;
Paul Hu019621e2023-01-13 23:26:49 +08001183 case MDNS_DISCOVERY_MANAGER_EVENT:
1184 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
1185 return NOT_HANDLED;
1186 }
1187 break;
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001188 case NsdManager.REGISTER_OFFLOAD_ENGINE:
1189 offloadEngineInfo = (OffloadEngineInfo) msg.obj;
1190 // TODO: Limits the number of registrations created by a given class.
1191 mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
1192 offloadEngineInfo);
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001193 sendAllOffloadServiceInfos(offloadEngineInfo);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001194 break;
1195 case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
1196 mOffloadEngines.unregister((IOffloadEngine) msg.obj);
1197 break;
Paul Hu77c11182023-10-23 16:17:32 +08001198 case NsdManager.REGISTER_CLIENT:
1199 final ConnectorArgs arg = (ConnectorArgs) msg.obj;
1200 final INsdManagerCallback cb = arg.callback;
1201 try {
1202 cb.asBinder().linkToDeath(arg.connector, 0);
1203 final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
1204 final NetworkNsdReportedMetrics metrics =
1205 mDeps.makeNetworkNsdReportedMetrics(
1206 (int) mClock.elapsedRealtime());
1207 clientInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
1208 mServiceLogs.forSubComponent(tag), metrics);
1209 mClients.put(arg.connector, clientInfo);
1210 } catch (RemoteException e) {
1211 Log.w(TAG, "Client request id " + clientRequestId
1212 + " has already died");
1213 }
1214 break;
1215 case NsdManager.UNREGISTER_CLIENT:
1216 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
1217 clientInfo = mClients.remove(connector);
1218 if (clientInfo != null) {
1219 clientInfo.expungeAllRequests();
1220 if (clientInfo.isPreSClient()) {
1221 mLegacyClientCount -= 1;
1222 }
1223 }
1224 maybeStopMonitoringSocketsIfNoActiveRequest();
1225 maybeScheduleStop();
1226 break;
1227 case NsdManager.DAEMON_CLEANUP:
1228 maybeStopDaemon();
1229 break;
1230 // This event should be only sent by the legacy (target SDK < S) clients.
1231 // Mark the sending client as legacy.
1232 case NsdManager.DAEMON_STARTUP:
1233 clientInfo = getClientInfoForReply(msg);
1234 if (clientInfo != null) {
1235 cancelStop();
1236 clientInfo.setPreSClient();
1237 mLegacyClientCount += 1;
1238 maybeStartDaemon();
1239 }
1240 break;
Irfan Sheriff75006652012-04-17 23:15:29 -07001241 default:
Paul Hu77c11182023-10-23 16:17:32 +08001242 Log.wtf(TAG, "Unhandled " + msg);
Hugo Benichif0c84092017-04-05 14:43:29 +09001243 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001244 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001245 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001246 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001247
Paul Hud44e1b72023-06-16 02:07:42 +00001248 private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001249 NsdServiceInfo servInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001250 ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001251 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001252 Log.e(TAG, String.format(
1253 "transactionId %d for %d has no client mapping", transactionId, code));
Hugo Benichif0c84092017-04-05 14:43:29 +09001254 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001255 }
1256
1257 /* This goes in response as msg.arg2 */
Paul Hud44e1b72023-06-16 02:07:42 +00001258 int clientRequestId = clientInfo.getClientRequestId(transactionId);
1259 if (clientRequestId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -07001260 // This can happen because of race conditions. For example,
1261 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
1262 // and we may get in this situation.
Paul Hud44e1b72023-06-16 02:07:42 +00001263 Log.d(TAG, String.format("%d for transactionId %d that is no longer active",
1264 code, transactionId));
Hugo Benichif0c84092017-04-05 14:43:29 +09001265 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001266 }
Paul Hu812e9212023-06-20 06:24:53 +00001267 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1268 if (request == null) {
1269 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1270 return false;
1271 }
Hugo Benichi32be63d2017-04-05 14:06:11 +09001272 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00001273 Log.d(TAG, String.format(
1274 "MDns service event code:%d transactionId=%d", code, transactionId));
Hugo Benichi32be63d2017-04-05 14:06:11 +09001275 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001276 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +08001277 case IMDnsEventListener.SERVICE_FOUND: {
1278 final DiscoveryInfo info = (DiscoveryInfo) obj;
1279 final String name = info.serviceName;
1280 final String type = info.registrationType;
1281 servInfo = new NsdServiceInfo(name, type);
1282 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001283 if (foundNetId == 0L) {
1284 // Ignore services that do not have a Network: they are not usable
1285 // by apps, as they would need privileged permissions to use
1286 // interfaces that do not have an associated Network.
1287 break;
1288 }
Remi NGUYEN VAN643edb62023-01-23 19:14:57 +09001289 if (foundNetId == INetd.DUMMY_NET_ID) {
1290 // Ignore services on the dummy0 interface: they are only seen when
1291 // discovering locally advertised services, and are not reachable
1292 // through that interface.
1293 break;
1294 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001295 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001296
1297 clientInfo.onServiceFound(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001298 break;
paulhu2b9ed952022-02-10 21:58:32 +08001299 }
1300 case IMDnsEventListener.SERVICE_LOST: {
1301 final DiscoveryInfo info = (DiscoveryInfo) obj;
1302 final String name = info.serviceName;
1303 final String type = info.registrationType;
1304 final int lostNetId = info.netId;
1305 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001306 // The network could be set to null (netId 0) if it was torn down when the
1307 // service is lost
1308 // TODO: avoid returning null in that case, possibly by remembering
1309 // found services on the same interface index and their network at the time
1310 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001311 clientInfo.onServiceLost(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001312 break;
paulhu2b9ed952022-02-10 21:58:32 +08001313 }
1314 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Paul Hu812e9212023-06-20 06:24:53 +00001315 clientInfo.onDiscoverServicesFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001316 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1317 transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001318 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001319 break;
paulhu2b9ed952022-02-10 21:58:32 +08001320 case IMDnsEventListener.SERVICE_REGISTERED: {
1321 final RegistrationInfo info = (RegistrationInfo) obj;
1322 final String name = info.serviceName;
1323 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Paul Hu508a0122023-09-11 15:31:33 +08001324 clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001325 break;
paulhu2b9ed952022-02-10 21:58:32 +08001326 }
1327 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Paul Hu777ed052023-06-19 13:35:15 +00001328 clientInfo.onRegisterServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001329 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1330 transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001331 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001332 break;
paulhu2b9ed952022-02-10 21:58:32 +08001333 case IMDnsEventListener.SERVICE_RESOLVED: {
1334 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001335 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +08001336 final String fullName = info.serviceFullName;
1337 while (index < fullName.length() && fullName.charAt(index) != '.') {
1338 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001339 ++index;
1340 }
1341 ++index;
1342 }
paulhu2b9ed952022-02-10 21:58:32 +08001343 if (index >= fullName.length()) {
1344 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001345 break;
1346 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001347
paulhube186602022-04-12 07:18:23 +00001348 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +08001349 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001350 String type = rest.replace(".local.", "");
1351
Paul Hu30bd70d2023-02-07 13:20:56 +00001352 final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
Paul Hu18aeccc2022-12-27 08:48:48 +00001353 serviceInfo.setServiceName(name);
1354 serviceInfo.setServiceType(type);
1355 serviceInfo.setPort(info.port);
1356 serviceInfo.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001357 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001358
Paul Hud44e1b72023-06-16 02:07:42 +00001359 stopResolveService(transactionId);
1360 removeRequestMap(clientRequestId, transactionId, clientInfo);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001361
Paul Hud44e1b72023-06-16 02:07:42 +00001362 final int transactionId2 = getUniqueId();
1363 if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
1364 storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
Paul Hua6bc4632023-06-26 01:18:29 +00001365 NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001366 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001367 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001368 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1369 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001370 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001371 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001372 }
1373 break;
paulhu2b9ed952022-02-10 21:58:32 +08001374 }
1375 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001376 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001377 stopResolveService(transactionId);
1378 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001379 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001380 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1381 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001382 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001383 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001384 break;
paulhu2b9ed952022-02-10 21:58:32 +08001385 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001386 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001387 stopGetAddrInfo(transactionId);
1388 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001389 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001390 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1391 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001392 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001393 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001394 break;
paulhu2b9ed952022-02-10 21:58:32 +08001395 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001396 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +08001397 final GetAddressInfo info = (GetAddressInfo) obj;
1398 final String address = info.address;
1399 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001400 InetAddress serviceHost = null;
1401 try {
paulhu2b9ed952022-02-10 21:58:32 +08001402 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001403 } catch (UnknownHostException e) {
1404 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
1405 }
1406
1407 // If the resolved service is on an interface without a network, consider it
1408 // as a failure: it would not be usable by apps as they would need
1409 // privileged permissions.
Paul Hu30bd70d2023-02-07 13:20:56 +00001410 if (netId != NETID_UNSET && serviceHost != null) {
1411 clientInfo.mResolvedService.setHost(serviceHost);
1412 setServiceNetworkForCallback(clientInfo.mResolvedService,
1413 netId, info.interfaceIdx);
1414 clientInfo.onResolveServiceSucceeded(
Paul Hua6bc4632023-06-26 01:18:29 +00001415 clientRequestId, clientInfo.mResolvedService, request);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001416 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001417 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001418 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1419 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001420 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001421 }
Paul Hud44e1b72023-06-16 02:07:42 +00001422 stopGetAddrInfo(transactionId);
1423 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001424 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001425 break;
paulhu2b9ed952022-02-10 21:58:32 +08001426 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001427 default:
Hugo Benichif0c84092017-04-05 14:43:29 +09001428 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001429 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001430 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001431 }
Paul Hu019621e2023-01-13 23:26:49 +08001432
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001433 @Nullable
1434 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
1435 final MdnsEvent event, int code) {
Paul Hu019621e2023-01-13 23:26:49 +08001436 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001437 final String[] typeArray = serviceInfo.getServiceType();
1438 final String joinedType;
1439 if (typeArray.length == 0
1440 || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
1441 Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
1442 + Arrays.toString(typeArray));
1443 return null;
1444 } else {
1445 joinedType = TextUtils.join(".",
1446 Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
1447 }
1448 final String serviceType;
1449 switch (code) {
1450 case NsdManager.SERVICE_FOUND:
1451 case NsdManager.SERVICE_LOST:
1452 // For consistency with historical behavior, discovered service types have
1453 // a dot at the end.
1454 serviceType = joinedType + ".";
1455 break;
1456 case RESOLVE_SERVICE_SUCCEEDED:
1457 // For consistency with historical behavior, resolved service types have
1458 // a dot at the beginning.
1459 serviceType = "." + joinedType;
1460 break;
1461 default:
1462 serviceType = joinedType;
1463 break;
1464 }
Paul Hu019621e2023-01-13 23:26:49 +08001465 final String serviceName = serviceInfo.getServiceInstanceName();
1466 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
1467 final Network network = serviceInfo.getNetwork();
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001468 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
1469 // network for Tethering interface. In other words, the network == null means the
1470 // network has netId = INetd.LOCAL_NET_ID.
Paul Hu019621e2023-01-13 23:26:49 +08001471 setServiceNetworkForCallback(
1472 servInfo,
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001473 network == null ? INetd.LOCAL_NET_ID : network.netId,
Paul Hu019621e2023-01-13 23:26:49 +08001474 serviceInfo.getInterfaceIndex());
Kangping Dong5af24b62023-12-10 21:41:16 +08001475 servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes()));
Paul Hu019621e2023-01-13 23:26:49 +08001476 return servInfo;
1477 }
1478
1479 private boolean handleMdnsDiscoveryManagerEvent(
1480 int transactionId, int code, Object obj) {
Paul Hud44e1b72023-06-16 02:07:42 +00001481 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Paul Hu019621e2023-01-13 23:26:49 +08001482 if (clientInfo == null) {
1483 Log.e(TAG, String.format(
1484 "id %d for %d has no client mapping", transactionId, code));
1485 return false;
1486 }
1487
1488 final MdnsEvent event = (MdnsEvent) obj;
Paul Hud44e1b72023-06-16 02:07:42 +00001489 final int clientRequestId = event.mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +08001490 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1491 if (request == null) {
1492 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1493 return false;
1494 }
1495
1496 // Deal with the discovery sent callback
1497 if (code == DISCOVERY_QUERY_SENT_CALLBACK) {
1498 request.onQuerySent();
1499 return true;
1500 }
1501
1502 // Deal with other callbacks.
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001503 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
1504 // Errors are already logged if null
1505 if (info == null) return false;
Paul Hu83ec7f42023-06-07 18:04:09 +08001506 mServiceLogs.log(String.format(
1507 "MdnsDiscoveryManager event code=%s transactionId=%d",
1508 NsdManager.nameOf(code), transactionId));
Paul Hu019621e2023-01-13 23:26:49 +08001509 switch (code) {
1510 case NsdManager.SERVICE_FOUND:
Paul Hu812e9212023-06-20 06:24:53 +00001511 clientInfo.onServiceFound(clientRequestId, info, request);
Paul Hu319751a2023-01-13 23:56:34 +08001512 break;
1513 case NsdManager.SERVICE_LOST:
Paul Hu812e9212023-06-20 06:24:53 +00001514 clientInfo.onServiceLost(clientRequestId, info, request);
Paul Hu019621e2023-01-13 23:26:49 +08001515 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001516 case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
1517 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Paul Hu75069ed2023-01-14 00:31:09 +08001518 info.setPort(serviceInfo.getPort());
1519
1520 Map<String, String> attrs = serviceInfo.getAttributes();
1521 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1522 final String key = kv.getKey();
1523 try {
1524 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1525 } catch (IllegalArgumentException e) {
1526 Log.e(TAG, "Invalid attribute", e);
1527 }
1528 }
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001529 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu2b865912023-03-06 14:27:53 +08001530 if (addresses.size() != 0) {
1531 info.setHostAddresses(addresses);
Paul Hua6bc4632023-06-26 01:18:29 +00001532 request.setServiceFromCache(event.mIsServiceFromCache);
1533 clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
Paul Hu2b865912023-03-06 14:27:53 +08001534 } else {
1535 // No address. Notify resolution failure.
Paul Hua6bc4632023-06-26 01:18:29 +00001536 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001537 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */,
1538 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001539 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu75069ed2023-01-14 00:31:09 +08001540 }
1541
1542 // Unregister the listener immediately like IMDnsEventListener design
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001543 if (!(request instanceof DiscoveryManagerRequest)) {
1544 Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event");
1545 break;
1546 }
Paul Hud44e1b72023-06-16 02:07:42 +00001547 stopDiscoveryManagerRequest(
1548 request, clientRequestId, transactionId, clientInfo);
Paul Hu75069ed2023-01-14 00:31:09 +08001549 break;
1550 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001551 case NsdManager.SERVICE_UPDATED: {
1552 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1553 info.setPort(serviceInfo.getPort());
1554
1555 Map<String, String> attrs = serviceInfo.getAttributes();
1556 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1557 final String key = kv.getKey();
1558 try {
1559 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1560 } catch (IllegalArgumentException e) {
1561 Log.e(TAG, "Invalid attribute", e);
1562 }
1563 }
1564
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001565 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001566 info.setHostAddresses(addresses);
Paul Huddce5912023-08-01 10:26:49 +08001567 clientInfo.onServiceUpdated(clientRequestId, info, request);
1568 // Set the ServiceFromCache flag only if the service is actually being
1569 // retrieved from the cache. This flag should not be overridden by later
1570 // service updates, which may not be cached.
1571 if (event.mIsServiceFromCache) {
1572 request.setServiceFromCache(true);
1573 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001574 break;
1575 }
1576 case NsdManager.SERVICE_UPDATED_LOST:
Paul Huddce5912023-08-01 10:26:49 +08001577 clientInfo.onServiceUpdatedLost(clientRequestId, request);
Paul Hu30bd70d2023-02-07 13:20:56 +00001578 break;
Paul Hu019621e2023-01-13 23:26:49 +08001579 default:
1580 return false;
1581 }
1582 return true;
1583 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001584 }
1585 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001586
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001587 @NonNull
1588 private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
1589 final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
1590 final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
1591 final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
1592 for (String ipv4Address : v4Addrs) {
1593 try {
1594 addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
1595 } catch (IllegalArgumentException e) {
1596 Log.wtf(TAG, "Invalid ipv4 address", e);
1597 }
1598 }
1599 for (String ipv6Address : v6Addrs) {
1600 try {
Yuyang Huanga6a6ff92023-04-24 13:33:34 +09001601 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
1602 ipv6Address);
1603 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
1604 } catch (IllegalArgumentException e) {
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001605 Log.wtf(TAG, "Invalid ipv6 address", e);
1606 }
1607 }
1608 return addresses;
1609 }
1610
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001611 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
1612 switch (netId) {
1613 case NETID_UNSET:
1614 info.setNetwork(null);
1615 break;
1616 case INetd.LOCAL_NET_ID:
1617 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
1618 // visible / usable for apps, so do not return it. Store the interface
1619 // index instead, so at least if the client tries to resolve the service
1620 // with that NsdServiceInfo, it will be done on the same interface.
1621 // If they recreate the NsdServiceInfo themselves, resolution would be
1622 // done on all interfaces as before T, which should also work.
1623 info.setNetwork(null);
1624 info.setInterfaceIndex(ifaceIdx);
1625 break;
1626 default:
1627 info.setNetwork(new Network(netId));
1628 }
1629 }
1630
paulhube186602022-04-12 07:18:23 +00001631 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
1632 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
1633 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
1634 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
1635 private String unescape(String s) {
1636 StringBuilder sb = new StringBuilder(s.length());
1637 for (int i = 0; i < s.length(); ++i) {
1638 char c = s.charAt(i);
1639 if (c == '\\') {
1640 if (++i >= s.length()) {
1641 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1642 break;
1643 }
1644 c = s.charAt(i);
1645 if (c != '.' && c != '\\') {
1646 if (i + 2 >= s.length()) {
1647 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1648 break;
1649 }
1650 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
1651 + (s.charAt(i + 2) - '0'));
1652 i += 2;
1653 }
1654 }
1655 sb.append(c);
1656 }
1657 return sb.toString();
1658 }
1659
Paul Hu7445e3d2023-03-03 15:14:00 +08001660 /**
1661 * Check the given service type is valid and construct it to a service type
1662 * which can use for discovery / resolution service.
1663 *
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001664 * <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 +08001665 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
1666 * underscore; they are alphanumerical characters or dashes or underscore, except the
1667 * last one that is just alphanumerical. The last label must be _tcp or _udp.
1668 *
Yuyang Huang170d42f2023-12-09 15:26:16 +09001669 * <p>The subtypes may also be specified with a comma after the service type, for example
1670 * _type._tcp,_subtype1,_subtype2
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001671 *
Paul Hu7445e3d2023-03-03 15:14:00 +08001672 * @param serviceType the request service type for discovery / resolution service
1673 * @return constructed service type or null if the given service type is invalid.
1674 */
1675 @Nullable
Yuyang Huang170d42f2023-12-09 15:26:16 +09001676 public static Pair<String, List<String>> parseTypeAndSubtype(String serviceType) {
Paul Hu7445e3d2023-03-03 15:14:00 +08001677 if (TextUtils.isEmpty(serviceType)) return null;
Yuyang Huang86d083f2023-12-12 19:56:41 +09001678 final Pattern serviceTypePattern = Pattern.compile(TYPE_REGEX);
Paul Hu7445e3d2023-03-03 15:14:00 +08001679 final Matcher matcher = serviceTypePattern.matcher(serviceType);
1680 if (!matcher.matches()) return null;
Yuyang Huang170d42f2023-12-09 15:26:16 +09001681 final String queryType = matcher.group(2);
1682 // Use the subtype at the beginning
1683 if (matcher.group(1) != null) {
1684 return new Pair<>(queryType, List.of(matcher.group(1)));
1685 }
1686 // Use the subtypes at the end
1687 final String subTypesStr = matcher.group(3);
1688 if (subTypesStr != null && !subTypesStr.isEmpty()) {
1689 final String[] subTypes = subTypesStr.substring(1).split(",");
1690 return new Pair<>(queryType, List.of(subTypes));
1691 }
1692
1693 return new Pair<>(queryType, Collections.emptyList());
Paul Hu7445e3d2023-03-03 15:14:00 +08001694 }
1695
Kangping Dong5af24b62023-12-10 21:41:16 +08001696 /** Returns {@code true} if {@code subtype} is a valid DNS-SD subtype label. */
1697 private static boolean checkSubtypeLabel(String subtype) {
1698 return Pattern.compile("^" + TYPE_SUBTYPE_LABEL_REGEX + "$").matcher(subtype).matches();
1699 }
1700
Hugo Benichi803a2f02017-04-24 11:35:06 +09001701 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +08001702 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Paul Hu4bd98ef2023-01-12 13:42:07 +08001703 this(ctx, handler, cleanupDelayMs, new Dependencies());
1704 }
1705
1706 @VisibleForTesting
1707 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
Luke Huang05298582021-06-13 16:52:05 +00001708 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001709 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001710 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -07001711 mNsdStateMachine.start();
Ken Chen80c9f6f2023-11-15 18:24:54 +08001712 // It can fail on V+ device since mdns native service provided by netd is removed.
1713 mMDnsManager = SdkLevel.isAtLeastV() ? null : ctx.getSystemService(MDnsManager.class);
paulhu2b9ed952022-02-10 21:58:32 +08001714 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001715 mDeps = deps;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001716
Paul Hu14667de2023-04-17 22:42:47 +08001717 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001718 LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor());
Yuyang Huang700778b2023-03-08 16:17:05 +09001719 // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
Yuyang Huangfca402a2023-05-24 14:45:59 +09001720 // address events are received. When the netlink monitor starts, any IP addresses already
1721 // on the interfaces will not be seen. In practice, the network will not connect at boot
1722 // time As a result, all the netlink message should be observed if the netlink monitor
1723 // starts here.
Yuyang Huang700778b2023-03-08 16:17:05 +09001724 handler.post(mMdnsSocketProvider::startNetLinkMonitor);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001725
1726 // NsdService is started after ActivityManager (startOtherServices in SystemServer, vs.
1727 // startBootstrapServices).
1728 mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt(
1729 MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF,
1730 DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF);
1731 final ActivityManager am = ctx.getSystemService(ActivityManager.class);
1732 am.addOnUidImportanceListener(new UidImportanceListener(handler),
1733 mRunningAppActiveImportanceCutoff);
1734
Paul Huf3fe3332023-10-16 17:13:25 +08001735 final MdnsFeatureFlags flags = new MdnsFeatureFlags.Builder()
1736 .setIsMdnsOffloadFeatureEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1737 mContext, MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD))
1738 .setIncludeInetAddressRecordsInProbing(mDeps.isFeatureEnabled(
1739 mContext, MdnsFeatureFlags.INCLUDE_INET_ADDRESS_RECORDS_IN_PROBING))
Paul Hu596a5002023-10-18 17:07:31 +08001740 .setIsExpiredServicesRemovalEnabled(mDeps.isFeatureEnabled(
1741 mContext, MdnsFeatureFlags.NSD_EXPIRED_SERVICES_REMOVAL))
Paul Hufd357ef2023-11-01 16:32:45 +08001742 .setIsLabelCountLimitEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1743 mContext, MdnsFeatureFlags.NSD_LIMIT_LABEL_COUNT))
Paul Hu01f243f2023-11-22 17:26:36 +08001744 .setIsKnownAnswerSuppressionEnabled(mDeps.isFeatureEnabled(
1745 mContext, MdnsFeatureFlags.NSD_KNOWN_ANSWER_SUPPRESSION))
Paul Huf3fe3332023-10-16 17:13:25 +08001746 .build();
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001747 mMdnsSocketClient =
Yuyang Huang7ddf2932023-08-01 18:16:30 +09001748 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
Paul Hufd357ef2023-11-01 16:32:45 +08001749 LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), flags);
Paul Hu14667de2023-04-17 22:42:47 +08001750 mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
Paul Huf3fe3332023-10-16 17:13:25 +08001751 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"), flags);
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001752 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
1753 mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
Yuyang Huangb96a0712023-09-07 15:13:15 +09001754 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"), flags);
Paul Hu777ed052023-06-19 13:35:15 +00001755 mClock = deps.makeClock();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001756 }
1757
1758 /**
1759 * Dependencies of NsdService, for injection in tests.
1760 */
1761 @VisibleForTesting
1762 public static class Dependencies {
1763 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001764 * Check whether the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001765 *
1766 * @param context The global context information about an app environment.
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001767 * @return true if the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001768 */
1769 public boolean isMdnsDiscoveryManagerEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001770 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001771 MDNS_DISCOVERY_MANAGER_VERSION);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001772 }
1773
1774 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001775 * Check whether the MdnsAdvertiser feature is enabled.
1776 *
1777 * @param context The global context information about an app environment.
1778 * @return true if the MdnsAdvertiser feature is enabled.
1779 */
1780 public boolean isMdnsAdvertiserEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001781 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001782 MDNS_ADVERTISER_VERSION);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001783 }
1784
1785 /**
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001786 * Get the type allowlist flag value.
1787 * @see #MDNS_TYPE_ALLOWLIST_FLAGS
1788 */
1789 @Nullable
1790 public String getTypeAllowlistFlags() {
1791 return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
1792 MDNS_TYPE_ALLOWLIST_FLAGS, null);
1793 }
1794
1795 /**
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001796 * @see DeviceConfigUtils#isTetheringFeatureEnabled
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001797 */
1798 public boolean isFeatureEnabled(Context context, String feature) {
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001799 return DeviceConfigUtils.isTetheringFeatureEnabled(context, feature);
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001800 }
1801
1802 /**
Yuyang Huangb96a0712023-09-07 15:13:15 +09001803 * @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut
1804 */
Motomu Utsumied4e7ec2023-09-13 14:58:32 +09001805 public boolean isTetheringFeatureNotChickenedOut(Context context, String feature) {
1806 return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, feature);
Yuyang Huangb96a0712023-09-07 15:13:15 +09001807 }
1808
1809 /**
Paul Huf3fe3332023-10-16 17:13:25 +08001810 * @see DeviceConfigUtils#isTrunkStableFeatureEnabled
1811 */
1812 public boolean isTrunkStableFeatureEnabled(String feature) {
1813 return DeviceConfigUtils.isTrunkStableFeatureEnabled(feature);
1814 }
1815
1816 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001817 * @see MdnsDiscoveryManager
1818 */
1819 public MdnsDiscoveryManager makeMdnsDiscoveryManager(
Paul Hu14667de2023-04-17 22:42:47 +08001820 @NonNull ExecutorProvider executorProvider,
Paul Huf3fe3332023-10-16 17:13:25 +08001821 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog,
1822 @NonNull MdnsFeatureFlags featureFlags) {
1823 return new MdnsDiscoveryManager(
1824 executorProvider, socketClient, sharedLog, featureFlags);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001825 }
1826
1827 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001828 * @see MdnsAdvertiser
1829 */
1830 public MdnsAdvertiser makeMdnsAdvertiser(
1831 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
Yuyang Huangb96a0712023-09-07 15:13:15 +09001832 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog,
1833 MdnsFeatureFlags featureFlags) {
1834 return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog, featureFlags);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001835 }
1836
1837 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001838 * @see MdnsSocketProvider
1839 */
Paul Hu14667de2023-04-17 22:42:47 +08001840 public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001841 @NonNull Looper looper, @NonNull SharedLog sharedLog,
1842 @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) {
1843 return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback);
1844 }
1845
1846 /**
1847 * @see DeviceConfig#getInt(String, String, int)
1848 */
1849 public int getDeviceConfigInt(@NonNull String config, int defaultValue) {
1850 return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue);
1851 }
1852
1853 /**
1854 * @see Binder#getCallingUid()
1855 */
1856 public int getCallingUid() {
1857 return Binder.getCallingUid();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001858 }
Paul Hu777ed052023-06-19 13:35:15 +00001859
1860 /**
1861 * @see NetworkNsdReportedMetrics
1862 */
Paul Hu508a0122023-09-11 15:31:33 +08001863 public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(int clientId) {
1864 return new NetworkNsdReportedMetrics(clientId);
Paul Hu777ed052023-06-19 13:35:15 +00001865 }
1866
1867 /**
1868 * @see MdnsUtils.Clock
1869 */
1870 public Clock makeClock() {
1871 return new Clock();
1872 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001873 }
1874
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001875 /**
1876 * Return whether a type is allowlisted to use the Java backend.
1877 * @param type The service type
1878 * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
1879 * {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
1880 */
1881 private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
1882 @NonNull String flagPrefix) {
1883 if (type == null) return false;
1884 final String typesConfig = mDeps.getTypeAllowlistFlags();
1885 if (TextUtils.isEmpty(typesConfig)) return false;
1886
1887 final String mappingPrefix = type + ":";
1888 String mappedFlag = null;
1889 for (String mapping : TextUtils.split(typesConfig, ",")) {
1890 if (mapping.startsWith(mappingPrefix)) {
1891 mappedFlag = mapping.substring(mappingPrefix.length());
1892 break;
1893 }
1894 }
1895
1896 if (mappedFlag == null) return false;
1897
1898 return mDeps.isFeatureEnabled(mContext,
1899 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
1900 }
1901
1902 private boolean useDiscoveryManagerForType(@Nullable String type) {
1903 return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
1904 }
1905
1906 private boolean useAdvertiserForType(@Nullable String type) {
1907 return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
1908 }
1909
paulhu1b35e822022-04-08 14:48:41 +08001910 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09001911 HandlerThread thread = new HandlerThread(TAG);
1912 thread.start();
1913 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +08001914 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001915 return service;
1916 }
1917
paulhu2b9ed952022-02-10 21:58:32 +08001918 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
1919 private final StateMachine mStateMachine;
1920
1921 MDnsEventCallback(StateMachine sm) {
1922 mStateMachine = sm;
1923 }
1924
1925 @Override
1926 public void onServiceRegistrationStatus(final RegistrationInfo status) {
1927 mStateMachine.sendMessage(
1928 MDNS_SERVICE_EVENT, status.result, status.id, status);
1929 }
1930
1931 @Override
1932 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
1933 mStateMachine.sendMessage(
1934 MDNS_SERVICE_EVENT, status.result, status.id, status);
1935 }
1936
1937 @Override
1938 public void onServiceResolutionStatus(final ResolutionInfo status) {
1939 mStateMachine.sendMessage(
1940 MDNS_SERVICE_EVENT, status.result, status.id, status);
1941 }
1942
1943 @Override
1944 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
1945 mStateMachine.sendMessage(
1946 MDNS_SERVICE_EVENT, status.result, status.id, status);
1947 }
1948
1949 @Override
1950 public int getInterfaceVersion() throws RemoteException {
1951 return this.VERSION;
1952 }
1953
1954 @Override
1955 public String getInterfaceHash() throws RemoteException {
1956 return this.HASH;
1957 }
1958 }
1959
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001960 private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) {
1961 final String targetInterface = offloadEngineInfo.mInterfaceName;
1962 final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine;
1963 final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers =
1964 mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface);
1965 for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) {
1966 try {
1967 offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo);
1968 } catch (RemoteException e) {
1969 // Can happen in regular cases, do not log a stacktrace
1970 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
1971 }
1972 }
1973 }
1974
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001975 private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
1976 @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
1977 final int count = mOffloadEngines.beginBroadcast();
1978 try {
1979 for (int i = 0; i < count; i++) {
1980 final OffloadEngineInfo offloadEngineInfo =
1981 (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
1982 final String interfaceName = offloadEngineInfo.mInterfaceName;
1983 if (!targetInterfaceName.equals(interfaceName)
1984 || ((offloadEngineInfo.mOffloadType
1985 & offloadServiceInfo.getOffloadType()) == 0)) {
1986 continue;
1987 }
1988 try {
1989 if (isRemove) {
1990 mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
1991 offloadServiceInfo);
1992 } else {
1993 mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
1994 offloadServiceInfo);
1995 }
1996 } catch (RemoteException e) {
1997 // Can happen in regular cases, do not log a stacktrace
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001998 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001999 }
2000 }
2001 } finally {
2002 mOffloadEngines.finishBroadcast();
2003 }
2004 }
2005
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002006 private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002007 // TODO: add a callback to notify when a service is being added on each interface (as soon
2008 // as probing starts), and call mOffloadCallbacks. This callback is for
2009 // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
2010
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002011 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00002012 public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
2013 mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
2014 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002015 if (clientInfo == null) return;
2016
Paul Hud44e1b72023-06-16 02:07:42 +00002017 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
2018 if (clientRequestId < 0) return;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002019
2020 // onRegisterServiceSucceeded only has the service name in its info. This aligns with
2021 // historical behavior.
2022 final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
Paul Hu777ed052023-06-19 13:35:15 +00002023 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu508a0122023-09-11 15:31:33 +08002024 clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, request);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002025 }
2026
2027 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00002028 public void onRegisterServiceFailed(int transactionId, int errorCode) {
2029 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002030 if (clientInfo == null) return;
2031
Paul Hud44e1b72023-06-16 02:07:42 +00002032 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
2033 if (clientRequestId < 0) return;
Paul Hu777ed052023-06-19 13:35:15 +00002034 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu508a0122023-09-11 15:31:33 +08002035 clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, false /* isLegacy */,
2036 transactionId, request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002037 }
2038
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002039 @Override
2040 public void onOffloadStartOrUpdate(@NonNull String interfaceName,
2041 @NonNull OffloadServiceInfo offloadServiceInfo) {
2042 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
2043 }
2044
2045 @Override
2046 public void onOffloadStop(@NonNull String interfaceName,
2047 @NonNull OffloadServiceInfo offloadServiceInfo) {
2048 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
2049 }
2050
Paul Hud44e1b72023-06-16 02:07:42 +00002051 private ClientInfo getClientInfoOrLog(int transactionId) {
2052 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002053 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00002054 Log.e(TAG, String.format("Callback for service %d has no client", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002055 }
2056 return clientInfo;
2057 }
2058
Paul Hud44e1b72023-06-16 02:07:42 +00002059 private int getClientRequestIdOrLog(@NonNull ClientInfo info, int transactionId) {
2060 final int clientRequestId = info.getClientRequestId(transactionId);
2061 if (clientRequestId < 0) {
2062 Log.e(TAG, String.format(
2063 "Client request ID not found for service %d", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002064 }
Paul Hud44e1b72023-06-16 02:07:42 +00002065 return clientRequestId;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002066 }
2067 }
2068
Paul Hu2e0a88c2023-03-09 16:05:01 +08002069 private static class ConnectorArgs {
2070 @NonNull public final NsdServiceConnector connector;
2071 @NonNull public final INsdManagerCallback callback;
2072 public final boolean useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002073 public final int uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002074
2075 ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
Paul Hub2e67d32023-04-18 05:50:14 +00002076 boolean useJavaBackend, int uid) {
Paul Hu2e0a88c2023-03-09 16:05:01 +08002077 this.connector = connector;
2078 this.callback = callback;
2079 this.useJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002080 this.uid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002081 }
2082 }
2083
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002084 @Override
Paul Hu2e0a88c2023-03-09 16:05:01 +08002085 public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09002086 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Paul Hu101dbf52023-08-09 16:05:20 +08002087 final int uid = mDeps.getCallingUid();
2088 if (cb == null) {
2089 throw new IllegalArgumentException("Unknown client callback from uid=" + uid);
2090 }
Paul Hu2e0a88c2023-03-09 16:05:01 +08002091 if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002092 final INsdServiceConnector connector = new NsdServiceConnector();
Paul Hub2e67d32023-04-18 05:50:14 +00002093 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
Paul Hu101dbf52023-08-09 16:05:20 +08002094 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002095 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -07002096 }
2097
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002098 private static class ListenerArgs {
2099 public final NsdServiceConnector connector;
2100 public final NsdServiceInfo serviceInfo;
2101 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
2102 this.connector = connector;
2103 this.serviceInfo = serviceInfo;
2104 }
2105 }
2106
Yuyang Huang86d083f2023-12-12 19:56:41 +09002107 private static class AdvertisingArgs {
2108 public final NsdServiceConnector connector;
2109 public final AdvertisingRequest advertisingRequest;
2110
2111 AdvertisingArgs(NsdServiceConnector connector, AdvertisingRequest advertisingRequest) {
2112 this.connector = connector;
2113 this.advertisingRequest = advertisingRequest;
2114 }
2115 }
2116
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002117 private class NsdServiceConnector extends INsdServiceConnector.Stub
2118 implements IBinder.DeathRecipient {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002119
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002120 @Override
Yuyang Huang86d083f2023-12-12 19:56:41 +09002121 public void registerService(int listenerKey, AdvertisingRequest advertisingRequest)
2122 throws RemoteException {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002123 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2124 NsdManager.REGISTER_SERVICE, 0, listenerKey,
Yuyang Huang86d083f2023-12-12 19:56:41 +09002125 new AdvertisingArgs(this, advertisingRequest)
2126 ));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002127 }
2128
2129 @Override
2130 public void unregisterService(int listenerKey) {
2131 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2132 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
Yuyang Huang86d083f2023-12-12 19:56:41 +09002133 new ListenerArgs(this, (NsdServiceInfo) null)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002134 }
2135
2136 @Override
2137 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
2138 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2139 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
2140 new ListenerArgs(this, serviceInfo)));
2141 }
2142
2143 @Override
2144 public void stopDiscovery(int listenerKey) {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002145 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_DISCOVERY,
2146 0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002147 }
2148
2149 @Override
2150 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
2151 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2152 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
2153 new ListenerArgs(this, serviceInfo)));
2154 }
2155
2156 @Override
Paul Hub58deb72022-12-26 09:24:42 +00002157 public void stopResolution(int listenerKey) {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002158 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_RESOLUTION,
2159 0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null)));
Paul Hub58deb72022-12-26 09:24:42 +00002160 }
2161
2162 @Override
Paul Hu18aeccc2022-12-27 08:48:48 +00002163 public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
2164 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2165 NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
2166 new ListenerArgs(this, serviceInfo)));
2167 }
2168
2169 @Override
2170 public void unregisterServiceInfoCallback(int listenerKey) {
2171 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2172 NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
Yuyang Huang86d083f2023-12-12 19:56:41 +09002173 new ListenerArgs(this, (NsdServiceInfo) null)));
Paul Hu18aeccc2022-12-27 08:48:48 +00002174 }
2175
2176 @Override
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002177 public void startDaemon() {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002178 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.DAEMON_STARTUP,
2179 new ListenerArgs(this, (NsdServiceInfo) null)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002180 }
2181
2182 @Override
2183 public void binderDied() {
2184 mNsdStateMachine.sendMessage(
2185 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002186
2187 }
2188
2189 @Override
2190 public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
2191 @OffloadEngine.OffloadCapability long offloadCapabilities,
2192 @OffloadEngine.OffloadType long offloadTypes) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002193 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002194 Objects.requireNonNull(ifaceName);
2195 Objects.requireNonNull(cb);
2196 mNsdStateMachine.sendMessage(
2197 mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
2198 new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
2199 offloadTypes)));
2200 }
2201
2202 @Override
2203 public void unregisterOffloadEngine(IOffloadEngine cb) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002204 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002205 Objects.requireNonNull(cb);
2206 mNsdStateMachine.sendMessage(
2207 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002208 }
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002209
2210 private static void checkOffloadEnginePermission(Context context) {
2211 if (!SdkLevel.isAtLeastT()) {
2212 throw new SecurityException("API is not available in before API level 33");
2213 }
Yuyang Huangd5896e72023-11-28 13:23:59 +09002214
Ken Chena7bae552023-12-27 11:40:57 +08002215 final ArrayList<String> permissionsList = new ArrayList<>(Arrays.asList(NETWORK_STACK,
2216 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS));
2217
2218 if (SdkLevel.isAtLeastV()) {
2219 // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V.
2220 permissionsList.add(REGISTER_NSD_OFFLOAD_ENGINE);
2221 } else if (SdkLevel.isAtLeastU()) {
2222 // REGISTER_NSD_OFFLOAD_ENGINE cannot be backport to U. In U, check the DEVICE_POWER
2223 // permission instead.
2224 permissionsList.add(DEVICE_POWER);
Yuyang Huangd5896e72023-11-28 13:23:59 +09002225 }
2226
Ken Chena7bae552023-12-27 11:40:57 +08002227 if (PermissionUtils.checkAnyPermissionOf(context,
2228 permissionsList.toArray(new String[0]))) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002229 return;
2230 }
2231 throw new SecurityException("Requires one of the following permissions: "
Ken Chena7bae552023-12-27 11:40:57 +08002232 + String.join(", ", permissionsList) + ".");
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002233 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002234 }
2235
Hugo Benichi912db992017-04-24 16:41:03 +09002236 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -07002237 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -07002238 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +09002239 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
2240 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -07002241 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -07002242 }
2243
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002244 private int getUniqueId() {
2245 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
2246 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002247 }
2248
Paul Hud44e1b72023-06-16 02:07:42 +00002249 private boolean registerService(int transactionId, NsdServiceInfo service) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002250 if (mMDnsManager == null) {
2251 Log.wtf(TAG, "registerService: mMDnsManager is null");
2252 return false;
2253 }
2254
Hugo Benichi6d706442017-04-24 16:19:58 +09002255 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002256 Log.d(TAG, "registerService: " + transactionId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002257 }
Hugo Benichi6d706442017-04-24 16:19:58 +09002258 String name = service.getServiceName();
2259 String type = service.getServiceType();
2260 int port = service.getPort();
2261 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002262 final int registerInterface = getNetworkInterfaceIndex(service);
2263 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +08002264 Log.e(TAG, "Interface to register service on not found");
2265 return false;
2266 }
Paul Hud44e1b72023-06-16 02:07:42 +00002267 return mMDnsManager.registerService(
2268 transactionId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002269 }
2270
Paul Hud44e1b72023-06-16 02:07:42 +00002271 private boolean unregisterService(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002272 if (mMDnsManager == null) {
2273 Log.wtf(TAG, "unregisterService: mMDnsManager is null");
2274 return false;
2275 }
Paul Hud44e1b72023-06-16 02:07:42 +00002276 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002277 }
2278
Paul Hud44e1b72023-06-16 02:07:42 +00002279 private boolean discoverServices(int transactionId, NsdServiceInfo serviceInfo) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002280 if (mMDnsManager == null) {
2281 Log.wtf(TAG, "discoverServices: mMDnsManager is null");
2282 return false;
2283 }
2284
paulhu2b9ed952022-02-10 21:58:32 +08002285 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002286 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
2287 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002288 Log.e(TAG, "Interface to discover service on not found");
2289 return false;
2290 }
Paul Hud44e1b72023-06-16 02:07:42 +00002291 return mMDnsManager.discover(transactionId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002292 }
2293
Paul Hud44e1b72023-06-16 02:07:42 +00002294 private boolean stopServiceDiscovery(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002295 if (mMDnsManager == null) {
2296 Log.wtf(TAG, "stopServiceDiscovery: mMDnsManager is null");
2297 return false;
2298 }
Paul Hud44e1b72023-06-16 02:07:42 +00002299 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002300 }
2301
Paul Hud44e1b72023-06-16 02:07:42 +00002302 private boolean resolveService(int transactionId, NsdServiceInfo service) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002303 if (mMDnsManager == null) {
2304 Log.wtf(TAG, "resolveService: mMDnsManager is null");
2305 return false;
2306 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002307 final String name = service.getServiceName();
2308 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002309 final int resolveInterface = getNetworkInterfaceIndex(service);
2310 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002311 Log.e(TAG, "Interface to resolve service on not found");
2312 return false;
2313 }
Paul Hud44e1b72023-06-16 02:07:42 +00002314 return mMDnsManager.resolve(transactionId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002315 }
2316
2317 /**
2318 * Guess the interface to use to resolve or discover a service on a specific network.
2319 *
2320 * This is an imperfect guess, as for example the network may be gone or not yet fully
2321 * registered. This is fine as failing is correct if the network is gone, and a client
2322 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
2323 * this is to support the legacy mdnsresponder implementation, which historically resolved
2324 * services on an unspecified network.
2325 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002326 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
2327 final Network network = serviceInfo.getNetwork();
2328 if (network == null) {
2329 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
2330 // provided by NsdService from discovery results, and the service was found on an
2331 // interface that has no app-usable Network).
2332 if (serviceInfo.getInterfaceIndex() != 0) {
2333 return serviceInfo.getInterfaceIndex();
2334 }
2335 return IFACE_IDX_ANY;
2336 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002337
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002338 String interfaceName = getNetworkInterfaceName(network);
2339 if (interfaceName == null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002340 return IFACE_IDX_ANY;
2341 }
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002342 return getNetworkInterfaceIndexByName(interfaceName);
2343 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002344
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002345 private String getNetworkInterfaceName(@Nullable Network network) {
2346 if (network == null) {
2347 return null;
2348 }
2349 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
2350 if (cm == null) {
2351 Log.wtf(TAG, "No ConnectivityManager");
2352 return null;
2353 }
2354 final LinkProperties lp = cm.getLinkProperties(network);
2355 if (lp == null) {
2356 return null;
2357 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002358 // Only resolve on non-stacked interfaces
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002359 return lp.getInterfaceName();
2360 }
2361
2362 private int getNetworkInterfaceIndexByName(final String ifaceName) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002363 final NetworkInterface iface;
2364 try {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002365 iface = NetworkInterface.getByName(ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002366 } catch (SocketException e) {
2367 Log.e(TAG, "Error querying interface", e);
2368 return IFACE_IDX_ANY;
2369 }
2370
2371 if (iface == null) {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002372 Log.e(TAG, "Interface not found: " + ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002373 return IFACE_IDX_ANY;
2374 }
2375
2376 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002377 }
2378
Paul Hud44e1b72023-06-16 02:07:42 +00002379 private boolean stopResolveService(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002380 if (mMDnsManager == null) {
2381 Log.wtf(TAG, "stopResolveService: mMDnsManager is null");
2382 return false;
2383 }
Paul Hud44e1b72023-06-16 02:07:42 +00002384 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002385 }
2386
Paul Hud44e1b72023-06-16 02:07:42 +00002387 private boolean getAddrInfo(int transactionId, String hostname, int interfaceIdx) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002388 if (mMDnsManager == null) {
2389 Log.wtf(TAG, "getAddrInfo: mMDnsManager is null");
2390 return false;
2391 }
Paul Hud44e1b72023-06-16 02:07:42 +00002392 return mMDnsManager.getServiceAddress(transactionId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002393 }
2394
Paul Hud44e1b72023-06-16 02:07:42 +00002395 private boolean stopGetAddrInfo(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002396 if (mMDnsManager == null) {
2397 Log.wtf(TAG, "stopGetAddrInfo: mMDnsManager is null");
2398 return false;
2399 }
Paul Hud44e1b72023-06-16 02:07:42 +00002400 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002401 }
2402
2403 @Override
Paul Hub2e67d32023-04-18 05:50:14 +00002404 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2405 if (!PermissionUtils.checkDumpPermission(mContext, TAG, writer)) return;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002406
Paul Hub2e67d32023-04-18 05:50:14 +00002407 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
2408 // Dump state machine logs
Irfan Sheriff75006652012-04-17 23:15:29 -07002409 mNsdStateMachine.dump(fd, pw, args);
Paul Hub2e67d32023-04-18 05:50:14 +00002410
2411 // Dump service and clients logs
2412 pw.println();
Paul Hu14667de2023-04-17 22:42:47 +08002413 pw.println("Logs:");
Paul Hub2e67d32023-04-18 05:50:14 +00002414 pw.increaseIndent();
2415 mServiceLogs.reverseDump(pw);
2416 pw.decreaseIndent();
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002417 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002418
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002419 private abstract static class ClientRequest {
Paul Hud44e1b72023-06-16 02:07:42 +00002420 private final int mTransactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002421 private final long mStartTimeMs;
Paul Hu812e9212023-06-20 06:24:53 +00002422 private int mFoundServiceCount = 0;
2423 private int mLostServiceCount = 0;
2424 private final Set<String> mServices = new ArraySet<>();
Paul Hua6bc4632023-06-26 01:18:29 +00002425 private boolean mIsServiceFromCache = false;
Paul Hubad6fe92023-07-24 21:25:22 +08002426 private int mSentQueryCount = NO_SENT_QUERY_COUNT;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002427
Paul Hu812e9212023-06-20 06:24:53 +00002428 private ClientRequest(int transactionId, long startTimeMs) {
Paul Hud44e1b72023-06-16 02:07:42 +00002429 mTransactionId = transactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002430 mStartTimeMs = startTimeMs;
2431 }
2432
Paul Hu812e9212023-06-20 06:24:53 +00002433 public long calculateRequestDurationMs(long stopTimeMs) {
Paul Hu777ed052023-06-19 13:35:15 +00002434 return stopTimeMs - mStartTimeMs;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002435 }
Paul Hu812e9212023-06-20 06:24:53 +00002436
2437 public void onServiceFound(String serviceName) {
2438 mFoundServiceCount++;
2439 if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) {
2440 mServices.add(serviceName);
2441 }
2442 }
2443
2444 public void onServiceLost() {
2445 mLostServiceCount++;
2446 }
2447
2448 public int getFoundServiceCount() {
2449 return mFoundServiceCount;
2450 }
2451
2452 public int getLostServiceCount() {
2453 return mLostServiceCount;
2454 }
2455
2456 public int getServicesCount() {
2457 return mServices.size();
2458 }
Paul Hua6bc4632023-06-26 01:18:29 +00002459
2460 public void setServiceFromCache(boolean isServiceFromCache) {
2461 mIsServiceFromCache = isServiceFromCache;
2462 }
2463
2464 public boolean isServiceFromCache() {
2465 return mIsServiceFromCache;
2466 }
Paul Hubad6fe92023-07-24 21:25:22 +08002467
2468 public void onQuerySent() {
2469 mSentQueryCount++;
2470 }
2471
2472 public int getSentQueryCount() {
2473 return mSentQueryCount;
2474 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002475 }
2476
2477 private static class LegacyClientRequest extends ClientRequest {
2478 private final int mRequestCode;
2479
Paul Hu812e9212023-06-20 06:24:53 +00002480 private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) {
2481 super(transactionId, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002482 mRequestCode = requestCode;
2483 }
2484 }
2485
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002486 private abstract static class JavaBackendClientRequest extends ClientRequest {
2487 @Nullable
2488 private final Network mRequestedNetwork;
2489
Paul Hu777ed052023-06-19 13:35:15 +00002490 private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002491 long startTimeMs) {
2492 super(transactionId, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002493 mRequestedNetwork = requestedNetwork;
2494 }
2495
2496 @Nullable
2497 public Network getRequestedNetwork() {
2498 return mRequestedNetwork;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002499 }
2500 }
2501
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002502 private static class AdvertiserClientRequest extends JavaBackendClientRequest {
Paul Hu777ed052023-06-19 13:35:15 +00002503 private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002504 long startTimeMs) {
2505 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002506 }
2507 }
2508
2509 private static class DiscoveryManagerRequest extends JavaBackendClientRequest {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002510 @NonNull
2511 private final MdnsListener mListener;
2512
Paul Hud44e1b72023-06-16 02:07:42 +00002513 private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
Paul Hu812e9212023-06-20 06:24:53 +00002514 @Nullable Network requestedNetwork, long startTimeMs) {
2515 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002516 mListener = listener;
2517 }
2518 }
2519
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002520 /* Information tracked per client */
2521 private class ClientInfo {
2522
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002523 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002524 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002525 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002526 private NsdServiceInfo mResolvedService;
2527
Paul Hud44e1b72023-06-16 02:07:42 +00002528 /* A map from client request ID (listenerKey) to the request */
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002529 private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>();
Paul Hu23fa2022023-01-13 22:57:24 +08002530
Luke Huangf7277ed2021-07-12 21:15:10 +08002531 // The target SDK of this client < Build.VERSION_CODES.S
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002532 private boolean mIsPreSClient = false;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002533 private final int mUid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002534 // The flag of using java backend if the client's target SDK >= U
2535 private final boolean mUseJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002536 // Store client logs
2537 private final SharedLog mClientLogs;
Paul Hucdef3532023-06-18 14:47:35 +00002538 // Report the nsd metrics data
2539 private final NetworkNsdReportedMetrics mMetrics;
Luke Huangf7277ed2021-07-12 21:15:10 +08002540
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002541 private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
Paul Hucdef3532023-06-18 14:47:35 +00002542 SharedLog sharedLog, NetworkNsdReportedMetrics metrics) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002543 mCb = cb;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002544 mUid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002545 mUseJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002546 mClientLogs = sharedLog;
2547 mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
Paul Hucdef3532023-06-18 14:47:35 +00002548 mMetrics = metrics;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002549 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002550
2551 @Override
2552 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -06002553 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -07002554 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002555 sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002556 sb.append("mUseJavaBackend ").append(mUseJavaBackend).append("\n");
2557 sb.append("mUid ").append(mUid).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002558 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002559 int clientRequestId = mClientRequests.keyAt(i);
2560 sb.append("clientRequestId ")
2561 .append(clientRequestId)
2562 .append(" transactionId ").append(mClientRequests.valueAt(i).mTransactionId)
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002563 .append(" type ").append(
2564 mClientRequests.valueAt(i).getClass().getSimpleName())
2565 .append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -07002566 }
2567 return sb.toString();
2568 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002569
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002570 private boolean isPreSClient() {
2571 return mIsPreSClient;
Luke Huangf7277ed2021-07-12 21:15:10 +08002572 }
2573
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002574 private void setPreSClient() {
2575 mIsPreSClient = true;
Luke Huangf7277ed2021-07-12 21:15:10 +08002576 }
2577
Paul Hu812e9212023-06-20 06:24:53 +00002578 private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) {
Paul Hue4f5f252023-02-16 21:13:47 +08002579 final MdnsListener listener =
2580 ((DiscoveryManagerRequest) request).mListener;
2581 mMdnsDiscoveryManager.unregisterListener(
2582 listener.getListenedServiceType(), listener);
Paul Hu812e9212023-06-20 06:24:53 +00002583 return listener;
Paul Hue4f5f252023-02-16 21:13:47 +08002584 }
2585
Dave Plattfeff2af2014-03-07 14:48:22 -08002586 // Remove any pending requests from the global map when we get rid of a client,
2587 // and send cancellations to the daemon.
2588 private void expungeAllRequests() {
Paul Hub2e67d32023-04-18 05:50:14 +00002589 mClientLogs.log("Client unregistered. expungeAllRequests!");
Hugo Benichid2552ae2017-04-11 14:42:47 +09002590 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002591 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002592 final int clientRequestId = mClientRequests.keyAt(i);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002593 final ClientRequest request = mClientRequests.valueAt(i);
Paul Hud44e1b72023-06-16 02:07:42 +00002594 final int transactionId = request.mTransactionId;
2595 mTransactionIdToClientInfoMap.remove(transactionId);
paulhub2225702021-11-17 09:35:33 +08002596 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002597 Log.d(TAG, "Terminating clientRequestId " + clientRequestId
2598 + " transactionId " + transactionId
2599 + " type " + mClientRequests.get(clientRequestId));
paulhub2225702021-11-17 09:35:33 +08002600 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002601
2602 if (request instanceof DiscoveryManagerRequest) {
Paul Hu812e9212023-06-20 06:24:53 +00002603 final MdnsListener listener = unregisterMdnsListenerFromRequest(request);
2604 if (listener instanceof DiscoveryListener) {
Paul Hu508a0122023-09-11 15:31:33 +08002605 mMetrics.reportServiceDiscoveryStop(false /* isLegacy */, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00002606 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2607 request.getFoundServiceCount(),
2608 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002609 request.getServicesCount(),
2610 request.getSentQueryCount());
Paul Hu60149052023-07-31 14:26:08 +08002611 } else if (listener instanceof ResolutionListener) {
Paul Hu508a0122023-09-11 15:31:33 +08002612 mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
Paul Hu60149052023-07-31 14:26:08 +08002613 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Huddce5912023-08-01 10:26:49 +08002614 } else if (listener instanceof ServiceInfoListener) {
2615 mMetrics.reportServiceInfoCallbackUnregistered(transactionId,
2616 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2617 request.getFoundServiceCount(),
2618 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002619 request.isServiceFromCache(),
2620 request.getSentQueryCount());
Paul Hu812e9212023-06-20 06:24:53 +00002621 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002622 continue;
2623 }
2624
2625 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +08002626 final AdvertiserMetrics metrics =
2627 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00002628 mAdvertiser.removeService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002629 mMetrics.reportServiceUnregistration(false /* isLegacy */, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002630 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2631 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2632 metrics.mConflictDuringProbingCount,
2633 metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002634 continue;
2635 }
2636
2637 if (!(request instanceof LegacyClientRequest)) {
2638 throw new IllegalStateException("Unknown request type: " + request.getClass());
2639 }
2640
2641 switch (((LegacyClientRequest) request).mRequestCode) {
Dave Plattfeff2af2014-03-07 14:48:22 -08002642 case NsdManager.DISCOVER_SERVICES:
Paul Hud44e1b72023-06-16 02:07:42 +00002643 stopServiceDiscovery(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002644 mMetrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00002645 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2646 request.getFoundServiceCount(),
2647 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002648 request.getServicesCount(),
2649 NO_SENT_QUERY_COUNT);
Dave Plattfeff2af2014-03-07 14:48:22 -08002650 break;
2651 case NsdManager.RESOLVE_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002652 stopResolveService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002653 mMetrics.reportServiceResolutionStop(true /* isLegacy */, transactionId,
Paul Hu60149052023-07-31 14:26:08 +08002654 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Dave Plattfeff2af2014-03-07 14:48:22 -08002655 break;
2656 case NsdManager.REGISTER_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002657 unregisterService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002658 mMetrics.reportServiceUnregistration(true /* isLegacy */, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002659 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2660 NO_PACKET /* repliedRequestsCount */,
2661 NO_PACKET /* sentPacketCount */,
2662 0 /* conflictDuringProbingCount */,
2663 0 /* conflictAfterProbingCount */);
Dave Plattfeff2af2014-03-07 14:48:22 -08002664 break;
2665 default:
2666 break;
2667 }
2668 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002669 mClientRequests.clear();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002670 updateMulticastLock();
2671 }
2672
2673 /**
2674 * Returns true if this client has any Java backend request that requests one of the given
2675 * networks.
2676 */
2677 boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) {
2678 for (int i = 0; i < mClientRequests.size(); i++) {
2679 final ClientRequest req = mClientRequests.valueAt(i);
2680 if (!(req instanceof JavaBackendClientRequest)) {
2681 continue;
2682 }
2683 final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i))
2684 .getRequestedNetwork();
2685 if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) {
2686 return true;
2687 }
2688 }
2689 return false;
Dave Plattfeff2af2014-03-07 14:48:22 -08002690 }
2691
Paul Hud44e1b72023-06-16 02:07:42 +00002692 // mClientRequests is a sparse array of client request id -> ClientRequest. For a given
2693 // transaction id, return the corresponding client request id.
2694 private int getClientRequestId(final int transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002695 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002696 if (mClientRequests.valueAt(i).mTransactionId == transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002697 return mClientRequests.keyAt(i);
2698 }
Christopher Lane74411222014-04-25 18:39:07 -07002699 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002700 return -1;
Christopher Lane74411222014-04-25 18:39:07 -07002701 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002702
Paul Hub2e67d32023-04-18 05:50:14 +00002703 private void log(String message) {
2704 mClientLogs.log(message);
2705 }
2706
Paul Hu508a0122023-09-11 15:31:33 +08002707 private static boolean isLegacyClientRequest(@NonNull ClientRequest request) {
2708 return !(request instanceof DiscoveryManagerRequest)
2709 && !(request instanceof AdvertiserClientRequest);
2710 }
2711
2712 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info,
2713 ClientRequest request) {
2714 mMetrics.reportServiceDiscoveryStarted(
2715 isLegacyClientRequest(request), request.mTransactionId);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002716 try {
2717 mCb.onDiscoverServicesStarted(listenerKey, info);
2718 } catch (RemoteException e) {
2719 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
2720 }
2721 }
Paul Hu508a0122023-09-11 15:31:33 +08002722 void onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2723 onDiscoverServicesFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2724 0L /* durationMs */);
Paul Hu812e9212023-06-20 06:24:53 +00002725 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002726
Paul Hu508a0122023-09-11 15:31:33 +08002727 void onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy,
2728 int transactionId, long durationMs) {
2729 mMetrics.reportServiceDiscoveryFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002730 try {
2731 mCb.onDiscoverServicesFailed(listenerKey, error);
2732 } catch (RemoteException e) {
2733 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
2734 }
2735 }
2736
Paul Hu812e9212023-06-20 06:24:53 +00002737 void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2738 request.onServiceFound(info.getServiceName());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002739 try {
2740 mCb.onServiceFound(listenerKey, info);
2741 } catch (RemoteException e) {
2742 Log.e(TAG, "Error calling onServiceFound(", e);
2743 }
2744 }
2745
Paul Hu812e9212023-06-20 06:24:53 +00002746 void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2747 request.onServiceLost();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002748 try {
2749 mCb.onServiceLost(listenerKey, info);
2750 } catch (RemoteException e) {
2751 Log.e(TAG, "Error calling onServiceLost(", e);
2752 }
2753 }
2754
2755 void onStopDiscoveryFailed(int listenerKey, int error) {
2756 try {
2757 mCb.onStopDiscoveryFailed(listenerKey, error);
2758 } catch (RemoteException e) {
2759 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
2760 }
2761 }
2762
Paul Hu812e9212023-06-20 06:24:53 +00002763 void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) {
2764 mMetrics.reportServiceDiscoveryStop(
Paul Hu508a0122023-09-11 15:31:33 +08002765 isLegacyClientRequest(request),
Paul Hu812e9212023-06-20 06:24:53 +00002766 request.mTransactionId,
2767 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2768 request.getFoundServiceCount(),
2769 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002770 request.getServicesCount(),
2771 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002772 try {
2773 mCb.onStopDiscoverySucceeded(listenerKey);
2774 } catch (RemoteException e) {
2775 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
2776 }
2777 }
2778
Paul Hu508a0122023-09-11 15:31:33 +08002779 void onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2780 onRegisterServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2781 0L /* durationMs */);
Paul Hu777ed052023-06-19 13:35:15 +00002782 }
2783
Paul Hu508a0122023-09-11 15:31:33 +08002784 void onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy,
2785 int transactionId, long durationMs) {
2786 mMetrics.reportServiceRegistrationFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002787 try {
2788 mCb.onRegisterServiceFailed(listenerKey, error);
2789 } catch (RemoteException e) {
2790 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
2791 }
2792 }
2793
Paul Hu508a0122023-09-11 15:31:33 +08002794 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info,
2795 ClientRequest request) {
2796 mMetrics.reportServiceRegistrationSucceeded(isLegacyClientRequest(request),
2797 request.mTransactionId,
2798 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002799 try {
2800 mCb.onRegisterServiceSucceeded(listenerKey, info);
2801 } catch (RemoteException e) {
2802 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
2803 }
2804 }
2805
2806 void onUnregisterServiceFailed(int listenerKey, int error) {
2807 try {
2808 mCb.onUnregisterServiceFailed(listenerKey, error);
2809 } catch (RemoteException e) {
2810 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
2811 }
2812 }
2813
Paul Hu508a0122023-09-11 15:31:33 +08002814 void onUnregisterServiceSucceeded(int listenerKey, ClientRequest request,
Paul Hu043bcd42023-07-14 16:38:25 +08002815 AdvertiserMetrics metrics) {
Paul Hu508a0122023-09-11 15:31:33 +08002816 mMetrics.reportServiceUnregistration(isLegacyClientRequest(request),
2817 request.mTransactionId,
2818 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hu043bcd42023-07-14 16:38:25 +08002819 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2820 metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002821 try {
2822 mCb.onUnregisterServiceSucceeded(listenerKey);
2823 } catch (RemoteException e) {
2824 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
2825 }
2826 }
2827
Paul Hu508a0122023-09-11 15:31:33 +08002828 void onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2829 onResolveServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2830 0L /* durationMs */);
Paul Hua6bc4632023-06-26 01:18:29 +00002831 }
2832
Paul Hu508a0122023-09-11 15:31:33 +08002833 void onResolveServiceFailed(int listenerKey, int error, boolean isLegacy,
2834 int transactionId, long durationMs) {
2835 mMetrics.reportServiceResolutionFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002836 try {
2837 mCb.onResolveServiceFailed(listenerKey, error);
2838 } catch (RemoteException e) {
2839 Log.e(TAG, "Error calling onResolveServiceFailed", e);
2840 }
2841 }
2842
Paul Hua6bc4632023-06-26 01:18:29 +00002843 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
2844 ClientRequest request) {
2845 mMetrics.reportServiceResolved(
Paul Hu508a0122023-09-11 15:31:33 +08002846 isLegacyClientRequest(request),
Paul Hua6bc4632023-06-26 01:18:29 +00002847 request.mTransactionId,
2848 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hubad6fe92023-07-24 21:25:22 +08002849 request.isServiceFromCache(),
2850 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002851 try {
2852 mCb.onResolveServiceSucceeded(listenerKey, info);
2853 } catch (RemoteException e) {
2854 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
2855 }
2856 }
Paul Hub58deb72022-12-26 09:24:42 +00002857
2858 void onStopResolutionFailed(int listenerKey, int error) {
2859 try {
2860 mCb.onStopResolutionFailed(listenerKey, error);
2861 } catch (RemoteException e) {
2862 Log.e(TAG, "Error calling onStopResolutionFailed", e);
2863 }
2864 }
2865
Paul Hu60149052023-07-31 14:26:08 +08002866 void onStopResolutionSucceeded(int listenerKey, ClientRequest request) {
2867 mMetrics.reportServiceResolutionStop(
Paul Hu508a0122023-09-11 15:31:33 +08002868 isLegacyClientRequest(request),
Paul Hu60149052023-07-31 14:26:08 +08002869 request.mTransactionId,
2870 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hub58deb72022-12-26 09:24:42 +00002871 try {
2872 mCb.onStopResolutionSucceeded(listenerKey);
2873 } catch (RemoteException e) {
2874 Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
2875 }
2876 }
Paul Hu18aeccc2022-12-27 08:48:48 +00002877
2878 void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
Paul Huddce5912023-08-01 10:26:49 +08002879 mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
Paul Hu18aeccc2022-12-27 08:48:48 +00002880 try {
2881 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
2882 } catch (RemoteException e) {
2883 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
2884 }
2885 }
2886
Paul Huddce5912023-08-01 10:26:49 +08002887 void onServiceInfoCallbackRegistered(int transactionId) {
2888 mMetrics.reportServiceInfoCallbackRegistered(transactionId);
2889 }
2890
2891 void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2892 request.onServiceFound(info.getServiceName());
Paul Hu18aeccc2022-12-27 08:48:48 +00002893 try {
2894 mCb.onServiceUpdated(listenerKey, info);
2895 } catch (RemoteException e) {
2896 Log.e(TAG, "Error calling onServiceUpdated", e);
2897 }
2898 }
2899
Paul Huddce5912023-08-01 10:26:49 +08002900 void onServiceUpdatedLost(int listenerKey, ClientRequest request) {
2901 request.onServiceLost();
Paul Hu18aeccc2022-12-27 08:48:48 +00002902 try {
2903 mCb.onServiceUpdatedLost(listenerKey);
2904 } catch (RemoteException e) {
2905 Log.e(TAG, "Error calling onServiceUpdatedLost", e);
2906 }
2907 }
2908
Paul Huddce5912023-08-01 10:26:49 +08002909 void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) {
2910 mMetrics.reportServiceInfoCallbackUnregistered(
2911 request.mTransactionId,
2912 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2913 request.getFoundServiceCount(),
2914 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002915 request.isServiceFromCache(),
2916 request.getSentQueryCount());
Paul Hu18aeccc2022-12-27 08:48:48 +00002917 try {
2918 mCb.onServiceInfoCallbackUnregistered(listenerKey);
2919 } catch (RemoteException e) {
2920 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
2921 }
2922 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002923 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002924}