blob: c045eafe58bca7040d0e10294beea1c352450f49 [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;
Remi NGUYEN VAN629234c2024-02-08 18:27:45 +090029import static android.net.nsd.NsdManager.SUBTYPE_LABEL_REGEX;
Yuyang Huang86d083f2023-12-12 19:56:41 +090030import static android.net.nsd.NsdManager.TYPE_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 Hu11a883d2023-12-14 07:36:44 +000038import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE;
Paul Hu05086bd2024-01-26 03:48:51 +000039import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE;
Paul Hucdef3532023-06-18 14:47:35 +000040import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090041
Paul Hu23fa2022023-01-13 22:57:24 +080042import android.annotation.NonNull;
Paul Hu4bd98ef2023-01-12 13:42:07 +080043import android.annotation.Nullable;
Yuyang Huangfc831702023-08-21 17:48:48 +090044import android.annotation.RequiresApi;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090045import android.app.ActivityManager;
paulhua262cc12019-08-12 16:25:11 +080046import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070047import android.content.Intent;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090048import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090049import android.net.INetd;
Paul Hu75069ed2023-01-14 00:31:09 +080050import android.net.InetAddresses;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090051import android.net.LinkProperties;
52import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080053import android.net.mdns.aidl.DiscoveryInfo;
54import android.net.mdns.aidl.GetAddressInfo;
55import android.net.mdns.aidl.IMDnsEventListener;
56import android.net.mdns.aidl.RegistrationInfo;
57import android.net.mdns.aidl.ResolutionInfo;
Yuyang Huang86d083f2023-12-12 19:56:41 +090058import android.net.nsd.AdvertisingRequest;
Kangping Dong1f1a3792023-12-10 22:05:04 +080059import android.net.nsd.DiscoveryRequest;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070060import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090061import android.net.nsd.INsdManagerCallback;
62import android.net.nsd.INsdServiceConnector;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090063import android.net.nsd.IOffloadEngine;
paulhu2b9ed952022-02-10 21:58:32 +080064import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070065import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080066import android.net.nsd.NsdServiceInfo;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090067import android.net.nsd.OffloadEngine;
68import android.net.nsd.OffloadServiceInfo;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090069import android.net.wifi.WifiManager;
Paul Hub2e67d32023-04-18 05:50:14 +000070import android.os.Binder;
Yuyang Huangfc831702023-08-21 17:48:48 +090071import android.os.Build;
Hugo Benichi803a2f02017-04-24 11:35:06 +090072import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080073import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090074import android.os.IBinder;
Paul Hu4bd98ef2023-01-12 13:42:07 +080075import android.os.Looper;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070076import android.os.Message;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090077import android.os.RemoteCallbackList;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090078import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070079import android.os.UserHandle;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090080import android.provider.DeviceConfig;
Paul Hu23fa2022023-01-13 22:57:24 +080081import android.text.TextUtils;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090082import android.util.ArraySet;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090083import android.util.Log;
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +090084import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070085import android.util.SparseArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070086
paulhua262cc12019-08-12 16:25:11 +080087import com.android.internal.annotations.VisibleForTesting;
Paul Hub2e67d32023-04-18 05:50:14 +000088import com.android.internal.util.IndentingPrintWriter;
paulhua262cc12019-08-12 16:25:11 +080089import com.android.internal.util.State;
90import com.android.internal.util.StateMachine;
Paul Hucdef3532023-06-18 14:47:35 +000091import com.android.metrics.NetworkNsdReportedMetrics;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090092import com.android.modules.utils.build.SdkLevel;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090093import com.android.net.module.util.CollectionUtils;
Paul Hu4bd98ef2023-01-12 13:42:07 +080094import com.android.net.module.util.DeviceConfigUtils;
Paul Hu55f943e2024-02-20 03:04:17 +000095import com.android.net.module.util.HandlerUtils;
Yuyang Huanga6a6ff92023-04-24 13:33:34 +090096import com.android.net.module.util.InetAddressUtils;
paulhu3ffffe72021-09-16 10:15:22 +080097import com.android.net.module.util.PermissionUtils;
Paul Hub2e67d32023-04-18 05:50:14 +000098import com.android.net.module.util.SharedLog;
Paul Hu4bd98ef2023-01-12 13:42:07 +080099import com.android.server.connectivity.mdns.ExecutorProvider;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900100import com.android.server.connectivity.mdns.MdnsAdvertiser;
Yuyang Huange5cba9c2023-11-02 18:05:47 +0900101import com.android.server.connectivity.mdns.MdnsAdvertisingOptions;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800102import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
Yuyang Huangb96a0712023-09-07 15:13:15 +0900103import com.android.server.connectivity.mdns.MdnsFeatureFlags;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900104import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800105import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
Paul Hu23fa2022023-01-13 22:57:24 +0800106import com.android.server.connectivity.mdns.MdnsSearchOptions;
107import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
108import com.android.server.connectivity.mdns.MdnsServiceInfo;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800109import com.android.server.connectivity.mdns.MdnsSocketProvider;
Yuyang Huangde802c82023-05-02 17:14:22 +0900110import com.android.server.connectivity.mdns.util.MdnsUtils;
paulhua262cc12019-08-12 16:25:11 +0800111
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700112import java.io.FileDescriptor;
113import java.io.PrintWriter;
Yuyang Huangaa0e9602023-03-17 12:43:09 +0900114import java.net.Inet6Address;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700115import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900116import java.net.NetworkInterface;
117import java.net.SocketException;
118import java.net.UnknownHostException;
Paul Hu2b865912023-03-06 14:27:53 +0800119import java.util.ArrayList;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +0900120import java.util.Arrays;
Kangping Dong5af24b62023-12-10 21:41:16 +0800121import java.util.Collection;
Yuyang Huang170d42f2023-12-09 15:26:16 +0900122import java.util.Collections;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700123import java.util.HashMap;
Kangping Dong5af24b62023-12-10 21:41:16 +0800124import java.util.LinkedHashMap;
Paul Hu23fa2022023-01-13 22:57:24 +0800125import java.util.List;
Paul Hu75069ed2023-01-14 00:31:09 +0800126import java.util.Map;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900127import java.util.Objects;
Paul Hu812e9212023-06-20 06:24:53 +0000128import java.util.Set;
Paul Hu23fa2022023-01-13 22:57:24 +0800129import java.util.regex.Matcher;
130import java.util.regex.Pattern;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700131
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700132/**
133 * Network Service Discovery Service handles remote service discovery operation requests by
134 * implementing the INsdManager interface.
135 *
136 * @hide
137 */
Yuyang Huangfc831702023-08-21 17:48:48 +0900138@RequiresApi(Build.VERSION_CODES.TIRAMISU)
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700139public class NsdService extends INsdManager.Stub {
140 private static final String TAG = "NsdService";
141 private static final String MDNS_TAG = "mDnsConnector";
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900142 /**
143 * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
144 * implementation.
145 */
Paul Hu4bd98ef2023-01-12 13:42:07 +0800146 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
Paul Hu23fa2022023-01-13 22:57:24 +0800147 private static final String LOCAL_DOMAIN_NAME = "local";
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700148
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900149 /**
150 * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
151 * implementation.
152 */
153 private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
154
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900155 /**
156 * Comma-separated list of type:flag mappings indicating the flags to use to allowlist
157 * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
158 *
159 * For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
160 * _mytype._tcp:mytype,_othertype._tcp.local:othertype
161 *
162 * In which case the flags:
163 * "mdns_discovery_manager_allowlist_mytype_version",
164 * "mdns_advertiser_allowlist_mytype_version",
165 * "mdns_discovery_manager_allowlist_othertype_version",
166 * "mdns_advertiser_allowlist_othertype_version"
167 * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
168 * be read with
Motomu Utsumi624aeb42023-08-15 15:52:27 +0900169 * {@link DeviceConfigUtils#isTetheringFeatureEnabled}
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900170 *
171 * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
172 * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
173 * @see #MDNS_ALLOWLIST_FLAG_SUFFIX
174 */
175 private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
176
177 private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
178 "mdns_discovery_manager_allowlist_";
179 private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
180 "mdns_advertiser_allowlist_";
181 private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
182
Remi NGUYEN VAN0ca094b2023-09-13 16:27:12 +0900183 private static final String FORCE_ENABLE_FLAG_FOR_TEST_PREFIX = "test_";
Kangping Dong5af24b62023-12-10 21:41:16 +0800184
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900185 @VisibleForTesting
186 static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
187 "mdns_config_running_app_active_importance_cutoff";
188 @VisibleForTesting
189 static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
190 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
191 private final int mRunningAppActiveImportanceCutoff;
192
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900193 public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Luke Huang92860f92021-06-23 06:29:30 +0000194 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900195 private static final int IFACE_IDX_ANY = 0;
Paul Hu812e9212023-06-20 06:24:53 +0000196 private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
197 @VisibleForTesting
198 static final int NO_TRANSACTION = -1;
Paul Hubad6fe92023-07-24 21:25:22 +0800199 private static final int NO_SENT_QUERY_COUNT = 0;
200 private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
Kangping Dong5af24b62023-12-10 21:41:16 +0800201 private static final int MAX_SUBTYPE_COUNT = 100;
Paul Hu14667de2023-04-17 22:42:47 +0800202 private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700203
Hugo Benichi32be63d2017-04-05 14:06:11 +0900204 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +0900205 private final NsdStateMachine mNsdStateMachine;
Ken Chen80c9f6f2023-11-15 18:24:54 +0800206 // It can be null on V+ device since mdns native service provided by netd is removed.
207 private final @Nullable MDnsManager mMDnsManager;
paulhu2b9ed952022-02-10 21:58:32 +0800208 private final MDnsEventCallback mMDnsEventCallback;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900209 @NonNull
210 private final Dependencies mDeps;
211 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800212 private final MdnsMultinetworkSocketClient mMdnsSocketClient;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900213 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800214 private final MdnsDiscoveryManager mMdnsDiscoveryManager;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900215 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800216 private final MdnsSocketProvider mMdnsSocketProvider;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900217 @NonNull
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900218 private final MdnsAdvertiser mAdvertiser;
Paul Hu777ed052023-06-19 13:35:15 +0000219 @NonNull
220 private final Clock mClock;
Paul Hu14667de2023-04-17 22:42:47 +0800221 private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
Paul Hu23fa2022023-01-13 22:57:24 +0800222 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
paulhu2b9ed952022-02-10 21:58:32 +0800223 // state machine thread. If change this outside state machine, it will need to introduce
224 // synchronization.
225 private boolean mIsDaemonStarted = false;
Paul Hu23fa2022023-01-13 22:57:24 +0800226 private boolean mIsMonitoringSocketsStarted = false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700227
228 /**
229 * Clients receiving asynchronous messages
230 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900231 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700232
Paul Hud44e1b72023-06-16 02:07:42 +0000233 /* A map from transaction(unique) id to client info */
234 private final SparseArray<ClientInfo> mTransactionIdToClientInfoMap = new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700235
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900236 // Note this is not final to avoid depending on the Wi-Fi service starting before NsdService
237 @Nullable
238 private WifiManager.MulticastLock mHeldMulticastLock;
239 // Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network
240 // (non-null), value is the requested Network (nullable)
241 @NonNull
242 private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>();
243 @NonNull
244 private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>();
245
Luke Huang05298582021-06-13 16:52:05 +0000246 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700247
Hugo Benichi32be63d2017-04-05 14:06:11 +0900248 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700249 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +0800250 // The count of the connected legacy clients.
251 private int mLegacyClientCount = 0;
Paul Hub2e67d32023-04-18 05:50:14 +0000252 // The number of client that ever connected.
253 private int mClientNumberId = 1;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700254
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900255 private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
256 new RemoteCallbackList<>();
Paul Hu11a883d2023-12-14 07:36:44 +0000257 @NonNull
258 private final MdnsFeatureFlags mMdnsFeatureFlags;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900259
260 private static class OffloadEngineInfo {
261 @NonNull final String mInterfaceName;
262 final long mOffloadCapabilities;
263 final long mOffloadType;
264 @NonNull final IOffloadEngine mOffloadEngine;
265
266 OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
267 @NonNull String interfaceName, long capabilities, long offloadType) {
268 this.mOffloadEngine = offloadEngine;
269 this.mInterfaceName = interfaceName;
270 this.mOffloadCapabilities = capabilities;
271 this.mOffloadType = offloadType;
272 }
273 }
274
Paul Hu812e9212023-06-20 06:24:53 +0000275 @VisibleForTesting
276 static class MdnsListener implements MdnsServiceBrowserListener {
Paul Hud44e1b72023-06-16 02:07:42 +0000277 protected final int mClientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800278 protected final int mTransactionId;
279 @NonNull
Paul Hu23fa2022023-01-13 22:57:24 +0800280 protected final String mListenedServiceType;
281
Kangping Dong97b2adc2024-01-11 16:00:37 +0800282 MdnsListener(int clientRequestId, int transactionId, @NonNull String listenedServiceType) {
Paul Hud44e1b72023-06-16 02:07:42 +0000283 mClientRequestId = clientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800284 mTransactionId = transactionId;
Paul Hu23fa2022023-01-13 22:57:24 +0800285 mListenedServiceType = listenedServiceType;
286 }
287
288 @NonNull
289 public String getListenedServiceType() {
290 return mListenedServiceType;
291 }
292
293 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000294 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
295 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800296
297 @Override
298 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
299
300 @Override
301 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
302
303 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000304 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
305 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800306
307 @Override
308 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
309
310 @Override
311 public void onSearchStoppedWithError(int error) { }
312
313 @Override
314 public void onSearchFailedToStart() { }
315
316 @Override
Paul Hubad6fe92023-07-24 21:25:22 +0800317 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
318 int sentQueryTransactionId) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800319
320 @Override
321 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
322 }
323
324 private class DiscoveryListener extends MdnsListener {
325
Paul Hud44e1b72023-06-16 02:07:42 +0000326 DiscoveryListener(int clientRequestId, int transactionId,
Kangping Dong97b2adc2024-01-11 16:00:37 +0800327 @NonNull String listenServiceType) {
328 super(clientRequestId, transactionId, listenServiceType);
Paul Hu23fa2022023-01-13 22:57:24 +0800329 }
330
331 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000332 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
333 boolean isServiceFromCache) {
Paul Hu019621e2023-01-13 23:26:49 +0800334 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
335 NsdManager.SERVICE_FOUND,
Paul Hua6bc4632023-06-26 01:18:29 +0000336 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu23fa2022023-01-13 22:57:24 +0800337 }
338
339 @Override
340 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
Paul Hu319751a2023-01-13 23:56:34 +0800341 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
342 NsdManager.SERVICE_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000343 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu23fa2022023-01-13 22:57:24 +0800344 }
Paul Hubad6fe92023-07-24 21:25:22 +0800345
346 @Override
347 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
348 int sentQueryTransactionId) {
349 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
350 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
351 }
Paul Hu23fa2022023-01-13 22:57:24 +0800352 }
353
Paul Hu75069ed2023-01-14 00:31:09 +0800354 private class ResolutionListener extends MdnsListener {
355
Paul Hud44e1b72023-06-16 02:07:42 +0000356 ResolutionListener(int clientRequestId, int transactionId,
Kangping Dong97b2adc2024-01-11 16:00:37 +0800357 @NonNull String listenServiceType) {
358 super(clientRequestId, transactionId, listenServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +0800359 }
360
361 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000362 public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
Paul Hu75069ed2023-01-14 00:31:09 +0800363 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
364 NsdManager.RESOLVE_SERVICE_SUCCEEDED,
Paul Hua6bc4632023-06-26 01:18:29 +0000365 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu75069ed2023-01-14 00:31:09 +0800366 }
Paul Hubad6fe92023-07-24 21:25:22 +0800367
368 @Override
369 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
370 int sentQueryTransactionId) {
371 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
372 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
373 }
Paul Hu75069ed2023-01-14 00:31:09 +0800374 }
375
Paul Hu30bd70d2023-02-07 13:20:56 +0000376 private class ServiceInfoListener extends MdnsListener {
377
Paul Hud44e1b72023-06-16 02:07:42 +0000378 ServiceInfoListener(int clientRequestId, int transactionId,
Kangping Dong97b2adc2024-01-11 16:00:37 +0800379 @NonNull String listenServiceType) {
380 super(clientRequestId, transactionId, listenServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +0000381 }
382
383 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000384 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
385 boolean isServiceFromCache) {
Paul Hu30bd70d2023-02-07 13:20:56 +0000386 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
387 NsdManager.SERVICE_UPDATED,
Paul Hua6bc4632023-06-26 01:18:29 +0000388 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu30bd70d2023-02-07 13:20:56 +0000389 }
390
391 @Override
392 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
393 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
394 NsdManager.SERVICE_UPDATED,
Paul Hud44e1b72023-06-16 02:07:42 +0000395 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000396 }
397
398 @Override
399 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
400 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
401 NsdManager.SERVICE_UPDATED_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000402 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000403 }
Paul Hubad6fe92023-07-24 21:25:22 +0800404
405 @Override
406 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
407 int sentQueryTransactionId) {
408 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
409 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
410 }
Paul Hu30bd70d2023-02-07 13:20:56 +0000411 }
412
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900413 private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
414 @Override
415 public void onSocketRequestFulfilled(@Nullable Network socketNetwork,
416 @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {
417 // The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF
418 // filtering on such interfaces, so taking the multicast lock is not necessary to
419 // disable APF filtering of multicast.
420 if (socketNetwork == null
421 || !CollectionUtils.contains(transports, TRANSPORT_WIFI)
422 || CollectionUtils.contains(transports, TRANSPORT_VPN)) {
423 return;
424 }
425
426 if (mWifiLockRequiredNetworks.add(socketNetwork)) {
427 updateMulticastLock();
428 }
429 }
430
431 @Override
432 public void onSocketDestroyed(@Nullable Network socketNetwork,
433 @NonNull MdnsInterfaceSocket socket) {
434 if (mWifiLockRequiredNetworks.remove(socketNetwork)) {
435 updateMulticastLock();
436 }
437 }
438 }
439
440 private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
441 private final Handler mHandler;
442
443 private UidImportanceListener(Handler handler) {
444 mHandler = handler;
445 }
446
447 @Override
448 public void onUidImportance(int uid, int importance) {
449 mHandler.post(() -> handleUidImportanceChanged(uid, importance));
450 }
451 }
452
453 private void handleUidImportanceChanged(int uid, int importance) {
454 // Lower importance values are more "important"
455 final boolean modified = importance <= mRunningAppActiveImportanceCutoff
456 ? mRunningAppActiveUids.add(uid)
457 : mRunningAppActiveUids.remove(uid);
458 if (modified) {
459 updateMulticastLock();
460 }
461 }
462
463 /**
464 * Take or release the lock based on updated internal state.
465 *
466 * This determines whether the lock needs to be held based on
467 * {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and
468 * {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been
469 * updated.
470 */
471 private void updateMulticastLock() {
472 final int needsLockUid = getMulticastLockNeededUid();
473 if (needsLockUid >= 0 && mHeldMulticastLock == null) {
474 final WifiManager wm = mContext.getSystemService(WifiManager.class);
475 if (wm == null) {
476 Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager");
477 return;
478 }
479 mHeldMulticastLock = wm.createMulticastLock(TAG);
480 mHeldMulticastLock.acquire();
481 mServiceLogs.log("Taking multicast lock for uid " + needsLockUid);
482 } else if (needsLockUid < 0 && mHeldMulticastLock != null) {
483 mHeldMulticastLock.release();
484 mHeldMulticastLock = null;
485 mServiceLogs.log("Released multicast lock");
486 }
487 }
488
489 /**
490 * @return The UID of an app requiring the multicast lock, or -1 if none.
491 */
492 private int getMulticastLockNeededUid() {
493 if (mWifiLockRequiredNetworks.size() == 0) {
494 // Return early if NSD is not active, or not on any relevant network
495 return -1;
496 }
Paul Hud44e1b72023-06-16 02:07:42 +0000497 for (int i = 0; i < mTransactionIdToClientInfoMap.size(); i++) {
498 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.valueAt(i);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900499 if (!mRunningAppActiveUids.contains(clientInfo.mUid)) {
500 // Ignore non-active UIDs
501 continue;
502 }
503
504 if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) {
505 return clientInfo.mUid;
506 }
507 }
508 return -1;
509 }
510
Paul Hu019621e2023-01-13 23:26:49 +0800511 /**
512 * Data class of mdns service callback information.
513 */
514 private static class MdnsEvent {
Paul Hud44e1b72023-06-16 02:07:42 +0000515 final int mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +0800516 @Nullable
Paul Hu019621e2023-01-13 23:26:49 +0800517 final MdnsServiceInfo mMdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000518 final boolean mIsServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800519
Paul Hubad6fe92023-07-24 21:25:22 +0800520 MdnsEvent(int clientRequestId) {
521 this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */);
522 }
523
524 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) {
Paul Hua6bc4632023-06-26 01:18:29 +0000525 this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
526 }
527
Paul Hubad6fe92023-07-24 21:25:22 +0800528 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo,
Paul Hua6bc4632023-06-26 01:18:29 +0000529 boolean isServiceFromCache) {
Paul Hud44e1b72023-06-16 02:07:42 +0000530 mClientRequestId = clientRequestId;
Paul Hu019621e2023-01-13 23:26:49 +0800531 mMdnsServiceInfo = mdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000532 mIsServiceFromCache = isServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800533 }
534 }
535
Paul Hu77c11182023-10-23 16:17:32 +0800536 // TODO: Use a Handler instead of a StateMachine since there are no state changes.
Irfan Sheriff75006652012-04-17 23:15:29 -0700537 private class NsdStateMachine extends StateMachine {
538
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700539 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700540
541 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700542 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900543 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700544 }
545
Luke Huang92860f92021-06-23 06:29:30 +0000546 private void maybeStartDaemon() {
Paul Hu4a87ed42024-01-10 08:58:30 +0000547 if (mIsDaemonStarted) {
548 if (DBG) Log.d(TAG, "Daemon is already started.");
Ken Chen80c9f6f2023-11-15 18:24:54 +0800549 return;
550 }
551
Paul Hu4a87ed42024-01-10 08:58:30 +0000552 if (mMDnsManager == null) {
553 Log.wtf(TAG, "maybeStartDaemon: mMDnsManager is null");
paulhu2b9ed952022-02-10 21:58:32 +0800554 return;
555 }
556 mMDnsManager.registerEventListener(mMDnsEventCallback);
557 mMDnsManager.startDaemon();
558 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000559 maybeScheduleStop();
Paul Hub2e67d32023-04-18 05:50:14 +0000560 mServiceLogs.log("Start mdns_responder daemon");
Luke Huang05298582021-06-13 16:52:05 +0000561 }
562
paulhu2b9ed952022-02-10 21:58:32 +0800563 private void maybeStopDaemon() {
Paul Hu4a87ed42024-01-10 08:58:30 +0000564 if (!mIsDaemonStarted) {
565 if (DBG) Log.d(TAG, "Daemon has not been started.");
Ken Chen80c9f6f2023-11-15 18:24:54 +0800566 return;
567 }
568
Paul Hu4a87ed42024-01-10 08:58:30 +0000569 if (mMDnsManager == null) {
570 Log.wtf(TAG, "maybeStopDaemon: mMDnsManager is null");
paulhu2b9ed952022-02-10 21:58:32 +0800571 return;
572 }
573 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
574 mMDnsManager.stopDaemon();
575 mIsDaemonStarted = false;
Paul Hub2e67d32023-04-18 05:50:14 +0000576 mServiceLogs.log("Stop mdns_responder daemon");
paulhu2b9ed952022-02-10 21:58:32 +0800577 }
578
Luke Huang92860f92021-06-23 06:29:30 +0000579 private boolean isAnyRequestActive() {
Paul Hud44e1b72023-06-16 02:07:42 +0000580 return mTransactionIdToClientInfoMap.size() != 0;
Luke Huang92860f92021-06-23 06:29:30 +0000581 }
582
583 private void scheduleStop() {
584 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
585 }
586 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800587 // The native daemon should stay alive and can't be cleanup
588 // if any legacy client connected.
589 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000590 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000591 }
592 }
593
Luke Huang92860f92021-06-23 06:29:30 +0000594 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000595 this.removeMessages(NsdManager.DAEMON_CLEANUP);
596 }
597
Paul Hu23fa2022023-01-13 22:57:24 +0800598 private void maybeStartMonitoringSockets() {
599 if (mIsMonitoringSocketsStarted) {
600 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
601 return;
602 }
603
604 mMdnsSocketProvider.startMonitoringSockets();
605 mIsMonitoringSocketsStarted = true;
606 }
607
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900608 private void maybeStopMonitoringSocketsIfNoActiveRequest() {
609 if (!mIsMonitoringSocketsStarted) return;
610 if (isAnyRequestActive()) return;
611
Paul Hu58f20602023-02-18 11:41:07 +0800612 mMdnsSocketProvider.requestStopWhenInactive();
Paul Hu23fa2022023-01-13 22:57:24 +0800613 mIsMonitoringSocketsStarted = false;
614 }
615
Hugo Benichi803a2f02017-04-24 11:35:06 +0900616 NsdStateMachine(String name, Handler handler) {
617 super(name, handler);
Paul Hu77c11182023-10-23 16:17:32 +0800618 addState(mEnabledState);
paulhu5568f452021-11-30 13:31:29 +0800619 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900620 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700621 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700622 }
623
Irfan Sheriff75006652012-04-17 23:15:29 -0700624 class EnabledState extends State {
625 @Override
626 public void enter() {
627 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700628 }
629
630 @Override
631 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000632 // TODO: it is incorrect to stop the daemon without expunging all requests
633 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000634 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700635 }
636
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700637 private boolean requestLimitReached(ClientInfo clientInfo) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900638 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800639 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700640 return true;
641 }
642 return false;
643 }
644
Paul Hu508a0122023-09-11 15:31:33 +0800645 private ClientRequest storeLegacyRequestMap(int clientRequestId, int transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +0000646 ClientInfo clientInfo, int what, long startTimeMs) {
Paul Hu508a0122023-09-11 15:31:33 +0800647 final LegacyClientRequest request =
648 new LegacyClientRequest(transactionId, what, startTimeMs);
649 clientInfo.mClientRequests.put(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000650 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000651 // Remove the cleanup event because here comes a new request.
652 cancelStop();
Paul Hu508a0122023-09-11 15:31:33 +0800653 return request;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700654 }
655
Paul Hud44e1b72023-06-16 02:07:42 +0000656 private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900657 ClientInfo clientInfo, @Nullable Network requestedNetwork) {
Paul Hu777ed052023-06-19 13:35:15 +0000658 clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
Paul Hu812e9212023-06-20 06:24:53 +0000659 transactionId, requestedNetwork, mClock.elapsedRealtime()));
Paul Hud44e1b72023-06-16 02:07:42 +0000660 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900661 updateMulticastLock();
Paul Hu23fa2022023-01-13 22:57:24 +0800662 }
663
Paul Hud44e1b72023-06-16 02:07:42 +0000664 private void removeRequestMap(
665 int clientRequestId, int transactionId, ClientInfo clientInfo) {
666 final ClientRequest existing = clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900667 if (existing == null) return;
Paul Hud44e1b72023-06-16 02:07:42 +0000668 clientInfo.mClientRequests.remove(clientRequestId);
669 mTransactionIdToClientInfoMap.remove(transactionId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900670
671 if (existing instanceof LegacyClientRequest) {
672 maybeScheduleStop();
673 } else {
674 maybeStopMonitoringSocketsIfNoActiveRequest();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900675 updateMulticastLock();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900676 }
677 }
678
Paul Hu508a0122023-09-11 15:31:33 +0800679 private ClientRequest storeDiscoveryManagerRequestMap(int clientRequestId,
680 int transactionId, MdnsListener listener, ClientInfo clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900681 @Nullable Network requestedNetwork) {
Paul Hu508a0122023-09-11 15:31:33 +0800682 final DiscoveryManagerRequest request = new DiscoveryManagerRequest(transactionId,
683 listener, requestedNetwork, mClock.elapsedRealtime());
684 clientInfo.mClientRequests.put(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000685 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900686 updateMulticastLock();
Paul Hu508a0122023-09-11 15:31:33 +0800687 return request;
Paul Hu23fa2022023-01-13 22:57:24 +0800688 }
689
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900690 /**
691 * Truncate a service name to up to 63 UTF-8 bytes.
692 *
693 * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
694 * names used in registerService follows historical behavior (see mdnsresponder
695 * handle_regservice_request).
696 */
697 @NonNull
698 private String truncateServiceName(@NonNull String originalName) {
Yuyang Huangde802c82023-05-02 17:14:22 +0900699 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
Paul Hu23fa2022023-01-13 22:57:24 +0800700 }
701
Paul Hud44e1b72023-06-16 02:07:42 +0000702 private void stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId,
703 int transactionId, ClientInfo clientInfo) {
Paul Hue4f5f252023-02-16 21:13:47 +0800704 clientInfo.unregisterMdnsListenerFromRequest(request);
Paul Hud44e1b72023-06-16 02:07:42 +0000705 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hue4f5f252023-02-16 21:13:47 +0800706 }
707
Paul Hu77c11182023-10-23 16:17:32 +0800708 private ClientInfo getClientInfoForReply(Message msg) {
709 final ListenerArgs args = (ListenerArgs) msg.obj;
710 return mClients.get(args.connector);
711 }
712
Kangping Dong5af24b62023-12-10 21:41:16 +0800713 /**
714 * Returns {@code false} if {@code subtypes} exceeds the maximum number limit or
715 * contains invalid subtype label.
716 */
717 private boolean checkSubtypeLabels(Set<String> subtypes) {
718 if (subtypes.size() > MAX_SUBTYPE_COUNT) {
719 mServiceLogs.e(
720 "Too many subtypes: " + subtypes.size() + " (max = "
721 + MAX_SUBTYPE_COUNT + ")");
722 return false;
723 }
724
725 for (String subtype : subtypes) {
726 if (!checkSubtypeLabel(subtype)) {
727 mServiceLogs.e("Subtype " + subtype + " is invalid");
728 return false;
729 }
730 }
731 return true;
732 }
733
734 private Set<String> dedupSubtypeLabels(Collection<String> subtypes) {
735 final Map<String, String> subtypeMap = new LinkedHashMap<>(subtypes.size());
736 for (String subtype : subtypes) {
737 subtypeMap.put(MdnsUtils.toDnsLowerCase(subtype), subtype);
738 }
739 return new ArraySet<>(subtypeMap.values());
740 }
741
Irfan Sheriff75006652012-04-17 23:15:29 -0700742 @Override
743 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900744 final ClientInfo clientInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000745 final int transactionId;
746 final int clientRequestId = msg.arg2;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900747 final OffloadEngineInfo offloadEngineInfo;
Irfan Sheriff75006652012-04-17 23:15:29 -0700748 switch (msg.what) {
Paul Hu75069ed2023-01-14 00:31:09 +0800749 case NsdManager.DISCOVER_SERVICES: {
paulhub2225702021-11-17 09:35:33 +0800750 if (DBG) Log.d(TAG, "Discover services");
Kangping Dong1f1a3792023-12-10 22:05:04 +0800751 final DiscoveryArgs discoveryArgs = (DiscoveryArgs) msg.obj;
752 clientInfo = mClients.get(discoveryArgs.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000753 // If the binder death notification for a INsdManagerCallback was received
754 // before any calls are received by NsdService, the clientInfo would be
755 // cleared and cause NPE. Add a null check here to prevent this corner case.
756 if (clientInfo == null) {
757 Log.e(TAG, "Unknown connector in discovery");
758 break;
759 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700760
761 if (requestLimitReached(clientInfo)) {
Paul Hu508a0122023-09-11 15:31:33 +0800762 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
763 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
Irfan Sheriff75006652012-04-17 23:15:29 -0700764 break;
765 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700766
Kangping Dong1f1a3792023-12-10 22:05:04 +0800767 final DiscoveryRequest discoveryRequest = discoveryArgs.discoveryRequest;
Paul Hud44e1b72023-06-16 02:07:42 +0000768 transactionId = getUniqueId();
Yuyang Huang170d42f2023-12-09 15:26:16 +0900769 final Pair<String, List<String>> typeAndSubtype =
Kangping Dong1f1a3792023-12-10 22:05:04 +0800770 parseTypeAndSubtype(discoveryRequest.getServiceType());
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900771 final String serviceType = typeAndSubtype == null
772 ? null : typeAndSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800773 if (clientInfo.mUseJavaBackend
774 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900775 || useDiscoveryManagerForType(serviceType)) {
Yuyang Huang170d42f2023-12-09 15:26:16 +0900776 if (serviceType == null || typeAndSubtype.second.size() > 1) {
Paul Hu508a0122023-09-11 15:31:33 +0800777 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
778 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Paul Hu23fa2022023-01-13 22:57:24 +0800779 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700780 }
Paul Hu23fa2022023-01-13 22:57:24 +0800781
Kangping Dong1f1a3792023-12-10 22:05:04 +0800782 String subtype = discoveryRequest.getSubtype();
783 if (subtype == null && !typeAndSubtype.second.isEmpty()) {
784 subtype = typeAndSubtype.second.get(0);
785 }
786
787 if (subtype != null && !checkSubtypeLabel(subtype)) {
788 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
789 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
790 break;
791 }
792
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900793 final String listenServiceType = serviceType + ".local";
Paul Hu23fa2022023-01-13 22:57:24 +0800794 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +0000795 final MdnsListener listener = new DiscoveryListener(clientRequestId,
Kangping Dong97b2adc2024-01-11 16:00:37 +0800796 transactionId, listenServiceType);
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900797 final MdnsSearchOptions.Builder optionsBuilder =
798 MdnsSearchOptions.newBuilder()
Kangping Dong1f1a3792023-12-10 22:05:04 +0800799 .setNetwork(discoveryRequest.getNetwork())
Yuyang Huangff963222023-06-01 18:42:42 +0900800 .setRemoveExpiredService(true)
Paul Hu11a883d2023-12-14 07:36:44 +0000801 .setQueryMode(
802 mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
803 ? AGGRESSIVE_QUERY_MODE
804 : PASSIVE_QUERY_MODE);
Kangping Dong1f1a3792023-12-10 22:05:04 +0800805 if (subtype != null) {
806 // checkSubtypeLabels() ensures that subtypes start with '_' but
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900807 // MdnsSearchOptions expects the underscore to not be present.
Kangping Dong1f1a3792023-12-10 22:05:04 +0800808 optionsBuilder.addSubtype(subtype.substring(1));
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900809 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900810 mMdnsDiscoveryManager.registerListener(
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900811 listenServiceType, listener, optionsBuilder.build());
Paul Hu508a0122023-09-11 15:31:33 +0800812 final ClientRequest request = storeDiscoveryManagerRequestMap(
813 clientRequestId, transactionId, listener, clientInfo,
Kangping Dong1f1a3792023-12-10 22:05:04 +0800814 discoveryRequest.getNetwork());
815 clientInfo.onDiscoverServicesStarted(
816 clientRequestId, discoveryRequest, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000817 clientInfo.log("Register a DiscoveryListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +0000818 + " for service type:" + listenServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -0700819 } else {
Paul Hu23fa2022023-01-13 22:57:24 +0800820 maybeStartDaemon();
Kangping Dong1f1a3792023-12-10 22:05:04 +0800821 if (discoverServices(transactionId, discoveryRequest)) {
Paul Hu23fa2022023-01-13 22:57:24 +0800822 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +0000823 Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
Kangping Dong1f1a3792023-12-10 22:05:04 +0800824 + discoveryRequest.getServiceType());
Paul Hu23fa2022023-01-13 22:57:24 +0800825 }
Paul Hu508a0122023-09-11 15:31:33 +0800826 final ClientRequest request = storeLegacyRequestMap(clientRequestId,
827 transactionId, clientInfo, msg.what,
828 mClock.elapsedRealtime());
Paul Hu812e9212023-06-20 06:24:53 +0000829 clientInfo.onDiscoverServicesStarted(
Kangping Dong1f1a3792023-12-10 22:05:04 +0800830 clientRequestId, discoveryRequest, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800831 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000832 stopServiceDiscovery(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800833 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
834 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Paul Hu23fa2022023-01-13 22:57:24 +0800835 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700836 }
837 break;
Paul Hu75069ed2023-01-14 00:31:09 +0800838 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900839 case NsdManager.STOP_DISCOVERY: {
paulhub2225702021-11-17 09:35:33 +0800840 if (DBG) Log.d(TAG, "Stop service discovery");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900841 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900842 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000843 // If the binder death notification for a INsdManagerCallback was received
844 // before any calls are received by NsdService, the clientInfo would be
845 // cleared and cause NPE. Add a null check here to prevent this corner case.
846 if (clientInfo == null) {
847 Log.e(TAG, "Unknown connector in stop discovery");
848 break;
849 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700850
Paul Hud44e1b72023-06-16 02:07:42 +0000851 final ClientRequest request =
852 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900853 if (request == null) {
854 Log.e(TAG, "Unknown client request in STOP_DISCOVERY");
Irfan Sheriff75006652012-04-17 23:15:29 -0700855 break;
856 }
Paul Hud44e1b72023-06-16 02:07:42 +0000857 transactionId = request.mTransactionId;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900858 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
859 // point, so this needs to check the type of the original request to
860 // unregister instead of looking at the flag value.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900861 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +0000862 stopDiscoveryManagerRequest(
863 request, clientRequestId, transactionId, clientInfo);
Paul Hu812e9212023-06-20 06:24:53 +0000864 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000865 clientInfo.log("Unregister the DiscoveryListener " + transactionId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700866 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000867 removeRequestMap(clientRequestId, transactionId, clientInfo);
868 if (stopServiceDiscovery(transactionId)) {
Paul Hu812e9212023-06-20 06:24:53 +0000869 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800870 } else {
871 clientInfo.onStopDiscoveryFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000872 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu23fa2022023-01-13 22:57:24 +0800873 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700874 }
875 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900876 }
877 case NsdManager.REGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800878 if (DBG) Log.d(TAG, "Register service");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900879 final AdvertisingArgs args = (AdvertisingArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900880 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000881 // If the binder death notification for a INsdManagerCallback was received
882 // before any calls are received by NsdService, the clientInfo would be
883 // cleared and cause NPE. Add a null check here to prevent this corner case.
884 if (clientInfo == null) {
885 Log.e(TAG, "Unknown connector in registration");
886 break;
887 }
888
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700889 if (requestLimitReached(clientInfo)) {
Paul Hu508a0122023-09-11 15:31:33 +0800890 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
891 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700892 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700893 }
Yuyang Huang86d083f2023-12-12 19:56:41 +0900894 final AdvertisingRequest advertisingRequest = args.advertisingRequest;
895 if (advertisingRequest == null) {
896 Log.e(TAG, "Unknown advertisingRequest in registration");
897 break;
898 }
899 final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo();
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900900 final String serviceType = serviceInfo.getServiceType();
Yuyang Huang170d42f2023-12-09 15:26:16 +0900901 final Pair<String, List<String>> typeSubtype = parseTypeAndSubtype(
902 serviceType);
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900903 final String registerServiceType = typeSubtype == null
904 ? null : typeSubtype.first;
Handa Wang096e32e2024-01-14 08:21:28 +0000905 final String hostname = serviceInfo.getHostname();
906 // Keep compatible with the legacy behavior: It's allowed to set host
907 // addresses for a service registration although the host addresses
908 // won't be registered. To register the addresses for a host, the
909 // hostname must be specified.
910 if (hostname == null) {
911 serviceInfo.setHostAddresses(Collections.emptyList());
912 }
Paul Hu2e0a88c2023-03-09 16:05:01 +0800913 if (clientInfo.mUseJavaBackend
914 || mDeps.isMdnsAdvertiserEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900915 || useAdvertiserForType(registerServiceType)) {
Handa Wang096e32e2024-01-14 08:21:28 +0000916 if (serviceType != null && registerServiceType == null) {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900917 Log.e(TAG, "Invalid service type: " + serviceType);
Paul Hu508a0122023-09-11 15:31:33 +0800918 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
919 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900920 break;
921 }
Yuyang Huang86d083f2023-12-12 19:56:41 +0900922 boolean isUpdateOnly = (advertisingRequest.getAdvertisingConfig()
923 & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0;
924 // If it is an update request, then reuse the old transactionId
925 if (isUpdateOnly) {
926 final ClientRequest existingClientRequest =
927 clientInfo.mClientRequests.get(clientRequestId);
928 if (existingClientRequest == null) {
929 Log.e(TAG, "Invalid update on requestId: " + clientRequestId);
930 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
931 NsdManager.FAILURE_INTERNAL_ERROR,
932 false /* isLegacy */);
933 break;
934 }
935 transactionId = existingClientRequest.mTransactionId;
936 } else {
937 transactionId = getUniqueId();
938 }
Handa Wang096e32e2024-01-14 08:21:28 +0000939
940 if (registerServiceType != null) {
941 serviceInfo.setServiceType(registerServiceType);
942 serviceInfo.setServiceName(
943 truncateServiceName(serviceInfo.getServiceName()));
944 }
945
946 if (!checkHostname(hostname)) {
947 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
948 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
949 break;
950 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900951
Kangping Dong5af24b62023-12-10 21:41:16 +0800952 Set<String> subtypes = new ArraySet<>(serviceInfo.getSubtypes());
Handa Wang096e32e2024-01-14 08:21:28 +0000953 if (typeSubtype != null && typeSubtype.second != null) {
954 for (String subType : typeSubtype.second) {
955 if (!TextUtils.isEmpty(subType)) {
956 subtypes.add(subType);
957 }
Yuyang Huang170d42f2023-12-09 15:26:16 +0900958 }
Kangping Dong5af24b62023-12-10 21:41:16 +0800959 }
Kangping Dong5af24b62023-12-10 21:41:16 +0800960 subtypes = dedupSubtypeLabels(subtypes);
961
962 if (!checkSubtypeLabels(subtypes)) {
963 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
964 NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */);
965 break;
966 }
967
968 serviceInfo.setSubtypes(subtypes);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900969 maybeStartMonitoringSockets();
Yuyang Huang86d083f2023-12-12 19:56:41 +0900970 final MdnsAdvertisingOptions mdnsAdvertisingOptions =
971 MdnsAdvertisingOptions.newBuilder().setIsOnlyUpdate(
972 isUpdateOnly).build();
Yuyang Huange5cba9c2023-11-02 18:05:47 +0900973 mAdvertiser.addOrUpdateService(transactionId, serviceInfo,
Handa Wang096e32e2024-01-14 08:21:28 +0000974 mdnsAdvertisingOptions, clientInfo.mUid);
Paul Hud44e1b72023-06-16 02:07:42 +0000975 storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900976 serviceInfo.getNetwork());
Irfan Sheriff75006652012-04-17 23:15:29 -0700977 } else {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900978 maybeStartDaemon();
Yuyang Huang86d083f2023-12-12 19:56:41 +0900979 transactionId = getUniqueId();
Paul Hud44e1b72023-06-16 02:07:42 +0000980 if (registerService(transactionId, serviceInfo)) {
981 if (DBG) {
982 Log.d(TAG, "Register " + clientRequestId
983 + " " + transactionId);
984 }
Paul Hua6bc4632023-06-26 01:18:29 +0000985 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
986 msg.what, mClock.elapsedRealtime());
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900987 // Return success after mDns reports success
988 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000989 unregisterService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800990 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
991 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900992 }
993
Irfan Sheriff75006652012-04-17 23:15:29 -0700994 }
995 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900996 }
997 case NsdManager.UNREGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800998 if (DBG) Log.d(TAG, "unregister service");
Yuyang Huang86d083f2023-12-12 19:56:41 +0900999 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001000 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +00001001 // If the binder death notification for a INsdManagerCallback was received
1002 // before any calls are received by NsdService, the clientInfo would be
1003 // cleared and cause NPE. Add a null check here to prevent this corner case.
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001004 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +08001005 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001006 break;
Irfan Sheriff75006652012-04-17 23:15:29 -07001007 }
Paul Hud44e1b72023-06-16 02:07:42 +00001008 final ClientRequest request =
1009 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001010 if (request == null) {
1011 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE");
1012 break;
1013 }
Paul Hud44e1b72023-06-16 02:07:42 +00001014 transactionId = request.mTransactionId;
1015 removeRequestMap(clientRequestId, transactionId, clientInfo);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001016
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001017 // Note isMdnsAdvertiserEnabled may have changed to false at this point,
1018 // so this needs to check the type of the original request to unregister
1019 // instead of looking at the flag value.
1020 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +08001021 final AdvertiserMetrics metrics =
1022 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00001023 mAdvertiser.removeService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08001024 clientInfo.onUnregisterServiceSucceeded(
1025 clientRequestId, request, metrics);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001026 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001027 if (unregisterService(transactionId)) {
Paul Hu508a0122023-09-11 15:31:33 +08001028 clientInfo.onUnregisterServiceSucceeded(clientRequestId, request,
Paul Hu043bcd42023-07-14 16:38:25 +08001029 new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
1030 NO_PACKET /* sentPacketCount */,
1031 0 /* conflictDuringProbingCount */,
1032 0 /* conflictAfterProbingCount */));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001033 } else {
1034 clientInfo.onUnregisterServiceFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001035 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001036 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001037 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001038 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001039 }
Paul Hu75069ed2023-01-14 00:31:09 +08001040 case NsdManager.RESOLVE_SERVICE: {
paulhub2225702021-11-17 09:35:33 +08001041 if (DBG) Log.d(TAG, "Resolve service");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001042 final ListenerArgs args = (ListenerArgs) msg.obj;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001043 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +00001044 // If the binder death notification for a INsdManagerCallback was received
1045 // before any calls are received by NsdService, the clientInfo would be
1046 // cleared and cause NPE. Add a null check here to prevent this corner case.
1047 if (clientInfo == null) {
1048 Log.e(TAG, "Unknown connector in resolution");
1049 break;
1050 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001051
Paul Hu75069ed2023-01-14 00:31:09 +08001052 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001053 transactionId = getUniqueId();
Yuyang Huang170d42f2023-12-09 15:26:16 +09001054 final Pair<String, List<String>> typeSubtype =
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001055 parseTypeAndSubtype(info.getServiceType());
1056 final String serviceType = typeSubtype == null
1057 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +08001058 if (clientInfo.mUseJavaBackend
1059 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001060 || useDiscoveryManagerForType(serviceType)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001061 if (serviceType == null) {
Paul Hu508a0122023-09-11 15:31:33 +08001062 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1063 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001064 break;
1065 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001066 final String resolveServiceType = serviceType + ".local";
Paul Hu75069ed2023-01-14 00:31:09 +08001067
1068 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001069 final MdnsListener listener = new ResolutionListener(clientRequestId,
Kangping Dong97b2adc2024-01-11 16:00:37 +08001070 transactionId, resolveServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +08001071 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1072 .setNetwork(info.getNetwork())
Paul Hu11a883d2023-12-14 07:36:44 +00001073 .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
1074 ? AGGRESSIVE_QUERY_MODE
1075 : PASSIVE_QUERY_MODE)
Remi NGUYEN VANbb62b1d2023-02-27 12:18:27 +09001076 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001077 .setRemoveExpiredService(true)
Paul Hu75069ed2023-01-14 00:31:09 +08001078 .build();
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001079 mMdnsDiscoveryManager.registerListener(
1080 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001081 storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
1082 listener, clientInfo, info.getNetwork());
1083 clientInfo.log("Register a ResolutionListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001084 + " for service type:" + resolveServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -07001085 } else {
Paul Hu75069ed2023-01-14 00:31:09 +08001086 if (clientInfo.mResolvedService != null) {
Paul Hu508a0122023-09-11 15:31:33 +08001087 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1088 NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001089 break;
1090 }
1091
1092 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +00001093 if (resolveService(transactionId, info)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001094 clientInfo.mResolvedService = new NsdServiceInfo();
Paul Hua6bc4632023-06-26 01:18:29 +00001095 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
1096 msg.what, mClock.elapsedRealtime());
Paul Hu75069ed2023-01-14 00:31:09 +08001097 } else {
Paul Hu508a0122023-09-11 15:31:33 +08001098 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1099 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001100 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001101 }
1102 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001103 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001104 case NsdManager.STOP_RESOLUTION: {
Paul Hub58deb72022-12-26 09:24:42 +00001105 if (DBG) Log.d(TAG, "Stop service resolution");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001106 final ListenerArgs args = (ListenerArgs) msg.obj;
Paul Hub58deb72022-12-26 09:24:42 +00001107 clientInfo = mClients.get(args.connector);
1108 // If the binder death notification for a INsdManagerCallback was received
1109 // before any calls are received by NsdService, the clientInfo would be
1110 // cleared and cause NPE. Add a null check here to prevent this corner case.
1111 if (clientInfo == null) {
1112 Log.e(TAG, "Unknown connector in stop resolution");
1113 break;
1114 }
1115
Paul Hud44e1b72023-06-16 02:07:42 +00001116 final ClientRequest request =
1117 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001118 if (request == null) {
1119 Log.e(TAG, "Unknown client request in STOP_RESOLUTION");
1120 break;
1121 }
Paul Hud44e1b72023-06-16 02:07:42 +00001122 transactionId = request.mTransactionId;
Paul Hue4f5f252023-02-16 21:13:47 +08001123 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
1124 // point, so this needs to check the type of the original request to
1125 // unregister instead of looking at the flag value.
1126 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001127 stopDiscoveryManagerRequest(
1128 request, clientRequestId, transactionId, clientInfo);
Paul Hu60149052023-07-31 14:26:08 +08001129 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001130 clientInfo.log("Unregister the ResolutionListener " + transactionId);
Paul Hub58deb72022-12-26 09:24:42 +00001131 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001132 removeRequestMap(clientRequestId, transactionId, clientInfo);
1133 if (stopResolveService(transactionId)) {
Paul Hu60149052023-07-31 14:26:08 +08001134 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hue4f5f252023-02-16 21:13:47 +08001135 } else {
1136 clientInfo.onStopResolutionFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001137 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
Paul Hue4f5f252023-02-16 21:13:47 +08001138 }
1139 clientInfo.mResolvedService = null;
Paul Hub58deb72022-12-26 09:24:42 +00001140 }
Paul Hub58deb72022-12-26 09:24:42 +00001141 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001142 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001143 case NsdManager.REGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001144 if (DBG) Log.d(TAG, "Register a service callback");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001145 final ListenerArgs args = (ListenerArgs) msg.obj;
Paul Hu18aeccc2022-12-27 08:48:48 +00001146 clientInfo = mClients.get(args.connector);
1147 // If the binder death notification for a INsdManagerCallback was received
1148 // before any calls are received by NsdService, the clientInfo would be
1149 // cleared and cause NPE. Add a null check here to prevent this corner case.
1150 if (clientInfo == null) {
1151 Log.e(TAG, "Unknown connector in callback registration");
1152 break;
1153 }
1154
Paul Hu30bd70d2023-02-07 13:20:56 +00001155 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001156 transactionId = getUniqueId();
Yuyang Huang170d42f2023-12-09 15:26:16 +09001157 final Pair<String, List<String>> typeAndSubtype =
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001158 parseTypeAndSubtype(info.getServiceType());
1159 final String serviceType = typeAndSubtype == null
1160 ? null : typeAndSubtype.first;
Paul Hu30bd70d2023-02-07 13:20:56 +00001161 if (serviceType == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001162 clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId,
Paul Hu30bd70d2023-02-07 13:20:56 +00001163 NsdManager.FAILURE_BAD_PARAMETERS);
Paul Hu18aeccc2022-12-27 08:48:48 +00001164 break;
1165 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001166 final String resolveServiceType = serviceType + ".local";
Paul Hu18aeccc2022-12-27 08:48:48 +00001167
Paul Hu30bd70d2023-02-07 13:20:56 +00001168 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001169 final MdnsListener listener = new ServiceInfoListener(clientRequestId,
Kangping Dong97b2adc2024-01-11 16:00:37 +08001170 transactionId, resolveServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +00001171 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1172 .setNetwork(info.getNetwork())
Paul Hu11a883d2023-12-14 07:36:44 +00001173 .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled()
1174 ? AGGRESSIVE_QUERY_MODE
1175 : PASSIVE_QUERY_MODE)
Paul Hu30bd70d2023-02-07 13:20:56 +00001176 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001177 .setRemoveExpiredService(true)
Paul Hu30bd70d2023-02-07 13:20:56 +00001178 .build();
1179 mMdnsDiscoveryManager.registerListener(
1180 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001181 storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener,
1182 clientInfo, info.getNetwork());
Paul Huddce5912023-08-01 10:26:49 +08001183 clientInfo.onServiceInfoCallbackRegistered(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00001184 clientInfo.log("Register a ServiceInfoListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001185 + " for service type:" + resolveServiceType);
Paul Hu18aeccc2022-12-27 08:48:48 +00001186 break;
Paul Hu30bd70d2023-02-07 13:20:56 +00001187 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001188 case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001189 if (DBG) Log.d(TAG, "Unregister a service callback");
Yuyang Huang86d083f2023-12-12 19:56:41 +09001190 final ListenerArgs args = (ListenerArgs) msg.obj;
Paul Hu18aeccc2022-12-27 08:48:48 +00001191 clientInfo = mClients.get(args.connector);
1192 // If the binder death notification for a INsdManagerCallback was received
1193 // before any calls are received by NsdService, the clientInfo would be
1194 // cleared and cause NPE. Add a null check here to prevent this corner case.
1195 if (clientInfo == null) {
1196 Log.e(TAG, "Unknown connector in callback unregistration");
1197 break;
1198 }
1199
Paul Hud44e1b72023-06-16 02:07:42 +00001200 final ClientRequest request =
1201 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001202 if (request == null) {
Paul Hu30bd70d2023-02-07 13:20:56 +00001203 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001204 break;
1205 }
Paul Hud44e1b72023-06-16 02:07:42 +00001206 transactionId = request.mTransactionId;
Paul Hu30bd70d2023-02-07 13:20:56 +00001207 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001208 stopDiscoveryManagerRequest(
1209 request, clientRequestId, transactionId, clientInfo);
Paul Huddce5912023-08-01 10:26:49 +08001210 clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001211 clientInfo.log("Unregister the ServiceInfoListener " + transactionId);
Paul Hu18aeccc2022-12-27 08:48:48 +00001212 } else {
Paul Hu30bd70d2023-02-07 13:20:56 +00001213 loge("Unregister failed with non-DiscoveryManagerRequest.");
Paul Hu18aeccc2022-12-27 08:48:48 +00001214 }
Paul Hu18aeccc2022-12-27 08:48:48 +00001215 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001216 }
paulhu2b9ed952022-02-10 21:58:32 +08001217 case MDNS_SERVICE_EVENT:
1218 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +09001219 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001220 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001221 break;
Paul Hu019621e2023-01-13 23:26:49 +08001222 case MDNS_DISCOVERY_MANAGER_EVENT:
1223 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
1224 return NOT_HANDLED;
1225 }
1226 break;
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001227 case NsdManager.REGISTER_OFFLOAD_ENGINE:
1228 offloadEngineInfo = (OffloadEngineInfo) msg.obj;
1229 // TODO: Limits the number of registrations created by a given class.
1230 mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
1231 offloadEngineInfo);
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001232 sendAllOffloadServiceInfos(offloadEngineInfo);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001233 break;
1234 case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
1235 mOffloadEngines.unregister((IOffloadEngine) msg.obj);
1236 break;
Paul Hu77c11182023-10-23 16:17:32 +08001237 case NsdManager.REGISTER_CLIENT:
1238 final ConnectorArgs arg = (ConnectorArgs) msg.obj;
1239 final INsdManagerCallback cb = arg.callback;
1240 try {
1241 cb.asBinder().linkToDeath(arg.connector, 0);
1242 final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
1243 final NetworkNsdReportedMetrics metrics =
1244 mDeps.makeNetworkNsdReportedMetrics(
1245 (int) mClock.elapsedRealtime());
1246 clientInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
1247 mServiceLogs.forSubComponent(tag), metrics);
1248 mClients.put(arg.connector, clientInfo);
1249 } catch (RemoteException e) {
1250 Log.w(TAG, "Client request id " + clientRequestId
1251 + " has already died");
1252 }
1253 break;
1254 case NsdManager.UNREGISTER_CLIENT:
1255 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
1256 clientInfo = mClients.remove(connector);
1257 if (clientInfo != null) {
1258 clientInfo.expungeAllRequests();
1259 if (clientInfo.isPreSClient()) {
1260 mLegacyClientCount -= 1;
1261 }
1262 }
1263 maybeStopMonitoringSocketsIfNoActiveRequest();
1264 maybeScheduleStop();
1265 break;
1266 case NsdManager.DAEMON_CLEANUP:
1267 maybeStopDaemon();
1268 break;
1269 // This event should be only sent by the legacy (target SDK < S) clients.
1270 // Mark the sending client as legacy.
1271 case NsdManager.DAEMON_STARTUP:
1272 clientInfo = getClientInfoForReply(msg);
1273 if (clientInfo != null) {
1274 cancelStop();
1275 clientInfo.setPreSClient();
1276 mLegacyClientCount += 1;
1277 maybeStartDaemon();
1278 }
1279 break;
Irfan Sheriff75006652012-04-17 23:15:29 -07001280 default:
Paul Hu77c11182023-10-23 16:17:32 +08001281 Log.wtf(TAG, "Unhandled " + msg);
Hugo Benichif0c84092017-04-05 14:43:29 +09001282 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001283 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001284 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001285 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001286
Paul Hud44e1b72023-06-16 02:07:42 +00001287 private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001288 NsdServiceInfo servInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001289 ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001290 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001291 Log.e(TAG, String.format(
1292 "transactionId %d for %d has no client mapping", transactionId, code));
Hugo Benichif0c84092017-04-05 14:43:29 +09001293 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001294 }
1295
1296 /* This goes in response as msg.arg2 */
Paul Hud44e1b72023-06-16 02:07:42 +00001297 int clientRequestId = clientInfo.getClientRequestId(transactionId);
1298 if (clientRequestId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -07001299 // This can happen because of race conditions. For example,
1300 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
1301 // and we may get in this situation.
Paul Hud44e1b72023-06-16 02:07:42 +00001302 Log.d(TAG, String.format("%d for transactionId %d that is no longer active",
1303 code, transactionId));
Hugo Benichif0c84092017-04-05 14:43:29 +09001304 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001305 }
Paul Hu812e9212023-06-20 06:24:53 +00001306 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1307 if (request == null) {
1308 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1309 return false;
1310 }
Hugo Benichi32be63d2017-04-05 14:06:11 +09001311 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00001312 Log.d(TAG, String.format(
1313 "MDns service event code:%d transactionId=%d", code, transactionId));
Hugo Benichi32be63d2017-04-05 14:06:11 +09001314 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001315 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +08001316 case IMDnsEventListener.SERVICE_FOUND: {
1317 final DiscoveryInfo info = (DiscoveryInfo) obj;
1318 final String name = info.serviceName;
1319 final String type = info.registrationType;
1320 servInfo = new NsdServiceInfo(name, type);
1321 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001322 if (foundNetId == 0L) {
1323 // Ignore services that do not have a Network: they are not usable
1324 // by apps, as they would need privileged permissions to use
1325 // interfaces that do not have an associated Network.
1326 break;
1327 }
Remi NGUYEN VAN643edb62023-01-23 19:14:57 +09001328 if (foundNetId == INetd.DUMMY_NET_ID) {
1329 // Ignore services on the dummy0 interface: they are only seen when
1330 // discovering locally advertised services, and are not reachable
1331 // through that interface.
1332 break;
1333 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001334 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001335
1336 clientInfo.onServiceFound(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001337 break;
paulhu2b9ed952022-02-10 21:58:32 +08001338 }
1339 case IMDnsEventListener.SERVICE_LOST: {
1340 final DiscoveryInfo info = (DiscoveryInfo) obj;
1341 final String name = info.serviceName;
1342 final String type = info.registrationType;
1343 final int lostNetId = info.netId;
1344 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001345 // The network could be set to null (netId 0) if it was torn down when the
1346 // service is lost
1347 // TODO: avoid returning null in that case, possibly by remembering
1348 // found services on the same interface index and their network at the time
1349 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001350 clientInfo.onServiceLost(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001351 break;
paulhu2b9ed952022-02-10 21:58:32 +08001352 }
1353 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Paul Hu812e9212023-06-20 06:24:53 +00001354 clientInfo.onDiscoverServicesFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001355 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1356 transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001357 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001358 break;
paulhu2b9ed952022-02-10 21:58:32 +08001359 case IMDnsEventListener.SERVICE_REGISTERED: {
1360 final RegistrationInfo info = (RegistrationInfo) obj;
1361 final String name = info.serviceName;
1362 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Paul Hu508a0122023-09-11 15:31:33 +08001363 clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001364 break;
paulhu2b9ed952022-02-10 21:58:32 +08001365 }
1366 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Paul Hu777ed052023-06-19 13:35:15 +00001367 clientInfo.onRegisterServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001368 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1369 transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001370 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001371 break;
paulhu2b9ed952022-02-10 21:58:32 +08001372 case IMDnsEventListener.SERVICE_RESOLVED: {
1373 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001374 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +08001375 final String fullName = info.serviceFullName;
1376 while (index < fullName.length() && fullName.charAt(index) != '.') {
1377 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001378 ++index;
1379 }
1380 ++index;
1381 }
paulhu2b9ed952022-02-10 21:58:32 +08001382 if (index >= fullName.length()) {
1383 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001384 break;
1385 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001386
paulhube186602022-04-12 07:18:23 +00001387 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +08001388 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001389 String type = rest.replace(".local.", "");
1390
Paul Hu30bd70d2023-02-07 13:20:56 +00001391 final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
Paul Hu18aeccc2022-12-27 08:48:48 +00001392 serviceInfo.setServiceName(name);
1393 serviceInfo.setServiceType(type);
1394 serviceInfo.setPort(info.port);
1395 serviceInfo.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001396 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001397
Paul Hud44e1b72023-06-16 02:07:42 +00001398 stopResolveService(transactionId);
1399 removeRequestMap(clientRequestId, transactionId, clientInfo);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001400
Paul Hud44e1b72023-06-16 02:07:42 +00001401 final int transactionId2 = getUniqueId();
1402 if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
1403 storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
Paul Hua6bc4632023-06-26 01:18:29 +00001404 NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001405 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001406 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001407 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1408 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001409 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001410 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001411 }
1412 break;
paulhu2b9ed952022-02-10 21:58:32 +08001413 }
1414 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001415 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001416 stopResolveService(transactionId);
1417 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001418 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001419 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1420 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001421 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001422 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001423 break;
paulhu2b9ed952022-02-10 21:58:32 +08001424 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001425 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001426 stopGetAddrInfo(transactionId);
1427 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001428 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001429 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1430 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001431 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001432 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001433 break;
paulhu2b9ed952022-02-10 21:58:32 +08001434 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001435 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +08001436 final GetAddressInfo info = (GetAddressInfo) obj;
1437 final String address = info.address;
1438 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001439 InetAddress serviceHost = null;
1440 try {
paulhu2b9ed952022-02-10 21:58:32 +08001441 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001442 } catch (UnknownHostException e) {
1443 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
1444 }
1445
1446 // If the resolved service is on an interface without a network, consider it
1447 // as a failure: it would not be usable by apps as they would need
1448 // privileged permissions.
Paul Hu30bd70d2023-02-07 13:20:56 +00001449 if (netId != NETID_UNSET && serviceHost != null) {
1450 clientInfo.mResolvedService.setHost(serviceHost);
1451 setServiceNetworkForCallback(clientInfo.mResolvedService,
1452 netId, info.interfaceIdx);
1453 clientInfo.onResolveServiceSucceeded(
Paul Hua6bc4632023-06-26 01:18:29 +00001454 clientRequestId, clientInfo.mResolvedService, request);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001455 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001456 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001457 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1458 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001459 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001460 }
Paul Hud44e1b72023-06-16 02:07:42 +00001461 stopGetAddrInfo(transactionId);
1462 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001463 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001464 break;
paulhu2b9ed952022-02-10 21:58:32 +08001465 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001466 default:
Hugo Benichif0c84092017-04-05 14:43:29 +09001467 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001468 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001469 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001470 }
Paul Hu019621e2023-01-13 23:26:49 +08001471
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001472 @Nullable
1473 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
1474 final MdnsEvent event, int code) {
Paul Hu019621e2023-01-13 23:26:49 +08001475 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001476 final String[] typeArray = serviceInfo.getServiceType();
1477 final String joinedType;
1478 if (typeArray.length == 0
1479 || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
1480 Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
1481 + Arrays.toString(typeArray));
1482 return null;
1483 } else {
1484 joinedType = TextUtils.join(".",
1485 Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
1486 }
1487 final String serviceType;
1488 switch (code) {
1489 case NsdManager.SERVICE_FOUND:
1490 case NsdManager.SERVICE_LOST:
1491 // For consistency with historical behavior, discovered service types have
1492 // a dot at the end.
1493 serviceType = joinedType + ".";
1494 break;
1495 case RESOLVE_SERVICE_SUCCEEDED:
1496 // For consistency with historical behavior, resolved service types have
1497 // a dot at the beginning.
1498 serviceType = "." + joinedType;
1499 break;
1500 default:
1501 serviceType = joinedType;
1502 break;
1503 }
Paul Hu019621e2023-01-13 23:26:49 +08001504 final String serviceName = serviceInfo.getServiceInstanceName();
1505 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
1506 final Network network = serviceInfo.getNetwork();
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001507 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
1508 // network for Tethering interface. In other words, the network == null means the
1509 // network has netId = INetd.LOCAL_NET_ID.
Paul Hu019621e2023-01-13 23:26:49 +08001510 setServiceNetworkForCallback(
1511 servInfo,
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001512 network == null ? INetd.LOCAL_NET_ID : network.netId,
Paul Hu019621e2023-01-13 23:26:49 +08001513 serviceInfo.getInterfaceIndex());
Kangping Dong5af24b62023-12-10 21:41:16 +08001514 servInfo.setSubtypes(dedupSubtypeLabels(serviceInfo.getSubtypes()));
Paul Hu019621e2023-01-13 23:26:49 +08001515 return servInfo;
1516 }
1517
1518 private boolean handleMdnsDiscoveryManagerEvent(
1519 int transactionId, int code, Object obj) {
Paul Hud44e1b72023-06-16 02:07:42 +00001520 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Paul Hu019621e2023-01-13 23:26:49 +08001521 if (clientInfo == null) {
1522 Log.e(TAG, String.format(
1523 "id %d for %d has no client mapping", transactionId, code));
1524 return false;
1525 }
1526
1527 final MdnsEvent event = (MdnsEvent) obj;
Paul Hud44e1b72023-06-16 02:07:42 +00001528 final int clientRequestId = event.mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +08001529 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1530 if (request == null) {
1531 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1532 return false;
1533 }
1534
1535 // Deal with the discovery sent callback
1536 if (code == DISCOVERY_QUERY_SENT_CALLBACK) {
1537 request.onQuerySent();
1538 return true;
1539 }
1540
1541 // Deal with other callbacks.
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001542 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
1543 // Errors are already logged if null
1544 if (info == null) return false;
Paul Hu83ec7f42023-06-07 18:04:09 +08001545 mServiceLogs.log(String.format(
1546 "MdnsDiscoveryManager event code=%s transactionId=%d",
1547 NsdManager.nameOf(code), transactionId));
Paul Hu019621e2023-01-13 23:26:49 +08001548 switch (code) {
1549 case NsdManager.SERVICE_FOUND:
Paul Hu812e9212023-06-20 06:24:53 +00001550 clientInfo.onServiceFound(clientRequestId, info, request);
Paul Hu319751a2023-01-13 23:56:34 +08001551 break;
1552 case NsdManager.SERVICE_LOST:
Paul Hu812e9212023-06-20 06:24:53 +00001553 clientInfo.onServiceLost(clientRequestId, info, request);
Paul Hu019621e2023-01-13 23:26:49 +08001554 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001555 case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
1556 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Paul Hu75069ed2023-01-14 00:31:09 +08001557 info.setPort(serviceInfo.getPort());
1558
1559 Map<String, String> attrs = serviceInfo.getAttributes();
1560 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1561 final String key = kv.getKey();
1562 try {
1563 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1564 } catch (IllegalArgumentException e) {
1565 Log.e(TAG, "Invalid attribute", e);
1566 }
1567 }
Handa Wang096e32e2024-01-14 08:21:28 +00001568 info.setHostname(getHostname(serviceInfo));
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001569 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu2b865912023-03-06 14:27:53 +08001570 if (addresses.size() != 0) {
1571 info.setHostAddresses(addresses);
Paul Hua6bc4632023-06-26 01:18:29 +00001572 request.setServiceFromCache(event.mIsServiceFromCache);
1573 clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
Paul Hu2b865912023-03-06 14:27:53 +08001574 } else {
1575 // No address. Notify resolution failure.
Paul Hua6bc4632023-06-26 01:18:29 +00001576 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001577 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */,
1578 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001579 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu75069ed2023-01-14 00:31:09 +08001580 }
1581
1582 // Unregister the listener immediately like IMDnsEventListener design
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001583 if (!(request instanceof DiscoveryManagerRequest)) {
1584 Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event");
1585 break;
1586 }
Paul Hud44e1b72023-06-16 02:07:42 +00001587 stopDiscoveryManagerRequest(
1588 request, clientRequestId, transactionId, clientInfo);
Paul Hu75069ed2023-01-14 00:31:09 +08001589 break;
1590 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001591 case NsdManager.SERVICE_UPDATED: {
1592 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1593 info.setPort(serviceInfo.getPort());
1594
1595 Map<String, String> attrs = serviceInfo.getAttributes();
1596 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1597 final String key = kv.getKey();
1598 try {
1599 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1600 } catch (IllegalArgumentException e) {
1601 Log.e(TAG, "Invalid attribute", e);
1602 }
1603 }
1604
Handa Wang096e32e2024-01-14 08:21:28 +00001605 info.setHostname(getHostname(serviceInfo));
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001606 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001607 info.setHostAddresses(addresses);
Paul Huddce5912023-08-01 10:26:49 +08001608 clientInfo.onServiceUpdated(clientRequestId, info, request);
1609 // Set the ServiceFromCache flag only if the service is actually being
1610 // retrieved from the cache. This flag should not be overridden by later
1611 // service updates, which may not be cached.
1612 if (event.mIsServiceFromCache) {
1613 request.setServiceFromCache(true);
1614 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001615 break;
1616 }
1617 case NsdManager.SERVICE_UPDATED_LOST:
Paul Huddce5912023-08-01 10:26:49 +08001618 clientInfo.onServiceUpdatedLost(clientRequestId, request);
Paul Hu30bd70d2023-02-07 13:20:56 +00001619 break;
Paul Hu019621e2023-01-13 23:26:49 +08001620 default:
1621 return false;
1622 }
1623 return true;
1624 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001625 }
1626 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001627
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001628 @NonNull
1629 private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
1630 final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
1631 final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
1632 final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
1633 for (String ipv4Address : v4Addrs) {
1634 try {
1635 addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
1636 } catch (IllegalArgumentException e) {
1637 Log.wtf(TAG, "Invalid ipv4 address", e);
1638 }
1639 }
1640 for (String ipv6Address : v6Addrs) {
1641 try {
Yuyang Huanga6a6ff92023-04-24 13:33:34 +09001642 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
1643 ipv6Address);
1644 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
1645 } catch (IllegalArgumentException e) {
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001646 Log.wtf(TAG, "Invalid ipv6 address", e);
1647 }
1648 }
1649 return addresses;
1650 }
1651
Handa Wang096e32e2024-01-14 08:21:28 +00001652 @NonNull
1653 private static String getHostname(@NonNull MdnsServiceInfo serviceInfo) {
1654 String[] hostname = serviceInfo.getHostName();
1655 // Strip the "local" top-level domain.
1656 if (hostname.length >= 2 && hostname[hostname.length - 1].equals("local")) {
1657 hostname = Arrays.copyOf(hostname, hostname.length - 1);
1658 }
1659 return String.join(".", hostname);
1660 }
1661
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001662 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
1663 switch (netId) {
1664 case NETID_UNSET:
1665 info.setNetwork(null);
1666 break;
1667 case INetd.LOCAL_NET_ID:
1668 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
1669 // visible / usable for apps, so do not return it. Store the interface
1670 // index instead, so at least if the client tries to resolve the service
1671 // with that NsdServiceInfo, it will be done on the same interface.
1672 // If they recreate the NsdServiceInfo themselves, resolution would be
1673 // done on all interfaces as before T, which should also work.
1674 info.setNetwork(null);
1675 info.setInterfaceIndex(ifaceIdx);
1676 break;
1677 default:
1678 info.setNetwork(new Network(netId));
1679 }
1680 }
1681
paulhube186602022-04-12 07:18:23 +00001682 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
1683 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
1684 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
1685 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
1686 private String unescape(String s) {
1687 StringBuilder sb = new StringBuilder(s.length());
1688 for (int i = 0; i < s.length(); ++i) {
1689 char c = s.charAt(i);
1690 if (c == '\\') {
1691 if (++i >= s.length()) {
1692 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1693 break;
1694 }
1695 c = s.charAt(i);
1696 if (c != '.' && c != '\\') {
1697 if (i + 2 >= s.length()) {
1698 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1699 break;
1700 }
1701 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
1702 + (s.charAt(i + 2) - '0'));
1703 i += 2;
1704 }
1705 }
1706 sb.append(c);
1707 }
1708 return sb.toString();
1709 }
1710
Paul Hu7445e3d2023-03-03 15:14:00 +08001711 /**
1712 * Check the given service type is valid and construct it to a service type
1713 * which can use for discovery / resolution service.
1714 *
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001715 * <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 +08001716 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
1717 * underscore; they are alphanumerical characters or dashes or underscore, except the
1718 * last one that is just alphanumerical. The last label must be _tcp or _udp.
1719 *
Yuyang Huang170d42f2023-12-09 15:26:16 +09001720 * <p>The subtypes may also be specified with a comma after the service type, for example
1721 * _type._tcp,_subtype1,_subtype2
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001722 *
Paul Hu7445e3d2023-03-03 15:14:00 +08001723 * @param serviceType the request service type for discovery / resolution service
1724 * @return constructed service type or null if the given service type is invalid.
1725 */
1726 @Nullable
Yuyang Huang170d42f2023-12-09 15:26:16 +09001727 public static Pair<String, List<String>> parseTypeAndSubtype(String serviceType) {
Paul Hu7445e3d2023-03-03 15:14:00 +08001728 if (TextUtils.isEmpty(serviceType)) return null;
Yuyang Huang86d083f2023-12-12 19:56:41 +09001729 final Pattern serviceTypePattern = Pattern.compile(TYPE_REGEX);
Paul Hu7445e3d2023-03-03 15:14:00 +08001730 final Matcher matcher = serviceTypePattern.matcher(serviceType);
1731 if (!matcher.matches()) return null;
Yuyang Huang170d42f2023-12-09 15:26:16 +09001732 final String queryType = matcher.group(2);
1733 // Use the subtype at the beginning
1734 if (matcher.group(1) != null) {
1735 return new Pair<>(queryType, List.of(matcher.group(1)));
1736 }
1737 // Use the subtypes at the end
1738 final String subTypesStr = matcher.group(3);
1739 if (subTypesStr != null && !subTypesStr.isEmpty()) {
1740 final String[] subTypes = subTypesStr.substring(1).split(",");
1741 return new Pair<>(queryType, List.of(subTypes));
1742 }
1743
1744 return new Pair<>(queryType, Collections.emptyList());
Paul Hu7445e3d2023-03-03 15:14:00 +08001745 }
1746
Handa Wang096e32e2024-01-14 08:21:28 +00001747 /**
1748 * Checks if the hostname is valid.
1749 *
1750 * <p>For now NsdService only allows single-label hostnames conforming to RFC 1035. In other
1751 * words, the hostname should be at most 63 characters long and it only contains letters, digits
1752 * and hyphens.
1753 */
1754 public static boolean checkHostname(@Nullable String hostname) {
1755 if (hostname == null) {
1756 return true;
1757 }
1758 String HOSTNAME_REGEX = "^[a-zA-Z]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$";
1759 return Pattern.compile(HOSTNAME_REGEX).matcher(hostname).matches();
1760 }
1761
Kangping Dong5af24b62023-12-10 21:41:16 +08001762 /** Returns {@code true} if {@code subtype} is a valid DNS-SD subtype label. */
1763 private static boolean checkSubtypeLabel(String subtype) {
Remi NGUYEN VAN629234c2024-02-08 18:27:45 +09001764 return Pattern.compile("^" + SUBTYPE_LABEL_REGEX + "$").matcher(subtype).matches();
Kangping Dong5af24b62023-12-10 21:41:16 +08001765 }
1766
Hugo Benichi803a2f02017-04-24 11:35:06 +09001767 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +08001768 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Paul Hu4bd98ef2023-01-12 13:42:07 +08001769 this(ctx, handler, cleanupDelayMs, new Dependencies());
1770 }
1771
1772 @VisibleForTesting
1773 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
Luke Huang05298582021-06-13 16:52:05 +00001774 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001775 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001776 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -07001777 mNsdStateMachine.start();
Ken Chen80c9f6f2023-11-15 18:24:54 +08001778 // It can fail on V+ device since mdns native service provided by netd is removed.
1779 mMDnsManager = SdkLevel.isAtLeastV() ? null : ctx.getSystemService(MDnsManager.class);
paulhu2b9ed952022-02-10 21:58:32 +08001780 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001781 mDeps = deps;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001782
Paul Hu14667de2023-04-17 22:42:47 +08001783 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001784 LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor());
Yuyang Huang700778b2023-03-08 16:17:05 +09001785 // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
Yuyang Huangfca402a2023-05-24 14:45:59 +09001786 // address events are received. When the netlink monitor starts, any IP addresses already
1787 // on the interfaces will not be seen. In practice, the network will not connect at boot
1788 // time As a result, all the netlink message should be observed if the netlink monitor
1789 // starts here.
Yuyang Huang700778b2023-03-08 16:17:05 +09001790 handler.post(mMdnsSocketProvider::startNetLinkMonitor);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001791
1792 // NsdService is started after ActivityManager (startOtherServices in SystemServer, vs.
1793 // startBootstrapServices).
1794 mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt(
1795 MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF,
1796 DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF);
1797 final ActivityManager am = ctx.getSystemService(ActivityManager.class);
1798 am.addOnUidImportanceListener(new UidImportanceListener(handler),
1799 mRunningAppActiveImportanceCutoff);
1800
Paul Hu11a883d2023-12-14 07:36:44 +00001801 mMdnsFeatureFlags = new MdnsFeatureFlags.Builder()
Paul Huf3fe3332023-10-16 17:13:25 +08001802 .setIsMdnsOffloadFeatureEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1803 mContext, MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD))
1804 .setIncludeInetAddressRecordsInProbing(mDeps.isFeatureEnabled(
1805 mContext, MdnsFeatureFlags.INCLUDE_INET_ADDRESS_RECORDS_IN_PROBING))
Paul Hu596a5002023-10-18 17:07:31 +08001806 .setIsExpiredServicesRemovalEnabled(mDeps.isFeatureEnabled(
1807 mContext, MdnsFeatureFlags.NSD_EXPIRED_SERVICES_REMOVAL))
Paul Hufd357ef2023-11-01 16:32:45 +08001808 .setIsLabelCountLimitEnabled(mDeps.isTetheringFeatureNotChickenedOut(
1809 mContext, MdnsFeatureFlags.NSD_LIMIT_LABEL_COUNT))
Paul Hu01f243f2023-11-22 17:26:36 +08001810 .setIsKnownAnswerSuppressionEnabled(mDeps.isFeatureEnabled(
1811 mContext, MdnsFeatureFlags.NSD_KNOWN_ANSWER_SUPPRESSION))
Remi NGUYEN VAN0ca094b2023-09-13 16:27:12 +09001812 .setIsUnicastReplyEnabled(mDeps.isFeatureEnabled(
1813 mContext, MdnsFeatureFlags.NSD_UNICAST_REPLY_ENABLED))
Paul Hu11a883d2023-12-14 07:36:44 +00001814 .setIsAggressiveQueryModeEnabled(mDeps.isFeatureEnabled(
1815 mContext, MdnsFeatureFlags.NSD_AGGRESSIVE_QUERY_MODE))
Remi NGUYEN VAN0ca094b2023-09-13 16:27:12 +09001816 .setOverrideProvider(flag -> mDeps.isFeatureEnabled(
1817 mContext, FORCE_ENABLE_FLAG_FOR_TEST_PREFIX + flag))
Paul Huf3fe3332023-10-16 17:13:25 +08001818 .build();
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001819 mMdnsSocketClient =
Yuyang Huang7ddf2932023-08-01 18:16:30 +09001820 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
Paul Hu11a883d2023-12-14 07:36:44 +00001821 LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), mMdnsFeatureFlags);
Paul Hu14667de2023-04-17 22:42:47 +08001822 mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
Paul Hu11a883d2023-12-14 07:36:44 +00001823 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"),
1824 mMdnsFeatureFlags);
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001825 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
1826 mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
Paul Hu11a883d2023-12-14 07:36:44 +00001827 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"),
1828 mMdnsFeatureFlags, mContext);
Paul Hu777ed052023-06-19 13:35:15 +00001829 mClock = deps.makeClock();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001830 }
1831
1832 /**
1833 * Dependencies of NsdService, for injection in tests.
1834 */
1835 @VisibleForTesting
1836 public static class Dependencies {
1837 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001838 * Check whether the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001839 *
1840 * @param context The global context information about an app environment.
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001841 * @return true if the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001842 */
1843 public boolean isMdnsDiscoveryManagerEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001844 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001845 MDNS_DISCOVERY_MANAGER_VERSION);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001846 }
1847
1848 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001849 * Check whether the MdnsAdvertiser feature is enabled.
1850 *
1851 * @param context The global context information about an app environment.
1852 * @return true if the MdnsAdvertiser feature is enabled.
1853 */
1854 public boolean isMdnsAdvertiserEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001855 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001856 MDNS_ADVERTISER_VERSION);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001857 }
1858
1859 /**
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001860 * Get the type allowlist flag value.
1861 * @see #MDNS_TYPE_ALLOWLIST_FLAGS
1862 */
1863 @Nullable
1864 public String getTypeAllowlistFlags() {
1865 return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
1866 MDNS_TYPE_ALLOWLIST_FLAGS, null);
1867 }
1868
1869 /**
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001870 * @see DeviceConfigUtils#isTetheringFeatureEnabled
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001871 */
1872 public boolean isFeatureEnabled(Context context, String feature) {
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001873 return DeviceConfigUtils.isTetheringFeatureEnabled(context, feature);
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001874 }
1875
1876 /**
Yuyang Huangb96a0712023-09-07 15:13:15 +09001877 * @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut
1878 */
Motomu Utsumied4e7ec2023-09-13 14:58:32 +09001879 public boolean isTetheringFeatureNotChickenedOut(Context context, String feature) {
1880 return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, feature);
Yuyang Huangb96a0712023-09-07 15:13:15 +09001881 }
1882
1883 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001884 * @see MdnsDiscoveryManager
1885 */
1886 public MdnsDiscoveryManager makeMdnsDiscoveryManager(
Paul Hu14667de2023-04-17 22:42:47 +08001887 @NonNull ExecutorProvider executorProvider,
Paul Huf3fe3332023-10-16 17:13:25 +08001888 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog,
1889 @NonNull MdnsFeatureFlags featureFlags) {
1890 return new MdnsDiscoveryManager(
1891 executorProvider, socketClient, sharedLog, featureFlags);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001892 }
1893
1894 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001895 * @see MdnsAdvertiser
1896 */
1897 public MdnsAdvertiser makeMdnsAdvertiser(
1898 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
Yuyang Huangb96a0712023-09-07 15:13:15 +09001899 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog,
Remi NGUYEN VAN5c9d6cb2024-01-23 17:14:55 +09001900 MdnsFeatureFlags featureFlags, Context context) {
1901 return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog, featureFlags, context);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001902 }
1903
1904 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001905 * @see MdnsSocketProvider
1906 */
Paul Hu14667de2023-04-17 22:42:47 +08001907 public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001908 @NonNull Looper looper, @NonNull SharedLog sharedLog,
1909 @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) {
1910 return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback);
1911 }
1912
1913 /**
1914 * @see DeviceConfig#getInt(String, String, int)
1915 */
1916 public int getDeviceConfigInt(@NonNull String config, int defaultValue) {
1917 return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue);
1918 }
1919
1920 /**
1921 * @see Binder#getCallingUid()
1922 */
1923 public int getCallingUid() {
1924 return Binder.getCallingUid();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001925 }
Paul Hu777ed052023-06-19 13:35:15 +00001926
1927 /**
1928 * @see NetworkNsdReportedMetrics
1929 */
Paul Hu508a0122023-09-11 15:31:33 +08001930 public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(int clientId) {
1931 return new NetworkNsdReportedMetrics(clientId);
Paul Hu777ed052023-06-19 13:35:15 +00001932 }
1933
1934 /**
1935 * @see MdnsUtils.Clock
1936 */
1937 public Clock makeClock() {
1938 return new Clock();
1939 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001940 }
1941
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001942 /**
1943 * Return whether a type is allowlisted to use the Java backend.
1944 * @param type The service type
1945 * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
1946 * {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
1947 */
1948 private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
1949 @NonNull String flagPrefix) {
1950 if (type == null) return false;
1951 final String typesConfig = mDeps.getTypeAllowlistFlags();
1952 if (TextUtils.isEmpty(typesConfig)) return false;
1953
1954 final String mappingPrefix = type + ":";
1955 String mappedFlag = null;
1956 for (String mapping : TextUtils.split(typesConfig, ",")) {
1957 if (mapping.startsWith(mappingPrefix)) {
1958 mappedFlag = mapping.substring(mappingPrefix.length());
1959 break;
1960 }
1961 }
1962
1963 if (mappedFlag == null) return false;
1964
1965 return mDeps.isFeatureEnabled(mContext,
1966 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
1967 }
1968
1969 private boolean useDiscoveryManagerForType(@Nullable String type) {
1970 return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
1971 }
1972
1973 private boolean useAdvertiserForType(@Nullable String type) {
1974 return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
1975 }
1976
paulhu1b35e822022-04-08 14:48:41 +08001977 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09001978 HandlerThread thread = new HandlerThread(TAG);
1979 thread.start();
1980 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +08001981 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001982 return service;
1983 }
1984
paulhu2b9ed952022-02-10 21:58:32 +08001985 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
1986 private final StateMachine mStateMachine;
1987
1988 MDnsEventCallback(StateMachine sm) {
1989 mStateMachine = sm;
1990 }
1991
1992 @Override
1993 public void onServiceRegistrationStatus(final RegistrationInfo status) {
1994 mStateMachine.sendMessage(
1995 MDNS_SERVICE_EVENT, status.result, status.id, status);
1996 }
1997
1998 @Override
1999 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
2000 mStateMachine.sendMessage(
2001 MDNS_SERVICE_EVENT, status.result, status.id, status);
2002 }
2003
2004 @Override
2005 public void onServiceResolutionStatus(final ResolutionInfo status) {
2006 mStateMachine.sendMessage(
2007 MDNS_SERVICE_EVENT, status.result, status.id, status);
2008 }
2009
2010 @Override
2011 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
2012 mStateMachine.sendMessage(
2013 MDNS_SERVICE_EVENT, status.result, status.id, status);
2014 }
2015
2016 @Override
2017 public int getInterfaceVersion() throws RemoteException {
2018 return this.VERSION;
2019 }
2020
2021 @Override
2022 public String getInterfaceHash() throws RemoteException {
2023 return this.HASH;
2024 }
2025 }
2026
Yuyang Huangc275a9e2023-08-25 18:03:22 +09002027 private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) {
2028 final String targetInterface = offloadEngineInfo.mInterfaceName;
2029 final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine;
2030 final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers =
2031 mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface);
2032 for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) {
2033 try {
2034 offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo);
2035 } catch (RemoteException e) {
2036 // Can happen in regular cases, do not log a stacktrace
2037 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
2038 }
2039 }
2040 }
2041
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002042 private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
2043 @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
2044 final int count = mOffloadEngines.beginBroadcast();
2045 try {
2046 for (int i = 0; i < count; i++) {
2047 final OffloadEngineInfo offloadEngineInfo =
2048 (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
2049 final String interfaceName = offloadEngineInfo.mInterfaceName;
2050 if (!targetInterfaceName.equals(interfaceName)
2051 || ((offloadEngineInfo.mOffloadType
2052 & offloadServiceInfo.getOffloadType()) == 0)) {
2053 continue;
2054 }
2055 try {
2056 if (isRemove) {
2057 mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
2058 offloadServiceInfo);
2059 } else {
2060 mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
2061 offloadServiceInfo);
2062 }
2063 } catch (RemoteException e) {
2064 // Can happen in regular cases, do not log a stacktrace
Yuyang Huangc275a9e2023-08-25 18:03:22 +09002065 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002066 }
2067 }
2068 } finally {
2069 mOffloadEngines.finishBroadcast();
2070 }
2071 }
2072
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002073 private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002074 // TODO: add a callback to notify when a service is being added on each interface (as soon
2075 // as probing starts), and call mOffloadCallbacks. This callback is for
2076 // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
2077
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002078 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00002079 public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
2080 mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
2081 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002082 if (clientInfo == null) return;
2083
Paul Hud44e1b72023-06-16 02:07:42 +00002084 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
2085 if (clientRequestId < 0) return;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002086
Handa Wang096e32e2024-01-14 08:21:28 +00002087 // onRegisterServiceSucceeded only has the service name and hostname in its info. This
2088 // aligns with historical behavior.
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002089 final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
Handa Wang096e32e2024-01-14 08:21:28 +00002090 cbInfo.setHostname(registeredInfo.getHostname());
Paul Hu777ed052023-06-19 13:35:15 +00002091 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu508a0122023-09-11 15:31:33 +08002092 clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, request);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002093 }
2094
2095 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00002096 public void onRegisterServiceFailed(int transactionId, int errorCode) {
2097 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002098 if (clientInfo == null) return;
2099
Paul Hud44e1b72023-06-16 02:07:42 +00002100 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
2101 if (clientRequestId < 0) return;
Paul Hu777ed052023-06-19 13:35:15 +00002102 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu508a0122023-09-11 15:31:33 +08002103 clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, false /* isLegacy */,
2104 transactionId, request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002105 }
2106
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002107 @Override
2108 public void onOffloadStartOrUpdate(@NonNull String interfaceName,
2109 @NonNull OffloadServiceInfo offloadServiceInfo) {
2110 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
2111 }
2112
2113 @Override
2114 public void onOffloadStop(@NonNull String interfaceName,
2115 @NonNull OffloadServiceInfo offloadServiceInfo) {
2116 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
2117 }
2118
Paul Hud44e1b72023-06-16 02:07:42 +00002119 private ClientInfo getClientInfoOrLog(int transactionId) {
2120 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002121 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00002122 Log.e(TAG, String.format("Callback for service %d has no client", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002123 }
2124 return clientInfo;
2125 }
2126
Paul Hud44e1b72023-06-16 02:07:42 +00002127 private int getClientRequestIdOrLog(@NonNull ClientInfo info, int transactionId) {
2128 final int clientRequestId = info.getClientRequestId(transactionId);
2129 if (clientRequestId < 0) {
2130 Log.e(TAG, String.format(
2131 "Client request ID not found for service %d", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002132 }
Paul Hud44e1b72023-06-16 02:07:42 +00002133 return clientRequestId;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002134 }
2135 }
2136
Paul Hu2e0a88c2023-03-09 16:05:01 +08002137 private static class ConnectorArgs {
2138 @NonNull public final NsdServiceConnector connector;
2139 @NonNull public final INsdManagerCallback callback;
2140 public final boolean useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002141 public final int uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002142
2143 ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
Paul Hub2e67d32023-04-18 05:50:14 +00002144 boolean useJavaBackend, int uid) {
Paul Hu2e0a88c2023-03-09 16:05:01 +08002145 this.connector = connector;
2146 this.callback = callback;
2147 this.useJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002148 this.uid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002149 }
2150 }
2151
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002152 @Override
Paul Hu2e0a88c2023-03-09 16:05:01 +08002153 public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09002154 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Paul Hu101dbf52023-08-09 16:05:20 +08002155 final int uid = mDeps.getCallingUid();
2156 if (cb == null) {
2157 throw new IllegalArgumentException("Unknown client callback from uid=" + uid);
2158 }
Paul Hu2e0a88c2023-03-09 16:05:01 +08002159 if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002160 final INsdServiceConnector connector = new NsdServiceConnector();
Paul Hub2e67d32023-04-18 05:50:14 +00002161 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
Paul Hu101dbf52023-08-09 16:05:20 +08002162 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002163 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -07002164 }
2165
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002166 private static class ListenerArgs {
2167 public final NsdServiceConnector connector;
2168 public final NsdServiceInfo serviceInfo;
2169 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
2170 this.connector = connector;
2171 this.serviceInfo = serviceInfo;
2172 }
2173 }
2174
Yuyang Huang86d083f2023-12-12 19:56:41 +09002175 private static class AdvertisingArgs {
2176 public final NsdServiceConnector connector;
2177 public final AdvertisingRequest advertisingRequest;
2178
2179 AdvertisingArgs(NsdServiceConnector connector, AdvertisingRequest advertisingRequest) {
2180 this.connector = connector;
2181 this.advertisingRequest = advertisingRequest;
2182 }
2183 }
2184
Kangping Dong1f1a3792023-12-10 22:05:04 +08002185 private static final class DiscoveryArgs {
2186 public final NsdServiceConnector connector;
2187 public final DiscoveryRequest discoveryRequest;
2188 DiscoveryArgs(NsdServiceConnector connector, DiscoveryRequest discoveryRequest) {
2189 this.connector = connector;
2190 this.discoveryRequest = discoveryRequest;
2191 }
2192 }
2193
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002194 private class NsdServiceConnector extends INsdServiceConnector.Stub
2195 implements IBinder.DeathRecipient {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002196
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002197 @Override
Yuyang Huang86d083f2023-12-12 19:56:41 +09002198 public void registerService(int listenerKey, AdvertisingRequest advertisingRequest)
2199 throws RemoteException {
Handa Wang096e32e2024-01-14 08:21:28 +00002200 NsdManager.checkServiceInfoForRegistration(advertisingRequest.getServiceInfo());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002201 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2202 NsdManager.REGISTER_SERVICE, 0, listenerKey,
Yuyang Huang86d083f2023-12-12 19:56:41 +09002203 new AdvertisingArgs(this, advertisingRequest)
2204 ));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002205 }
2206
2207 @Override
2208 public void unregisterService(int listenerKey) {
2209 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2210 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
Yuyang Huang86d083f2023-12-12 19:56:41 +09002211 new ListenerArgs(this, (NsdServiceInfo) null)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002212 }
2213
2214 @Override
Kangping Dong1f1a3792023-12-10 22:05:04 +08002215 public void discoverServices(int listenerKey, DiscoveryRequest discoveryRequest) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002216 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2217 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
Kangping Dong1f1a3792023-12-10 22:05:04 +08002218 new DiscoveryArgs(this, discoveryRequest)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002219 }
2220
2221 @Override
2222 public void stopDiscovery(int listenerKey) {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002223 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_DISCOVERY,
2224 0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002225 }
2226
2227 @Override
2228 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
2229 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2230 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
2231 new ListenerArgs(this, serviceInfo)));
2232 }
2233
2234 @Override
Paul Hub58deb72022-12-26 09:24:42 +00002235 public void stopResolution(int listenerKey) {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002236 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.STOP_RESOLUTION,
2237 0, listenerKey, new ListenerArgs(this, (NsdServiceInfo) null)));
Paul Hub58deb72022-12-26 09:24:42 +00002238 }
2239
2240 @Override
Paul Hu18aeccc2022-12-27 08:48:48 +00002241 public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
2242 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2243 NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
2244 new ListenerArgs(this, serviceInfo)));
2245 }
2246
2247 @Override
2248 public void unregisterServiceInfoCallback(int listenerKey) {
2249 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2250 NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
Yuyang Huang86d083f2023-12-12 19:56:41 +09002251 new ListenerArgs(this, (NsdServiceInfo) null)));
Paul Hu18aeccc2022-12-27 08:48:48 +00002252 }
2253
2254 @Override
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002255 public void startDaemon() {
Yuyang Huang86d083f2023-12-12 19:56:41 +09002256 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.DAEMON_STARTUP,
2257 new ListenerArgs(this, (NsdServiceInfo) null)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002258 }
2259
2260 @Override
2261 public void binderDied() {
2262 mNsdStateMachine.sendMessage(
2263 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002264
2265 }
2266
2267 @Override
2268 public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
2269 @OffloadEngine.OffloadCapability long offloadCapabilities,
2270 @OffloadEngine.OffloadType long offloadTypes) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002271 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002272 Objects.requireNonNull(ifaceName);
2273 Objects.requireNonNull(cb);
2274 mNsdStateMachine.sendMessage(
2275 mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
2276 new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
2277 offloadTypes)));
2278 }
2279
2280 @Override
2281 public void unregisterOffloadEngine(IOffloadEngine cb) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002282 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002283 Objects.requireNonNull(cb);
2284 mNsdStateMachine.sendMessage(
2285 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002286 }
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002287
2288 private static void checkOffloadEnginePermission(Context context) {
2289 if (!SdkLevel.isAtLeastT()) {
2290 throw new SecurityException("API is not available in before API level 33");
2291 }
Yuyang Huangd5896e72023-11-28 13:23:59 +09002292
Ken Chena7bae552023-12-27 11:40:57 +08002293 final ArrayList<String> permissionsList = new ArrayList<>(Arrays.asList(NETWORK_STACK,
2294 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS));
2295
2296 if (SdkLevel.isAtLeastV()) {
2297 // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V.
2298 permissionsList.add(REGISTER_NSD_OFFLOAD_ENGINE);
2299 } else if (SdkLevel.isAtLeastU()) {
2300 // REGISTER_NSD_OFFLOAD_ENGINE cannot be backport to U. In U, check the DEVICE_POWER
2301 // permission instead.
2302 permissionsList.add(DEVICE_POWER);
Yuyang Huangd5896e72023-11-28 13:23:59 +09002303 }
2304
Junyu Lai71b51532024-02-01 10:39:01 +08002305 if (PermissionUtils.hasAnyPermissionOf(context,
Ken Chena7bae552023-12-27 11:40:57 +08002306 permissionsList.toArray(new String[0]))) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002307 return;
2308 }
2309 throw new SecurityException("Requires one of the following permissions: "
Ken Chena7bae552023-12-27 11:40:57 +08002310 + String.join(", ", permissionsList) + ".");
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002311 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002312 }
2313
Hugo Benichi912db992017-04-24 16:41:03 +09002314 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -07002315 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -07002316 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +09002317 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
2318 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -07002319 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -07002320 }
2321
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002322 private int getUniqueId() {
2323 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
2324 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002325 }
2326
Paul Hud44e1b72023-06-16 02:07:42 +00002327 private boolean registerService(int transactionId, NsdServiceInfo service) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002328 if (mMDnsManager == null) {
2329 Log.wtf(TAG, "registerService: mMDnsManager is null");
2330 return false;
2331 }
2332
Hugo Benichi6d706442017-04-24 16:19:58 +09002333 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002334 Log.d(TAG, "registerService: " + transactionId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002335 }
Hugo Benichi6d706442017-04-24 16:19:58 +09002336 String name = service.getServiceName();
2337 String type = service.getServiceType();
2338 int port = service.getPort();
2339 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002340 final int registerInterface = getNetworkInterfaceIndex(service);
2341 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +08002342 Log.e(TAG, "Interface to register service on not found");
2343 return false;
2344 }
Paul Hud44e1b72023-06-16 02:07:42 +00002345 return mMDnsManager.registerService(
2346 transactionId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002347 }
2348
Paul Hud44e1b72023-06-16 02:07:42 +00002349 private boolean unregisterService(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002350 if (mMDnsManager == null) {
2351 Log.wtf(TAG, "unregisterService: mMDnsManager is null");
2352 return false;
2353 }
Paul Hud44e1b72023-06-16 02:07:42 +00002354 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002355 }
2356
Kangping Dong1f1a3792023-12-10 22:05:04 +08002357 private boolean discoverServices(int transactionId, DiscoveryRequest discoveryRequest) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002358 if (mMDnsManager == null) {
2359 Log.wtf(TAG, "discoverServices: mMDnsManager is null");
2360 return false;
2361 }
2362
Kangping Dong1f1a3792023-12-10 22:05:04 +08002363 final String type = discoveryRequest.getServiceType();
2364 final int discoverInterface = getNetworkInterfaceIndex(discoveryRequest);
2365 if (discoveryRequest.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002366 Log.e(TAG, "Interface to discover service on not found");
2367 return false;
2368 }
Paul Hud44e1b72023-06-16 02:07:42 +00002369 return mMDnsManager.discover(transactionId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002370 }
2371
Paul Hud44e1b72023-06-16 02:07:42 +00002372 private boolean stopServiceDiscovery(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002373 if (mMDnsManager == null) {
2374 Log.wtf(TAG, "stopServiceDiscovery: mMDnsManager is null");
2375 return false;
2376 }
Paul Hud44e1b72023-06-16 02:07:42 +00002377 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002378 }
2379
Paul Hud44e1b72023-06-16 02:07:42 +00002380 private boolean resolveService(int transactionId, NsdServiceInfo service) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002381 if (mMDnsManager == null) {
2382 Log.wtf(TAG, "resolveService: mMDnsManager is null");
2383 return false;
2384 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002385 final String name = service.getServiceName();
2386 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002387 final int resolveInterface = getNetworkInterfaceIndex(service);
2388 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002389 Log.e(TAG, "Interface to resolve service on not found");
2390 return false;
2391 }
Paul Hud44e1b72023-06-16 02:07:42 +00002392 return mMDnsManager.resolve(transactionId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002393 }
2394
2395 /**
2396 * Guess the interface to use to resolve or discover a service on a specific network.
2397 *
2398 * This is an imperfect guess, as for example the network may be gone or not yet fully
2399 * registered. This is fine as failing is correct if the network is gone, and a client
2400 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
2401 * this is to support the legacy mdnsresponder implementation, which historically resolved
2402 * services on an unspecified network.
2403 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002404 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
2405 final Network network = serviceInfo.getNetwork();
2406 if (network == null) {
2407 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
2408 // provided by NsdService from discovery results, and the service was found on an
2409 // interface that has no app-usable Network).
2410 if (serviceInfo.getInterfaceIndex() != 0) {
2411 return serviceInfo.getInterfaceIndex();
2412 }
2413 return IFACE_IDX_ANY;
2414 }
Kangping Dong1f1a3792023-12-10 22:05:04 +08002415 return getNetworkInterfaceIndex(network);
2416 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002417
Kangping Dong1f1a3792023-12-10 22:05:04 +08002418 /**
2419 * Returns the interface to use to discover a service on a specific network, or {@link
2420 * IFACE_IDX_ANY} if no network is specified.
2421 */
2422 private int getNetworkInterfaceIndex(DiscoveryRequest discoveryRequest) {
2423 final Network network = discoveryRequest.getNetwork();
2424 if (network == null) {
2425 return IFACE_IDX_ANY;
2426 }
2427 return getNetworkInterfaceIndex(network);
2428 }
2429
2430 /**
2431 * Returns the interface of a specific network, or {@link IFACE_IDX_ANY} if no interface is
2432 * associated with {@code network}.
2433 */
2434 private int getNetworkInterfaceIndex(@NonNull Network network) {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002435 String interfaceName = getNetworkInterfaceName(network);
2436 if (interfaceName == null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002437 return IFACE_IDX_ANY;
2438 }
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002439 return getNetworkInterfaceIndexByName(interfaceName);
2440 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002441
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002442 private String getNetworkInterfaceName(@Nullable Network network) {
2443 if (network == null) {
2444 return null;
2445 }
2446 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
2447 if (cm == null) {
2448 Log.wtf(TAG, "No ConnectivityManager");
2449 return null;
2450 }
2451 final LinkProperties lp = cm.getLinkProperties(network);
2452 if (lp == null) {
2453 return null;
2454 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002455 // Only resolve on non-stacked interfaces
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002456 return lp.getInterfaceName();
2457 }
2458
2459 private int getNetworkInterfaceIndexByName(final String ifaceName) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002460 final NetworkInterface iface;
2461 try {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002462 iface = NetworkInterface.getByName(ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002463 } catch (SocketException e) {
2464 Log.e(TAG, "Error querying interface", e);
2465 return IFACE_IDX_ANY;
2466 }
2467
2468 if (iface == null) {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002469 Log.e(TAG, "Interface not found: " + ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002470 return IFACE_IDX_ANY;
2471 }
2472
2473 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002474 }
2475
Paul Hud44e1b72023-06-16 02:07:42 +00002476 private boolean stopResolveService(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002477 if (mMDnsManager == null) {
2478 Log.wtf(TAG, "stopResolveService: mMDnsManager is null");
2479 return false;
2480 }
Paul Hud44e1b72023-06-16 02:07:42 +00002481 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002482 }
2483
Paul Hud44e1b72023-06-16 02:07:42 +00002484 private boolean getAddrInfo(int transactionId, String hostname, int interfaceIdx) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002485 if (mMDnsManager == null) {
2486 Log.wtf(TAG, "getAddrInfo: mMDnsManager is null");
2487 return false;
2488 }
Paul Hud44e1b72023-06-16 02:07:42 +00002489 return mMDnsManager.getServiceAddress(transactionId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002490 }
2491
Paul Hud44e1b72023-06-16 02:07:42 +00002492 private boolean stopGetAddrInfo(int transactionId) {
Ken Chen80c9f6f2023-11-15 18:24:54 +08002493 if (mMDnsManager == null) {
2494 Log.wtf(TAG, "stopGetAddrInfo: mMDnsManager is null");
2495 return false;
2496 }
Paul Hud44e1b72023-06-16 02:07:42 +00002497 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002498 }
2499
2500 @Override
Paul Hub2e67d32023-04-18 05:50:14 +00002501 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Junyu Lai71b51532024-02-01 10:39:01 +08002502 if (!PermissionUtils.hasDumpPermission(mContext, TAG, writer)) return;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002503
Paul Hub2e67d32023-04-18 05:50:14 +00002504 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
2505 // Dump state machine logs
Irfan Sheriff75006652012-04-17 23:15:29 -07002506 mNsdStateMachine.dump(fd, pw, args);
Paul Hub2e67d32023-04-18 05:50:14 +00002507
2508 // Dump service and clients logs
2509 pw.println();
Paul Hu14667de2023-04-17 22:42:47 +08002510 pw.println("Logs:");
Paul Hub2e67d32023-04-18 05:50:14 +00002511 pw.increaseIndent();
2512 mServiceLogs.reverseDump(pw);
2513 pw.decreaseIndent();
Paul Hu55f943e2024-02-20 03:04:17 +00002514
2515 //Dump DiscoveryManager
2516 pw.println();
2517 pw.println("DiscoveryManager:");
2518 pw.increaseIndent();
2519 HandlerUtils.runWithScissorsForDump(
2520 mNsdStateMachine.getHandler(), () -> mMdnsDiscoveryManager.dump(pw), 10_000);
2521 pw.decreaseIndent();
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002522 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002523
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002524 private abstract static class ClientRequest {
Paul Hud44e1b72023-06-16 02:07:42 +00002525 private final int mTransactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002526 private final long mStartTimeMs;
Paul Hu812e9212023-06-20 06:24:53 +00002527 private int mFoundServiceCount = 0;
2528 private int mLostServiceCount = 0;
2529 private final Set<String> mServices = new ArraySet<>();
Paul Hua6bc4632023-06-26 01:18:29 +00002530 private boolean mIsServiceFromCache = false;
Paul Hubad6fe92023-07-24 21:25:22 +08002531 private int mSentQueryCount = NO_SENT_QUERY_COUNT;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002532
Paul Hu812e9212023-06-20 06:24:53 +00002533 private ClientRequest(int transactionId, long startTimeMs) {
Paul Hud44e1b72023-06-16 02:07:42 +00002534 mTransactionId = transactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002535 mStartTimeMs = startTimeMs;
2536 }
2537
Paul Hu812e9212023-06-20 06:24:53 +00002538 public long calculateRequestDurationMs(long stopTimeMs) {
Paul Hu777ed052023-06-19 13:35:15 +00002539 return stopTimeMs - mStartTimeMs;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002540 }
Paul Hu812e9212023-06-20 06:24:53 +00002541
2542 public void onServiceFound(String serviceName) {
2543 mFoundServiceCount++;
2544 if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) {
2545 mServices.add(serviceName);
2546 }
2547 }
2548
2549 public void onServiceLost() {
2550 mLostServiceCount++;
2551 }
2552
2553 public int getFoundServiceCount() {
2554 return mFoundServiceCount;
2555 }
2556
2557 public int getLostServiceCount() {
2558 return mLostServiceCount;
2559 }
2560
2561 public int getServicesCount() {
2562 return mServices.size();
2563 }
Paul Hua6bc4632023-06-26 01:18:29 +00002564
2565 public void setServiceFromCache(boolean isServiceFromCache) {
2566 mIsServiceFromCache = isServiceFromCache;
2567 }
2568
2569 public boolean isServiceFromCache() {
2570 return mIsServiceFromCache;
2571 }
Paul Hubad6fe92023-07-24 21:25:22 +08002572
2573 public void onQuerySent() {
2574 mSentQueryCount++;
2575 }
2576
2577 public int getSentQueryCount() {
2578 return mSentQueryCount;
2579 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002580 }
2581
2582 private static class LegacyClientRequest extends ClientRequest {
2583 private final int mRequestCode;
2584
Paul Hu812e9212023-06-20 06:24:53 +00002585 private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) {
2586 super(transactionId, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002587 mRequestCode = requestCode;
2588 }
2589 }
2590
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002591 private abstract static class JavaBackendClientRequest extends ClientRequest {
2592 @Nullable
2593 private final Network mRequestedNetwork;
2594
Paul Hu777ed052023-06-19 13:35:15 +00002595 private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002596 long startTimeMs) {
2597 super(transactionId, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002598 mRequestedNetwork = requestedNetwork;
2599 }
2600
2601 @Nullable
2602 public Network getRequestedNetwork() {
2603 return mRequestedNetwork;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002604 }
2605 }
2606
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002607 private static class AdvertiserClientRequest extends JavaBackendClientRequest {
Paul Hu777ed052023-06-19 13:35:15 +00002608 private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002609 long startTimeMs) {
2610 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002611 }
2612 }
2613
2614 private static class DiscoveryManagerRequest extends JavaBackendClientRequest {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002615 @NonNull
2616 private final MdnsListener mListener;
2617
Paul Hud44e1b72023-06-16 02:07:42 +00002618 private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
Paul Hu812e9212023-06-20 06:24:53 +00002619 @Nullable Network requestedNetwork, long startTimeMs) {
2620 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002621 mListener = listener;
2622 }
2623 }
2624
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002625 /* Information tracked per client */
2626 private class ClientInfo {
2627
Remi NGUYEN VAN12a0c6a2024-02-09 18:24:29 +09002628 /**
2629 * Maximum number of requests (callbacks) for a client.
2630 *
2631 * 200 listeners should be more than enough for most use-cases: even if a client tries to
2632 * file callbacks for every service on a local network, there are generally much less than
2633 * 200 devices on a local network (a /24 only allows 255 IPv4 devices), and while some
2634 * devices may have multiple services, many devices do not advertise any.
2635 */
2636 private static final int MAX_LIMIT = 200;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002637 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002638 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002639 private NsdServiceInfo mResolvedService;
2640
Paul Hud44e1b72023-06-16 02:07:42 +00002641 /* A map from client request ID (listenerKey) to the request */
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002642 private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>();
Paul Hu23fa2022023-01-13 22:57:24 +08002643
Luke Huangf7277ed2021-07-12 21:15:10 +08002644 // The target SDK of this client < Build.VERSION_CODES.S
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002645 private boolean mIsPreSClient = false;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002646 private final int mUid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002647 // The flag of using java backend if the client's target SDK >= U
2648 private final boolean mUseJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002649 // Store client logs
2650 private final SharedLog mClientLogs;
Paul Hucdef3532023-06-18 14:47:35 +00002651 // Report the nsd metrics data
2652 private final NetworkNsdReportedMetrics mMetrics;
Luke Huangf7277ed2021-07-12 21:15:10 +08002653
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002654 private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
Paul Hucdef3532023-06-18 14:47:35 +00002655 SharedLog sharedLog, NetworkNsdReportedMetrics metrics) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002656 mCb = cb;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002657 mUid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002658 mUseJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002659 mClientLogs = sharedLog;
2660 mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
Paul Hucdef3532023-06-18 14:47:35 +00002661 mMetrics = metrics;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002662 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002663
2664 @Override
2665 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -06002666 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -07002667 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002668 sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002669 sb.append("mUseJavaBackend ").append(mUseJavaBackend).append("\n");
2670 sb.append("mUid ").append(mUid).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002671 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002672 int clientRequestId = mClientRequests.keyAt(i);
2673 sb.append("clientRequestId ")
2674 .append(clientRequestId)
2675 .append(" transactionId ").append(mClientRequests.valueAt(i).mTransactionId)
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002676 .append(" type ").append(
2677 mClientRequests.valueAt(i).getClass().getSimpleName())
2678 .append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -07002679 }
2680 return sb.toString();
2681 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002682
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002683 private boolean isPreSClient() {
2684 return mIsPreSClient;
Luke Huangf7277ed2021-07-12 21:15:10 +08002685 }
2686
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002687 private void setPreSClient() {
2688 mIsPreSClient = true;
Luke Huangf7277ed2021-07-12 21:15:10 +08002689 }
2690
Paul Hu812e9212023-06-20 06:24:53 +00002691 private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) {
Paul Hue4f5f252023-02-16 21:13:47 +08002692 final MdnsListener listener =
2693 ((DiscoveryManagerRequest) request).mListener;
2694 mMdnsDiscoveryManager.unregisterListener(
2695 listener.getListenedServiceType(), listener);
Paul Hu812e9212023-06-20 06:24:53 +00002696 return listener;
Paul Hue4f5f252023-02-16 21:13:47 +08002697 }
2698
Dave Plattfeff2af2014-03-07 14:48:22 -08002699 // Remove any pending requests from the global map when we get rid of a client,
2700 // and send cancellations to the daemon.
2701 private void expungeAllRequests() {
Paul Hub2e67d32023-04-18 05:50:14 +00002702 mClientLogs.log("Client unregistered. expungeAllRequests!");
Hugo Benichid2552ae2017-04-11 14:42:47 +09002703 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002704 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002705 final int clientRequestId = mClientRequests.keyAt(i);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002706 final ClientRequest request = mClientRequests.valueAt(i);
Paul Hud44e1b72023-06-16 02:07:42 +00002707 final int transactionId = request.mTransactionId;
2708 mTransactionIdToClientInfoMap.remove(transactionId);
paulhub2225702021-11-17 09:35:33 +08002709 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002710 Log.d(TAG, "Terminating clientRequestId " + clientRequestId
2711 + " transactionId " + transactionId
2712 + " type " + mClientRequests.get(clientRequestId));
paulhub2225702021-11-17 09:35:33 +08002713 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002714
2715 if (request instanceof DiscoveryManagerRequest) {
Paul Hu812e9212023-06-20 06:24:53 +00002716 final MdnsListener listener = unregisterMdnsListenerFromRequest(request);
2717 if (listener instanceof DiscoveryListener) {
Paul Hu508a0122023-09-11 15:31:33 +08002718 mMetrics.reportServiceDiscoveryStop(false /* isLegacy */, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00002719 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2720 request.getFoundServiceCount(),
2721 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002722 request.getServicesCount(),
2723 request.getSentQueryCount());
Paul Hu60149052023-07-31 14:26:08 +08002724 } else if (listener instanceof ResolutionListener) {
Paul Hu508a0122023-09-11 15:31:33 +08002725 mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
Paul Hu60149052023-07-31 14:26:08 +08002726 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Huddce5912023-08-01 10:26:49 +08002727 } else if (listener instanceof ServiceInfoListener) {
2728 mMetrics.reportServiceInfoCallbackUnregistered(transactionId,
2729 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2730 request.getFoundServiceCount(),
2731 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002732 request.isServiceFromCache(),
2733 request.getSentQueryCount());
Paul Hu812e9212023-06-20 06:24:53 +00002734 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002735 continue;
2736 }
2737
2738 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +08002739 final AdvertiserMetrics metrics =
2740 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00002741 mAdvertiser.removeService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002742 mMetrics.reportServiceUnregistration(false /* isLegacy */, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002743 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2744 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2745 metrics.mConflictDuringProbingCount,
2746 metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002747 continue;
2748 }
2749
2750 if (!(request instanceof LegacyClientRequest)) {
2751 throw new IllegalStateException("Unknown request type: " + request.getClass());
2752 }
2753
2754 switch (((LegacyClientRequest) request).mRequestCode) {
Dave Plattfeff2af2014-03-07 14:48:22 -08002755 case NsdManager.DISCOVER_SERVICES:
Paul Hud44e1b72023-06-16 02:07:42 +00002756 stopServiceDiscovery(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002757 mMetrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00002758 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2759 request.getFoundServiceCount(),
2760 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002761 request.getServicesCount(),
2762 NO_SENT_QUERY_COUNT);
Dave Plattfeff2af2014-03-07 14:48:22 -08002763 break;
2764 case NsdManager.RESOLVE_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002765 stopResolveService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002766 mMetrics.reportServiceResolutionStop(true /* isLegacy */, transactionId,
Paul Hu60149052023-07-31 14:26:08 +08002767 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Dave Plattfeff2af2014-03-07 14:48:22 -08002768 break;
2769 case NsdManager.REGISTER_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002770 unregisterService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002771 mMetrics.reportServiceUnregistration(true /* isLegacy */, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002772 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2773 NO_PACKET /* repliedRequestsCount */,
2774 NO_PACKET /* sentPacketCount */,
2775 0 /* conflictDuringProbingCount */,
2776 0 /* conflictAfterProbingCount */);
Dave Plattfeff2af2014-03-07 14:48:22 -08002777 break;
2778 default:
2779 break;
2780 }
2781 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002782 mClientRequests.clear();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002783 updateMulticastLock();
2784 }
2785
2786 /**
2787 * Returns true if this client has any Java backend request that requests one of the given
2788 * networks.
2789 */
2790 boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) {
2791 for (int i = 0; i < mClientRequests.size(); i++) {
2792 final ClientRequest req = mClientRequests.valueAt(i);
2793 if (!(req instanceof JavaBackendClientRequest)) {
2794 continue;
2795 }
2796 final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i))
2797 .getRequestedNetwork();
2798 if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) {
2799 return true;
2800 }
2801 }
2802 return false;
Dave Plattfeff2af2014-03-07 14:48:22 -08002803 }
2804
Paul Hud44e1b72023-06-16 02:07:42 +00002805 // mClientRequests is a sparse array of client request id -> ClientRequest. For a given
2806 // transaction id, return the corresponding client request id.
2807 private int getClientRequestId(final int transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002808 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002809 if (mClientRequests.valueAt(i).mTransactionId == transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002810 return mClientRequests.keyAt(i);
2811 }
Christopher Lane74411222014-04-25 18:39:07 -07002812 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002813 return -1;
Christopher Lane74411222014-04-25 18:39:07 -07002814 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002815
Paul Hub2e67d32023-04-18 05:50:14 +00002816 private void log(String message) {
2817 mClientLogs.log(message);
2818 }
2819
Paul Hu508a0122023-09-11 15:31:33 +08002820 private static boolean isLegacyClientRequest(@NonNull ClientRequest request) {
2821 return !(request instanceof DiscoveryManagerRequest)
2822 && !(request instanceof AdvertiserClientRequest);
2823 }
2824
Kangping Dong1f1a3792023-12-10 22:05:04 +08002825 void onDiscoverServicesStarted(int listenerKey, DiscoveryRequest discoveryRequest,
Paul Hu508a0122023-09-11 15:31:33 +08002826 ClientRequest request) {
2827 mMetrics.reportServiceDiscoveryStarted(
2828 isLegacyClientRequest(request), request.mTransactionId);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002829 try {
Kangping Dong1f1a3792023-12-10 22:05:04 +08002830 mCb.onDiscoverServicesStarted(listenerKey, discoveryRequest);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002831 } catch (RemoteException e) {
2832 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
2833 }
2834 }
Paul Hu508a0122023-09-11 15:31:33 +08002835 void onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2836 onDiscoverServicesFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2837 0L /* durationMs */);
Paul Hu812e9212023-06-20 06:24:53 +00002838 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002839
Paul Hu508a0122023-09-11 15:31:33 +08002840 void onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy,
2841 int transactionId, long durationMs) {
2842 mMetrics.reportServiceDiscoveryFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002843 try {
2844 mCb.onDiscoverServicesFailed(listenerKey, error);
2845 } catch (RemoteException e) {
2846 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
2847 }
2848 }
2849
Paul Hu812e9212023-06-20 06:24:53 +00002850 void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2851 request.onServiceFound(info.getServiceName());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002852 try {
2853 mCb.onServiceFound(listenerKey, info);
2854 } catch (RemoteException e) {
2855 Log.e(TAG, "Error calling onServiceFound(", e);
2856 }
2857 }
2858
Paul Hu812e9212023-06-20 06:24:53 +00002859 void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2860 request.onServiceLost();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002861 try {
2862 mCb.onServiceLost(listenerKey, info);
2863 } catch (RemoteException e) {
2864 Log.e(TAG, "Error calling onServiceLost(", e);
2865 }
2866 }
2867
2868 void onStopDiscoveryFailed(int listenerKey, int error) {
2869 try {
2870 mCb.onStopDiscoveryFailed(listenerKey, error);
2871 } catch (RemoteException e) {
2872 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
2873 }
2874 }
2875
Paul Hu812e9212023-06-20 06:24:53 +00002876 void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) {
2877 mMetrics.reportServiceDiscoveryStop(
Paul Hu508a0122023-09-11 15:31:33 +08002878 isLegacyClientRequest(request),
Paul Hu812e9212023-06-20 06:24:53 +00002879 request.mTransactionId,
2880 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2881 request.getFoundServiceCount(),
2882 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002883 request.getServicesCount(),
2884 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002885 try {
2886 mCb.onStopDiscoverySucceeded(listenerKey);
2887 } catch (RemoteException e) {
2888 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
2889 }
2890 }
2891
Paul Hu508a0122023-09-11 15:31:33 +08002892 void onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2893 onRegisterServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2894 0L /* durationMs */);
Paul Hu777ed052023-06-19 13:35:15 +00002895 }
2896
Paul Hu508a0122023-09-11 15:31:33 +08002897 void onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy,
2898 int transactionId, long durationMs) {
2899 mMetrics.reportServiceRegistrationFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002900 try {
2901 mCb.onRegisterServiceFailed(listenerKey, error);
2902 } catch (RemoteException e) {
2903 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
2904 }
2905 }
2906
Paul Hu508a0122023-09-11 15:31:33 +08002907 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info,
2908 ClientRequest request) {
2909 mMetrics.reportServiceRegistrationSucceeded(isLegacyClientRequest(request),
2910 request.mTransactionId,
2911 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002912 try {
2913 mCb.onRegisterServiceSucceeded(listenerKey, info);
2914 } catch (RemoteException e) {
2915 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
2916 }
2917 }
2918
2919 void onUnregisterServiceFailed(int listenerKey, int error) {
2920 try {
2921 mCb.onUnregisterServiceFailed(listenerKey, error);
2922 } catch (RemoteException e) {
2923 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
2924 }
2925 }
2926
Paul Hu508a0122023-09-11 15:31:33 +08002927 void onUnregisterServiceSucceeded(int listenerKey, ClientRequest request,
Paul Hu043bcd42023-07-14 16:38:25 +08002928 AdvertiserMetrics metrics) {
Paul Hu508a0122023-09-11 15:31:33 +08002929 mMetrics.reportServiceUnregistration(isLegacyClientRequest(request),
2930 request.mTransactionId,
2931 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hu043bcd42023-07-14 16:38:25 +08002932 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2933 metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002934 try {
2935 mCb.onUnregisterServiceSucceeded(listenerKey);
2936 } catch (RemoteException e) {
2937 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
2938 }
2939 }
2940
Paul Hu508a0122023-09-11 15:31:33 +08002941 void onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2942 onResolveServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2943 0L /* durationMs */);
Paul Hua6bc4632023-06-26 01:18:29 +00002944 }
2945
Paul Hu508a0122023-09-11 15:31:33 +08002946 void onResolveServiceFailed(int listenerKey, int error, boolean isLegacy,
2947 int transactionId, long durationMs) {
2948 mMetrics.reportServiceResolutionFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002949 try {
2950 mCb.onResolveServiceFailed(listenerKey, error);
2951 } catch (RemoteException e) {
2952 Log.e(TAG, "Error calling onResolveServiceFailed", e);
2953 }
2954 }
2955
Paul Hua6bc4632023-06-26 01:18:29 +00002956 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
2957 ClientRequest request) {
2958 mMetrics.reportServiceResolved(
Paul Hu508a0122023-09-11 15:31:33 +08002959 isLegacyClientRequest(request),
Paul Hua6bc4632023-06-26 01:18:29 +00002960 request.mTransactionId,
2961 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hubad6fe92023-07-24 21:25:22 +08002962 request.isServiceFromCache(),
2963 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002964 try {
2965 mCb.onResolveServiceSucceeded(listenerKey, info);
2966 } catch (RemoteException e) {
2967 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
2968 }
2969 }
Paul Hub58deb72022-12-26 09:24:42 +00002970
2971 void onStopResolutionFailed(int listenerKey, int error) {
2972 try {
2973 mCb.onStopResolutionFailed(listenerKey, error);
2974 } catch (RemoteException e) {
2975 Log.e(TAG, "Error calling onStopResolutionFailed", e);
2976 }
2977 }
2978
Paul Hu60149052023-07-31 14:26:08 +08002979 void onStopResolutionSucceeded(int listenerKey, ClientRequest request) {
2980 mMetrics.reportServiceResolutionStop(
Paul Hu508a0122023-09-11 15:31:33 +08002981 isLegacyClientRequest(request),
Paul Hu60149052023-07-31 14:26:08 +08002982 request.mTransactionId,
2983 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hub58deb72022-12-26 09:24:42 +00002984 try {
2985 mCb.onStopResolutionSucceeded(listenerKey);
2986 } catch (RemoteException e) {
2987 Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
2988 }
2989 }
Paul Hu18aeccc2022-12-27 08:48:48 +00002990
2991 void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
Paul Huddce5912023-08-01 10:26:49 +08002992 mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
Paul Hu18aeccc2022-12-27 08:48:48 +00002993 try {
2994 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
2995 } catch (RemoteException e) {
2996 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
2997 }
2998 }
2999
Paul Huddce5912023-08-01 10:26:49 +08003000 void onServiceInfoCallbackRegistered(int transactionId) {
3001 mMetrics.reportServiceInfoCallbackRegistered(transactionId);
3002 }
3003
3004 void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) {
3005 request.onServiceFound(info.getServiceName());
Paul Hu18aeccc2022-12-27 08:48:48 +00003006 try {
3007 mCb.onServiceUpdated(listenerKey, info);
3008 } catch (RemoteException e) {
3009 Log.e(TAG, "Error calling onServiceUpdated", e);
3010 }
3011 }
3012
Paul Huddce5912023-08-01 10:26:49 +08003013 void onServiceUpdatedLost(int listenerKey, ClientRequest request) {
3014 request.onServiceLost();
Paul Hu18aeccc2022-12-27 08:48:48 +00003015 try {
3016 mCb.onServiceUpdatedLost(listenerKey);
3017 } catch (RemoteException e) {
3018 Log.e(TAG, "Error calling onServiceUpdatedLost", e);
3019 }
3020 }
3021
Paul Huddce5912023-08-01 10:26:49 +08003022 void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) {
3023 mMetrics.reportServiceInfoCallbackUnregistered(
3024 request.mTransactionId,
3025 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
3026 request.getFoundServiceCount(),
3027 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08003028 request.isServiceFromCache(),
3029 request.getSentQueryCount());
Paul Hu18aeccc2022-12-27 08:48:48 +00003030 try {
3031 mCb.onServiceInfoCallbackUnregistered(listenerKey);
3032 } catch (RemoteException e) {
3033 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
3034 }
3035 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07003036 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07003037}