blob: 93ccb8529553560a8c28a20b292c76d2801ccc05 [file] [log] [blame]
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001/*
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002 * Copyright (C) 2021 The Android Open Source Project
Irfan Sheriff77ec5582012-03-22 17:01:39 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Yuyang Huang33fa4d22023-02-14 22:59:37 +090019import static android.Manifest.permission.NETWORK_SETTINGS;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090020import static android.Manifest.permission.NETWORK_STACK;
paulhu2b9ed952022-02-10 21:58:32 +080021import static android.net.ConnectivityManager.NETID_UNSET;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090022import static android.net.NetworkCapabilities.TRANSPORT_VPN;
23import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090024import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
Paul Hu019621e2023-01-13 23:26:49 +080025import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
paulhu2b9ed952022-02-10 21:58:32 +080026import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +090027import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +090028import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
Motomu Utsumied4e7ec2023-09-13 14:58:32 +090029
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090030import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090031import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
Paul Hu043bcd42023-07-14 16:38:25 +080032import static com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserMetrics;
33import static com.android.server.connectivity.mdns.MdnsConstants.NO_PACKET;
Yuyang Huangde802c82023-05-02 17:14:22 +090034import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
Paul Hucdef3532023-06-18 14:47:35 +000035import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
Remi NGUYEN VANbeb03f12023-03-08 19:03:27 +090036
Paul Hu23fa2022023-01-13 22:57:24 +080037import android.annotation.NonNull;
Paul Hu4bd98ef2023-01-12 13:42:07 +080038import android.annotation.Nullable;
Yuyang Huangfc831702023-08-21 17:48:48 +090039import android.annotation.RequiresApi;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090040import android.app.ActivityManager;
paulhua262cc12019-08-12 16:25:11 +080041import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070042import android.content.Intent;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090043import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090044import android.net.INetd;
Paul Hu75069ed2023-01-14 00:31:09 +080045import android.net.InetAddresses;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090046import android.net.LinkProperties;
47import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080048import android.net.mdns.aidl.DiscoveryInfo;
49import android.net.mdns.aidl.GetAddressInfo;
50import android.net.mdns.aidl.IMDnsEventListener;
51import android.net.mdns.aidl.RegistrationInfo;
52import android.net.mdns.aidl.ResolutionInfo;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070053import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090054import android.net.nsd.INsdManagerCallback;
55import android.net.nsd.INsdServiceConnector;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090056import android.net.nsd.IOffloadEngine;
paulhu2b9ed952022-02-10 21:58:32 +080057import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070058import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080059import android.net.nsd.NsdServiceInfo;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090060import android.net.nsd.OffloadEngine;
61import android.net.nsd.OffloadServiceInfo;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090062import android.net.wifi.WifiManager;
Paul Hub2e67d32023-04-18 05:50:14 +000063import android.os.Binder;
Yuyang Huangfc831702023-08-21 17:48:48 +090064import android.os.Build;
Hugo Benichi803a2f02017-04-24 11:35:06 +090065import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080066import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090067import android.os.IBinder;
Paul Hu4bd98ef2023-01-12 13:42:07 +080068import android.os.Looper;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070069import android.os.Message;
Yuyang Huang33fa4d22023-02-14 22:59:37 +090070import android.os.RemoteCallbackList;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090071import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070072import android.os.UserHandle;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090073import android.provider.DeviceConfig;
Paul Hu23fa2022023-01-13 22:57:24 +080074import android.text.TextUtils;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090075import android.util.ArraySet;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090076import android.util.Log;
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +090077import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070078import android.util.SparseArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070079
paulhua262cc12019-08-12 16:25:11 +080080import com.android.internal.annotations.VisibleForTesting;
Paul Hub2e67d32023-04-18 05:50:14 +000081import com.android.internal.util.IndentingPrintWriter;
paulhua262cc12019-08-12 16:25:11 +080082import com.android.internal.util.State;
83import com.android.internal.util.StateMachine;
Paul Hucdef3532023-06-18 14:47:35 +000084import com.android.metrics.NetworkNsdReportedMetrics;
Yuyang Huang8e6fbc82023-08-07 17:46:19 +090085import com.android.modules.utils.build.SdkLevel;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090086import com.android.net.module.util.CollectionUtils;
Paul Hu4bd98ef2023-01-12 13:42:07 +080087import com.android.net.module.util.DeviceConfigUtils;
Yuyang Huanga6a6ff92023-04-24 13:33:34 +090088import com.android.net.module.util.InetAddressUtils;
paulhu3ffffe72021-09-16 10:15:22 +080089import com.android.net.module.util.PermissionUtils;
Paul Hub2e67d32023-04-18 05:50:14 +000090import com.android.net.module.util.SharedLog;
Paul Hu4bd98ef2023-01-12 13:42:07 +080091import com.android.server.connectivity.mdns.ExecutorProvider;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +090092import com.android.server.connectivity.mdns.MdnsAdvertiser;
Paul Hu4bd98ef2023-01-12 13:42:07 +080093import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
Yuyang Huangb96a0712023-09-07 15:13:15 +090094import com.android.server.connectivity.mdns.MdnsFeatureFlags;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +090095import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
Paul Hu4bd98ef2023-01-12 13:42:07 +080096import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
Paul Hu23fa2022023-01-13 22:57:24 +080097import com.android.server.connectivity.mdns.MdnsSearchOptions;
98import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
99import com.android.server.connectivity.mdns.MdnsServiceInfo;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800100import com.android.server.connectivity.mdns.MdnsSocketProvider;
Yuyang Huangde802c82023-05-02 17:14:22 +0900101import com.android.server.connectivity.mdns.util.MdnsUtils;
paulhua262cc12019-08-12 16:25:11 +0800102
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700103import java.io.FileDescriptor;
104import java.io.PrintWriter;
Yuyang Huangaa0e9602023-03-17 12:43:09 +0900105import java.net.Inet6Address;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700106import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900107import java.net.NetworkInterface;
108import java.net.SocketException;
109import java.net.UnknownHostException;
Paul Hu2b865912023-03-06 14:27:53 +0800110import java.util.ArrayList;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +0900111import java.util.Arrays;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700112import java.util.HashMap;
Paul Hu23fa2022023-01-13 22:57:24 +0800113import java.util.List;
Paul Hu75069ed2023-01-14 00:31:09 +0800114import java.util.Map;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900115import java.util.Objects;
Paul Hu812e9212023-06-20 06:24:53 +0000116import java.util.Set;
Paul Hu23fa2022023-01-13 22:57:24 +0800117import java.util.regex.Matcher;
118import java.util.regex.Pattern;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700119
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700120/**
121 * Network Service Discovery Service handles remote service discovery operation requests by
122 * implementing the INsdManager interface.
123 *
124 * @hide
125 */
Yuyang Huangfc831702023-08-21 17:48:48 +0900126@RequiresApi(Build.VERSION_CODES.TIRAMISU)
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700127public class NsdService extends INsdManager.Stub {
128 private static final String TAG = "NsdService";
129 private static final String MDNS_TAG = "mDnsConnector";
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900130 /**
131 * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
132 * implementation.
133 */
Paul Hu4bd98ef2023-01-12 13:42:07 +0800134 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
Paul Hu23fa2022023-01-13 22:57:24 +0800135 private static final String LOCAL_DOMAIN_NAME = "local";
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700136
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900137 /**
138 * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
139 * implementation.
140 */
141 private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
142
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900143 /**
144 * Comma-separated list of type:flag mappings indicating the flags to use to allowlist
145 * discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
146 *
147 * For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
148 * _mytype._tcp:mytype,_othertype._tcp.local:othertype
149 *
150 * In which case the flags:
151 * "mdns_discovery_manager_allowlist_mytype_version",
152 * "mdns_advertiser_allowlist_mytype_version",
153 * "mdns_discovery_manager_allowlist_othertype_version",
154 * "mdns_advertiser_allowlist_othertype_version"
155 * would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
156 * be read with
Motomu Utsumi624aeb42023-08-15 15:52:27 +0900157 * {@link DeviceConfigUtils#isTetheringFeatureEnabled}
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900158 *
159 * @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
160 * @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
161 * @see #MDNS_ALLOWLIST_FLAG_SUFFIX
162 */
163 private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
164
165 private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
166 "mdns_discovery_manager_allowlist_";
167 private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
168 "mdns_advertiser_allowlist_";
169 private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
170
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900171 @VisibleForTesting
172 static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
173 "mdns_config_running_app_active_importance_cutoff";
174 @VisibleForTesting
175 static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
176 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
177 private final int mRunningAppActiveImportanceCutoff;
178
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900179 public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Luke Huang92860f92021-06-23 06:29:30 +0000180 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900181 private static final int IFACE_IDX_ANY = 0;
Paul Hu812e9212023-06-20 06:24:53 +0000182 private static final int MAX_SERVICES_COUNT_METRIC_PER_CLIENT = 100;
183 @VisibleForTesting
184 static final int NO_TRANSACTION = -1;
Paul Hubad6fe92023-07-24 21:25:22 +0800185 private static final int NO_SENT_QUERY_COUNT = 0;
186 private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
Paul Hu14667de2023-04-17 22:42:47 +0800187 private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700188
Hugo Benichi32be63d2017-04-05 14:06:11 +0900189 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +0900190 private final NsdStateMachine mNsdStateMachine;
paulhu2b9ed952022-02-10 21:58:32 +0800191 private final MDnsManager mMDnsManager;
192 private final MDnsEventCallback mMDnsEventCallback;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900193 @NonNull
194 private final Dependencies mDeps;
195 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800196 private final MdnsMultinetworkSocketClient mMdnsSocketClient;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900197 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800198 private final MdnsDiscoveryManager mMdnsDiscoveryManager;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900199 @NonNull
Paul Hu4bd98ef2023-01-12 13:42:07 +0800200 private final MdnsSocketProvider mMdnsSocketProvider;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900201 @NonNull
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900202 private final MdnsAdvertiser mAdvertiser;
Paul Hu777ed052023-06-19 13:35:15 +0000203 @NonNull
204 private final Clock mClock;
Paul Hu14667de2023-04-17 22:42:47 +0800205 private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
Paul Hu23fa2022023-01-13 22:57:24 +0800206 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
paulhu2b9ed952022-02-10 21:58:32 +0800207 // state machine thread. If change this outside state machine, it will need to introduce
208 // synchronization.
209 private boolean mIsDaemonStarted = false;
Paul Hu23fa2022023-01-13 22:57:24 +0800210 private boolean mIsMonitoringSocketsStarted = false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700211
212 /**
213 * Clients receiving asynchronous messages
214 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900215 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700216
Paul Hud44e1b72023-06-16 02:07:42 +0000217 /* A map from transaction(unique) id to client info */
218 private final SparseArray<ClientInfo> mTransactionIdToClientInfoMap = new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700219
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900220 // Note this is not final to avoid depending on the Wi-Fi service starting before NsdService
221 @Nullable
222 private WifiManager.MulticastLock mHeldMulticastLock;
223 // Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network
224 // (non-null), value is the requested Network (nullable)
225 @NonNull
226 private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>();
227 @NonNull
228 private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>();
229
Luke Huang05298582021-06-13 16:52:05 +0000230 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700231
Hugo Benichi32be63d2017-04-05 14:06:11 +0900232 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700233 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +0800234 // The count of the connected legacy clients.
235 private int mLegacyClientCount = 0;
Paul Hub2e67d32023-04-18 05:50:14 +0000236 // The number of client that ever connected.
237 private int mClientNumberId = 1;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700238
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900239 private final RemoteCallbackList<IOffloadEngine> mOffloadEngines =
240 new RemoteCallbackList<>();
241
242 private static class OffloadEngineInfo {
243 @NonNull final String mInterfaceName;
244 final long mOffloadCapabilities;
245 final long mOffloadType;
246 @NonNull final IOffloadEngine mOffloadEngine;
247
248 OffloadEngineInfo(@NonNull IOffloadEngine offloadEngine,
249 @NonNull String interfaceName, long capabilities, long offloadType) {
250 this.mOffloadEngine = offloadEngine;
251 this.mInterfaceName = interfaceName;
252 this.mOffloadCapabilities = capabilities;
253 this.mOffloadType = offloadType;
254 }
255 }
256
Paul Hu812e9212023-06-20 06:24:53 +0000257 @VisibleForTesting
258 static class MdnsListener implements MdnsServiceBrowserListener {
Paul Hud44e1b72023-06-16 02:07:42 +0000259 protected final int mClientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800260 protected final int mTransactionId;
261 @NonNull
262 protected final NsdServiceInfo mReqServiceInfo;
263 @NonNull
264 protected final String mListenedServiceType;
265
Paul Hud44e1b72023-06-16 02:07:42 +0000266 MdnsListener(int clientRequestId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
Paul Hu23fa2022023-01-13 22:57:24 +0800267 @NonNull String listenedServiceType) {
Paul Hud44e1b72023-06-16 02:07:42 +0000268 mClientRequestId = clientRequestId;
Paul Hu23fa2022023-01-13 22:57:24 +0800269 mTransactionId = transactionId;
270 mReqServiceInfo = reqServiceInfo;
271 mListenedServiceType = listenedServiceType;
272 }
273
274 @NonNull
275 public String getListenedServiceType() {
276 return mListenedServiceType;
277 }
278
279 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000280 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
281 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800282
283 @Override
284 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
285
286 @Override
287 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
288
289 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000290 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
291 boolean isServiceFromCache) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800292
293 @Override
294 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
295
296 @Override
297 public void onSearchStoppedWithError(int error) { }
298
299 @Override
300 public void onSearchFailedToStart() { }
301
302 @Override
Paul Hubad6fe92023-07-24 21:25:22 +0800303 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
304 int sentQueryTransactionId) { }
Paul Hu23fa2022023-01-13 22:57:24 +0800305
306 @Override
307 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
308 }
309
310 private class DiscoveryListener extends MdnsListener {
311
Paul Hud44e1b72023-06-16 02:07:42 +0000312 DiscoveryListener(int clientRequestId, int transactionId,
313 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
314 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu23fa2022023-01-13 22:57:24 +0800315 }
316
317 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000318 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo,
319 boolean isServiceFromCache) {
Paul Hu019621e2023-01-13 23:26:49 +0800320 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
321 NsdManager.SERVICE_FOUND,
Paul Hua6bc4632023-06-26 01:18:29 +0000322 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu23fa2022023-01-13 22:57:24 +0800323 }
324
325 @Override
326 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
Paul Hu319751a2023-01-13 23:56:34 +0800327 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
328 NsdManager.SERVICE_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000329 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu23fa2022023-01-13 22:57:24 +0800330 }
Paul Hubad6fe92023-07-24 21:25:22 +0800331
332 @Override
333 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
334 int sentQueryTransactionId) {
335 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
336 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
337 }
Paul Hu23fa2022023-01-13 22:57:24 +0800338 }
339
Paul Hu75069ed2023-01-14 00:31:09 +0800340 private class ResolutionListener extends MdnsListener {
341
Paul Hud44e1b72023-06-16 02:07:42 +0000342 ResolutionListener(int clientRequestId, int transactionId,
343 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
344 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +0800345 }
346
347 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000348 public void onServiceFound(MdnsServiceInfo serviceInfo, boolean isServiceFromCache) {
Paul Hu75069ed2023-01-14 00:31:09 +0800349 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
350 NsdManager.RESOLVE_SERVICE_SUCCEEDED,
Paul Hua6bc4632023-06-26 01:18:29 +0000351 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu75069ed2023-01-14 00:31:09 +0800352 }
Paul Hubad6fe92023-07-24 21:25:22 +0800353
354 @Override
355 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
356 int sentQueryTransactionId) {
357 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
358 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
359 }
Paul Hu75069ed2023-01-14 00:31:09 +0800360 }
361
Paul Hu30bd70d2023-02-07 13:20:56 +0000362 private class ServiceInfoListener extends MdnsListener {
363
Paul Hud44e1b72023-06-16 02:07:42 +0000364 ServiceInfoListener(int clientRequestId, int transactionId,
365 @NonNull NsdServiceInfo reqServiceInfo, @NonNull String listenServiceType) {
366 super(clientRequestId, transactionId, reqServiceInfo, listenServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +0000367 }
368
369 @Override
Paul Hua6bc4632023-06-26 01:18:29 +0000370 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo,
371 boolean isServiceFromCache) {
Paul Hu30bd70d2023-02-07 13:20:56 +0000372 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
373 NsdManager.SERVICE_UPDATED,
Paul Hua6bc4632023-06-26 01:18:29 +0000374 new MdnsEvent(mClientRequestId, serviceInfo, isServiceFromCache));
Paul Hu30bd70d2023-02-07 13:20:56 +0000375 }
376
377 @Override
378 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
379 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
380 NsdManager.SERVICE_UPDATED,
Paul Hud44e1b72023-06-16 02:07:42 +0000381 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000382 }
383
384 @Override
385 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
386 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
387 NsdManager.SERVICE_UPDATED_LOST,
Paul Hud44e1b72023-06-16 02:07:42 +0000388 new MdnsEvent(mClientRequestId, serviceInfo));
Paul Hu30bd70d2023-02-07 13:20:56 +0000389 }
Paul Hubad6fe92023-07-24 21:25:22 +0800390
391 @Override
392 public void onDiscoveryQuerySent(@NonNull List<String> subtypes,
393 int sentQueryTransactionId) {
394 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
395 DISCOVERY_QUERY_SENT_CALLBACK, new MdnsEvent(mClientRequestId));
396 }
Paul Hu30bd70d2023-02-07 13:20:56 +0000397 }
398
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900399 private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
400 @Override
401 public void onSocketRequestFulfilled(@Nullable Network socketNetwork,
402 @NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {
403 // The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF
404 // filtering on such interfaces, so taking the multicast lock is not necessary to
405 // disable APF filtering of multicast.
406 if (socketNetwork == null
407 || !CollectionUtils.contains(transports, TRANSPORT_WIFI)
408 || CollectionUtils.contains(transports, TRANSPORT_VPN)) {
409 return;
410 }
411
412 if (mWifiLockRequiredNetworks.add(socketNetwork)) {
413 updateMulticastLock();
414 }
415 }
416
417 @Override
418 public void onSocketDestroyed(@Nullable Network socketNetwork,
419 @NonNull MdnsInterfaceSocket socket) {
420 if (mWifiLockRequiredNetworks.remove(socketNetwork)) {
421 updateMulticastLock();
422 }
423 }
424 }
425
426 private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
427 private final Handler mHandler;
428
429 private UidImportanceListener(Handler handler) {
430 mHandler = handler;
431 }
432
433 @Override
434 public void onUidImportance(int uid, int importance) {
435 mHandler.post(() -> handleUidImportanceChanged(uid, importance));
436 }
437 }
438
439 private void handleUidImportanceChanged(int uid, int importance) {
440 // Lower importance values are more "important"
441 final boolean modified = importance <= mRunningAppActiveImportanceCutoff
442 ? mRunningAppActiveUids.add(uid)
443 : mRunningAppActiveUids.remove(uid);
444 if (modified) {
445 updateMulticastLock();
446 }
447 }
448
449 /**
450 * Take or release the lock based on updated internal state.
451 *
452 * This determines whether the lock needs to be held based on
453 * {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and
454 * {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been
455 * updated.
456 */
457 private void updateMulticastLock() {
458 final int needsLockUid = getMulticastLockNeededUid();
459 if (needsLockUid >= 0 && mHeldMulticastLock == null) {
460 final WifiManager wm = mContext.getSystemService(WifiManager.class);
461 if (wm == null) {
462 Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager");
463 return;
464 }
465 mHeldMulticastLock = wm.createMulticastLock(TAG);
466 mHeldMulticastLock.acquire();
467 mServiceLogs.log("Taking multicast lock for uid " + needsLockUid);
468 } else if (needsLockUid < 0 && mHeldMulticastLock != null) {
469 mHeldMulticastLock.release();
470 mHeldMulticastLock = null;
471 mServiceLogs.log("Released multicast lock");
472 }
473 }
474
475 /**
476 * @return The UID of an app requiring the multicast lock, or -1 if none.
477 */
478 private int getMulticastLockNeededUid() {
479 if (mWifiLockRequiredNetworks.size() == 0) {
480 // Return early if NSD is not active, or not on any relevant network
481 return -1;
482 }
Paul Hud44e1b72023-06-16 02:07:42 +0000483 for (int i = 0; i < mTransactionIdToClientInfoMap.size(); i++) {
484 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.valueAt(i);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900485 if (!mRunningAppActiveUids.contains(clientInfo.mUid)) {
486 // Ignore non-active UIDs
487 continue;
488 }
489
490 if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) {
491 return clientInfo.mUid;
492 }
493 }
494 return -1;
495 }
496
Paul Hu019621e2023-01-13 23:26:49 +0800497 /**
498 * Data class of mdns service callback information.
499 */
500 private static class MdnsEvent {
Paul Hud44e1b72023-06-16 02:07:42 +0000501 final int mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +0800502 @Nullable
Paul Hu019621e2023-01-13 23:26:49 +0800503 final MdnsServiceInfo mMdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000504 final boolean mIsServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800505
Paul Hubad6fe92023-07-24 21:25:22 +0800506 MdnsEvent(int clientRequestId) {
507 this(clientRequestId, null /* mdnsServiceInfo */, false /* isServiceFromCache */);
508 }
509
510 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo) {
Paul Hua6bc4632023-06-26 01:18:29 +0000511 this(clientRequestId, mdnsServiceInfo, false /* isServiceFromCache */);
512 }
513
Paul Hubad6fe92023-07-24 21:25:22 +0800514 MdnsEvent(int clientRequestId, @Nullable MdnsServiceInfo mdnsServiceInfo,
Paul Hua6bc4632023-06-26 01:18:29 +0000515 boolean isServiceFromCache) {
Paul Hud44e1b72023-06-16 02:07:42 +0000516 mClientRequestId = clientRequestId;
Paul Hu019621e2023-01-13 23:26:49 +0800517 mMdnsServiceInfo = mdnsServiceInfo;
Paul Hua6bc4632023-06-26 01:18:29 +0000518 mIsServiceFromCache = isServiceFromCache;
Paul Hu019621e2023-01-13 23:26:49 +0800519 }
520 }
521
Irfan Sheriff75006652012-04-17 23:15:29 -0700522 private class NsdStateMachine extends StateMachine {
523
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700524 private final DefaultState mDefaultState = new DefaultState();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700525 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700526
527 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700528 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900529 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700530 }
531
Luke Huang92860f92021-06-23 06:29:30 +0000532 private void maybeStartDaemon() {
paulhu2b9ed952022-02-10 21:58:32 +0800533 if (mIsDaemonStarted) {
534 if (DBG) Log.d(TAG, "Daemon is already started.");
535 return;
536 }
537 mMDnsManager.registerEventListener(mMDnsEventCallback);
538 mMDnsManager.startDaemon();
539 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000540 maybeScheduleStop();
Paul Hub2e67d32023-04-18 05:50:14 +0000541 mServiceLogs.log("Start mdns_responder daemon");
Luke Huang05298582021-06-13 16:52:05 +0000542 }
543
paulhu2b9ed952022-02-10 21:58:32 +0800544 private void maybeStopDaemon() {
545 if (!mIsDaemonStarted) {
546 if (DBG) Log.d(TAG, "Daemon has not been started.");
547 return;
548 }
549 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
550 mMDnsManager.stopDaemon();
551 mIsDaemonStarted = false;
Paul Hub2e67d32023-04-18 05:50:14 +0000552 mServiceLogs.log("Stop mdns_responder daemon");
paulhu2b9ed952022-02-10 21:58:32 +0800553 }
554
Luke Huang92860f92021-06-23 06:29:30 +0000555 private boolean isAnyRequestActive() {
Paul Hud44e1b72023-06-16 02:07:42 +0000556 return mTransactionIdToClientInfoMap.size() != 0;
Luke Huang92860f92021-06-23 06:29:30 +0000557 }
558
559 private void scheduleStop() {
560 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
561 }
562 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800563 // The native daemon should stay alive and can't be cleanup
564 // if any legacy client connected.
565 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000566 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000567 }
568 }
569
Luke Huang92860f92021-06-23 06:29:30 +0000570 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000571 this.removeMessages(NsdManager.DAEMON_CLEANUP);
572 }
573
Paul Hu23fa2022023-01-13 22:57:24 +0800574 private void maybeStartMonitoringSockets() {
575 if (mIsMonitoringSocketsStarted) {
576 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
577 return;
578 }
579
580 mMdnsSocketProvider.startMonitoringSockets();
581 mIsMonitoringSocketsStarted = true;
582 }
583
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900584 private void maybeStopMonitoringSocketsIfNoActiveRequest() {
585 if (!mIsMonitoringSocketsStarted) return;
586 if (isAnyRequestActive()) return;
587
Paul Hu58f20602023-02-18 11:41:07 +0800588 mMdnsSocketProvider.requestStopWhenInactive();
Paul Hu23fa2022023-01-13 22:57:24 +0800589 mIsMonitoringSocketsStarted = false;
590 }
591
Hugo Benichi803a2f02017-04-24 11:35:06 +0900592 NsdStateMachine(String name, Handler handler) {
593 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700594 addState(mDefaultState);
Irfan Sheriff75006652012-04-17 23:15:29 -0700595 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800596 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900597 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700598 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700599 }
600
601 class DefaultState extends State {
602 @Override
603 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900604 final ClientInfo cInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000605 final int clientRequestId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700606 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900607 case NsdManager.REGISTER_CLIENT:
Paul Hu2e0a88c2023-03-09 16:05:01 +0800608 final ConnectorArgs arg = (ConnectorArgs) msg.obj;
609 final INsdManagerCallback cb = arg.callback;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900610 try {
Paul Hu2e0a88c2023-03-09 16:05:01 +0800611 cb.asBinder().linkToDeath(arg.connector, 0);
Paul Hub2e67d32023-04-18 05:50:14 +0000612 final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
Paul Hu777ed052023-06-19 13:35:15 +0000613 final NetworkNsdReportedMetrics metrics =
614 mDeps.makeNetworkNsdReportedMetrics(
Paul Hu508a0122023-09-11 15:31:33 +0800615 (int) mClock.elapsedRealtime());
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900616 cInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
Paul Hucdef3532023-06-18 14:47:35 +0000617 mServiceLogs.forSubComponent(tag), metrics);
Paul Hu2e0a88c2023-03-09 16:05:01 +0800618 mClients.put(arg.connector, cInfo);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900619 } catch (RemoteException e) {
Paul Hud44e1b72023-06-16 02:07:42 +0000620 Log.w(TAG, "Client request id " + clientRequestId
621 + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700622 }
623 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900624 case NsdManager.UNREGISTER_CLIENT:
625 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
626 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800627 if (cInfo != null) {
628 cInfo.expungeAllRequests();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900629 if (cInfo.isPreSClient()) {
Luke Huangf7277ed2021-07-12 21:15:10 +0800630 mLegacyClientCount -= 1;
631 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800632 }
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900633 maybeStopMonitoringSocketsIfNoActiveRequest();
Luke Huangf7277ed2021-07-12 21:15:10 +0800634 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700635 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700636 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900637 cInfo = getClientInfoForReply(msg);
638 if (cInfo != null) {
Paul Hu508a0122023-09-11 15:31:33 +0800639 cInfo.onDiscoverServicesFailedImmediately(clientRequestId,
640 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900641 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700642 break;
643 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900644 cInfo = getClientInfoForReply(msg);
645 if (cInfo != null) {
646 cInfo.onStopDiscoveryFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000647 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900648 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700649 break;
650 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900651 cInfo = getClientInfoForReply(msg);
652 if (cInfo != null) {
Paul Hu508a0122023-09-11 15:31:33 +0800653 cInfo.onRegisterServiceFailedImmediately(clientRequestId,
654 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900655 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700656 break;
657 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900658 cInfo = getClientInfoForReply(msg);
659 if (cInfo != null) {
660 cInfo.onUnregisterServiceFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000661 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900662 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700663 break;
664 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900665 cInfo = getClientInfoForReply(msg);
666 if (cInfo != null) {
Paul Hu508a0122023-09-11 15:31:33 +0800667 cInfo.onResolveServiceFailedImmediately(clientRequestId,
668 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900669 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700670 break;
Paul Hub58deb72022-12-26 09:24:42 +0000671 case NsdManager.STOP_RESOLUTION:
672 cInfo = getClientInfoForReply(msg);
673 if (cInfo != null) {
674 cInfo.onStopResolutionFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000675 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
Paul Hub58deb72022-12-26 09:24:42 +0000676 }
677 break;
Paul Hu18aeccc2022-12-27 08:48:48 +0000678 case NsdManager.REGISTER_SERVICE_CALLBACK:
679 cInfo = getClientInfoForReply(msg);
680 if (cInfo != null) {
681 cInfo.onServiceInfoCallbackRegistrationFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000682 clientRequestId, NsdManager.FAILURE_BAD_PARAMETERS);
Paul Hu18aeccc2022-12-27 08:48:48 +0000683 }
684 break;
Luke Huang05298582021-06-13 16:52:05 +0000685 case NsdManager.DAEMON_CLEANUP:
paulhu2b9ed952022-02-10 21:58:32 +0800686 maybeStopDaemon();
Luke Huang05298582021-06-13 16:52:05 +0000687 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800688 // This event should be only sent by the legacy (target SDK < S) clients.
689 // Mark the sending client as legacy.
690 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900691 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800692 if (cInfo != null) {
693 cancelStop();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900694 cInfo.setPreSClient();
Luke Huangf7277ed2021-07-12 21:15:10 +0800695 mLegacyClientCount += 1;
696 maybeStartDaemon();
697 }
698 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700699 default:
paulhub2225702021-11-17 09:35:33 +0800700 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700701 return NOT_HANDLED;
702 }
703 return HANDLED;
704 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900705
706 private ClientInfo getClientInfoForReply(Message msg) {
707 final ListenerArgs args = (ListenerArgs) msg.obj;
708 return mClients.get(args.connector);
709 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700710 }
711
Irfan Sheriff75006652012-04-17 23:15:29 -0700712 class EnabledState extends State {
713 @Override
714 public void enter() {
715 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700716 }
717
718 @Override
719 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000720 // TODO: it is incorrect to stop the daemon without expunging all requests
721 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000722 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700723 }
724
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700725 private boolean requestLimitReached(ClientInfo clientInfo) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900726 if (clientInfo.mClientRequests.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800727 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700728 return true;
729 }
730 return false;
731 }
732
Paul Hu508a0122023-09-11 15:31:33 +0800733 private ClientRequest storeLegacyRequestMap(int clientRequestId, int transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +0000734 ClientInfo clientInfo, int what, long startTimeMs) {
Paul Hu508a0122023-09-11 15:31:33 +0800735 final LegacyClientRequest request =
736 new LegacyClientRequest(transactionId, what, startTimeMs);
737 clientInfo.mClientRequests.put(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000738 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000739 // Remove the cleanup event because here comes a new request.
740 cancelStop();
Paul Hu508a0122023-09-11 15:31:33 +0800741 return request;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700742 }
743
Paul Hud44e1b72023-06-16 02:07:42 +0000744 private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900745 ClientInfo clientInfo, @Nullable Network requestedNetwork) {
Paul Hu777ed052023-06-19 13:35:15 +0000746 clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
Paul Hu812e9212023-06-20 06:24:53 +0000747 transactionId, requestedNetwork, mClock.elapsedRealtime()));
Paul Hud44e1b72023-06-16 02:07:42 +0000748 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900749 updateMulticastLock();
Paul Hu23fa2022023-01-13 22:57:24 +0800750 }
751
Paul Hud44e1b72023-06-16 02:07:42 +0000752 private void removeRequestMap(
753 int clientRequestId, int transactionId, ClientInfo clientInfo) {
754 final ClientRequest existing = clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900755 if (existing == null) return;
Paul Hud44e1b72023-06-16 02:07:42 +0000756 clientInfo.mClientRequests.remove(clientRequestId);
757 mTransactionIdToClientInfoMap.remove(transactionId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900758
759 if (existing instanceof LegacyClientRequest) {
760 maybeScheduleStop();
761 } else {
762 maybeStopMonitoringSocketsIfNoActiveRequest();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900763 updateMulticastLock();
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900764 }
765 }
766
Paul Hu508a0122023-09-11 15:31:33 +0800767 private ClientRequest storeDiscoveryManagerRequestMap(int clientRequestId,
768 int transactionId, MdnsListener listener, ClientInfo clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900769 @Nullable Network requestedNetwork) {
Paul Hu508a0122023-09-11 15:31:33 +0800770 final DiscoveryManagerRequest request = new DiscoveryManagerRequest(transactionId,
771 listener, requestedNetwork, mClock.elapsedRealtime());
772 clientInfo.mClientRequests.put(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000773 mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900774 updateMulticastLock();
Paul Hu508a0122023-09-11 15:31:33 +0800775 return request;
Paul Hu23fa2022023-01-13 22:57:24 +0800776 }
777
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900778 /**
779 * Truncate a service name to up to 63 UTF-8 bytes.
780 *
781 * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
782 * names used in registerService follows historical behavior (see mdnsresponder
783 * handle_regservice_request).
784 */
785 @NonNull
786 private String truncateServiceName(@NonNull String originalName) {
Yuyang Huangde802c82023-05-02 17:14:22 +0900787 return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
Paul Hu23fa2022023-01-13 22:57:24 +0800788 }
789
Paul Hud44e1b72023-06-16 02:07:42 +0000790 private void stopDiscoveryManagerRequest(ClientRequest request, int clientRequestId,
791 int transactionId, ClientInfo clientInfo) {
Paul Hue4f5f252023-02-16 21:13:47 +0800792 clientInfo.unregisterMdnsListenerFromRequest(request);
Paul Hud44e1b72023-06-16 02:07:42 +0000793 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hue4f5f252023-02-16 21:13:47 +0800794 }
795
Irfan Sheriff75006652012-04-17 23:15:29 -0700796 @Override
797 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900798 final ClientInfo clientInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000799 final int transactionId;
800 final int clientRequestId = msg.arg2;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900801 final ListenerArgs args;
Yuyang Huang33fa4d22023-02-14 22:59:37 +0900802 final OffloadEngineInfo offloadEngineInfo;
Irfan Sheriff75006652012-04-17 23:15:29 -0700803 switch (msg.what) {
Paul Hu75069ed2023-01-14 00:31:09 +0800804 case NsdManager.DISCOVER_SERVICES: {
paulhub2225702021-11-17 09:35:33 +0800805 if (DBG) Log.d(TAG, "Discover services");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900806 args = (ListenerArgs) msg.obj;
807 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000808 // If the binder death notification for a INsdManagerCallback was received
809 // before any calls are received by NsdService, the clientInfo would be
810 // cleared and cause NPE. Add a null check here to prevent this corner case.
811 if (clientInfo == null) {
812 Log.e(TAG, "Unknown connector in discovery");
813 break;
814 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700815
816 if (requestLimitReached(clientInfo)) {
Paul Hu508a0122023-09-11 15:31:33 +0800817 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
818 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
Irfan Sheriff75006652012-04-17 23:15:29 -0700819 break;
820 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700821
Paul Hu23fa2022023-01-13 22:57:24 +0800822 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +0000823 transactionId = getUniqueId();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900824 final Pair<String, String> typeAndSubtype =
825 parseTypeAndSubtype(info.getServiceType());
826 final String serviceType = typeAndSubtype == null
827 ? null : typeAndSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800828 if (clientInfo.mUseJavaBackend
829 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900830 || useDiscoveryManagerForType(serviceType)) {
Paul Hu23fa2022023-01-13 22:57:24 +0800831 if (serviceType == null) {
Paul Hu508a0122023-09-11 15:31:33 +0800832 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
833 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Paul Hu23fa2022023-01-13 22:57:24 +0800834 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700835 }
Paul Hu23fa2022023-01-13 22:57:24 +0800836
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900837 final String listenServiceType = serviceType + ".local";
Paul Hu23fa2022023-01-13 22:57:24 +0800838 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +0000839 final MdnsListener listener = new DiscoveryListener(clientRequestId,
840 transactionId, info, listenServiceType);
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900841 final MdnsSearchOptions.Builder optionsBuilder =
842 MdnsSearchOptions.newBuilder()
843 .setNetwork(info.getNetwork())
Yuyang Huangff963222023-06-01 18:42:42 +0900844 .setRemoveExpiredService(true)
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900845 .setIsPassiveMode(true);
846 if (typeAndSubtype.second != null) {
847 // The parsing ensures subtype starts with an underscore.
848 // MdnsSearchOptions expects the underscore to not be present.
849 optionsBuilder.addSubtype(typeAndSubtype.second.substring(1));
850 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900851 mMdnsDiscoveryManager.registerListener(
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900852 listenServiceType, listener, optionsBuilder.build());
Paul Hu508a0122023-09-11 15:31:33 +0800853 final ClientRequest request = storeDiscoveryManagerRequestMap(
854 clientRequestId, transactionId, listener, clientInfo,
855 info.getNetwork());
856 clientInfo.onDiscoverServicesStarted(clientRequestId, info, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000857 clientInfo.log("Register a DiscoveryListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +0000858 + " for service type:" + listenServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -0700859 } else {
Paul Hu23fa2022023-01-13 22:57:24 +0800860 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +0000861 if (discoverServices(transactionId, info)) {
Paul Hu23fa2022023-01-13 22:57:24 +0800862 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +0000863 Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId
Paul Hu23fa2022023-01-13 22:57:24 +0800864 + info.getServiceType());
865 }
Paul Hu508a0122023-09-11 15:31:33 +0800866 final ClientRequest request = storeLegacyRequestMap(clientRequestId,
867 transactionId, clientInfo, msg.what,
868 mClock.elapsedRealtime());
Paul Hu812e9212023-06-20 06:24:53 +0000869 clientInfo.onDiscoverServicesStarted(
Paul Hu508a0122023-09-11 15:31:33 +0800870 clientRequestId, info, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800871 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000872 stopServiceDiscovery(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800873 clientInfo.onDiscoverServicesFailedImmediately(clientRequestId,
874 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Paul Hu23fa2022023-01-13 22:57:24 +0800875 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700876 }
877 break;
Paul Hu75069ed2023-01-14 00:31:09 +0800878 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900879 case NsdManager.STOP_DISCOVERY: {
paulhub2225702021-11-17 09:35:33 +0800880 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900881 args = (ListenerArgs) msg.obj;
882 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000883 // If the binder death notification for a INsdManagerCallback was received
884 // before any calls are received by NsdService, the clientInfo would be
885 // cleared and cause NPE. Add a null check here to prevent this corner case.
886 if (clientInfo == null) {
887 Log.e(TAG, "Unknown connector in stop discovery");
888 break;
889 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700890
Paul Hud44e1b72023-06-16 02:07:42 +0000891 final ClientRequest request =
892 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900893 if (request == null) {
894 Log.e(TAG, "Unknown client request in STOP_DISCOVERY");
Irfan Sheriff75006652012-04-17 23:15:29 -0700895 break;
896 }
Paul Hud44e1b72023-06-16 02:07:42 +0000897 transactionId = request.mTransactionId;
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +0900898 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
899 // point, so this needs to check the type of the original request to
900 // unregister instead of looking at the flag value.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900901 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +0000902 stopDiscoveryManagerRequest(
903 request, clientRequestId, transactionId, clientInfo);
Paul Hu812e9212023-06-20 06:24:53 +0000904 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +0000905 clientInfo.log("Unregister the DiscoveryListener " + transactionId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700906 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000907 removeRequestMap(clientRequestId, transactionId, clientInfo);
908 if (stopServiceDiscovery(transactionId)) {
Paul Hu812e9212023-06-20 06:24:53 +0000909 clientInfo.onStopDiscoverySucceeded(clientRequestId, request);
Paul Hu23fa2022023-01-13 22:57:24 +0800910 } else {
911 clientInfo.onStopDiscoveryFailed(
Paul Hud44e1b72023-06-16 02:07:42 +0000912 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Paul Hu23fa2022023-01-13 22:57:24 +0800913 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700914 }
915 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900916 }
917 case NsdManager.REGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800918 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900919 args = (ListenerArgs) msg.obj;
920 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000921 // If the binder death notification for a INsdManagerCallback was received
922 // before any calls are received by NsdService, the clientInfo would be
923 // cleared and cause NPE. Add a null check here to prevent this corner case.
924 if (clientInfo == null) {
925 Log.e(TAG, "Unknown connector in registration");
926 break;
927 }
928
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700929 if (requestLimitReached(clientInfo)) {
Paul Hu508a0122023-09-11 15:31:33 +0800930 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
931 NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700932 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700933 }
934
Paul Hud44e1b72023-06-16 02:07:42 +0000935 transactionId = getUniqueId();
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900936 final NsdServiceInfo serviceInfo = args.serviceInfo;
937 final String serviceType = serviceInfo.getServiceType();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900938 final Pair<String, String> typeSubtype = parseTypeAndSubtype(serviceType);
939 final String registerServiceType = typeSubtype == null
940 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +0800941 if (clientInfo.mUseJavaBackend
942 || mDeps.isMdnsAdvertiserEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +0900943 || useAdvertiserForType(registerServiceType)) {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900944 if (registerServiceType == null) {
945 Log.e(TAG, "Invalid service type: " + serviceType);
Paul Hu508a0122023-09-11 15:31:33 +0800946 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
947 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900948 break;
949 }
950 serviceInfo.setServiceType(registerServiceType);
951 serviceInfo.setServiceName(truncateServiceName(
952 serviceInfo.getServiceName()));
953
954 maybeStartMonitoringSockets();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +0900955 // TODO: pass in the subtype as well. Including the subtype in the
956 // service type would generate service instance names like
957 // Name._subtype._sub._type._tcp, which is incorrect
958 // (it should be Name._type._tcp).
Paul Hud44e1b72023-06-16 02:07:42 +0000959 mAdvertiser.addService(transactionId, serviceInfo, typeSubtype.second);
960 storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +0900961 serviceInfo.getNetwork());
Irfan Sheriff75006652012-04-17 23:15:29 -0700962 } else {
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900963 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +0000964 if (registerService(transactionId, serviceInfo)) {
965 if (DBG) {
966 Log.d(TAG, "Register " + clientRequestId
967 + " " + transactionId);
968 }
Paul Hua6bc4632023-06-26 01:18:29 +0000969 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
970 msg.what, mClock.elapsedRealtime());
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900971 // Return success after mDns reports success
972 } else {
Paul Hud44e1b72023-06-16 02:07:42 +0000973 unregisterService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +0800974 clientInfo.onRegisterServiceFailedImmediately(clientRequestId,
975 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +0900976 }
977
Irfan Sheriff75006652012-04-17 23:15:29 -0700978 }
979 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900980 }
981 case NsdManager.UNREGISTER_SERVICE: {
paulhub2225702021-11-17 09:35:33 +0800982 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900983 args = (ListenerArgs) msg.obj;
984 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000985 // If the binder death notification for a INsdManagerCallback was received
986 // before any calls are received by NsdService, the clientInfo would be
987 // cleared and cause NPE. Add a null check here to prevent this corner case.
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900988 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800989 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700990 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700991 }
Paul Hud44e1b72023-06-16 02:07:42 +0000992 final ClientRequest request =
993 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +0900994 if (request == null) {
995 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE");
996 break;
997 }
Paul Hud44e1b72023-06-16 02:07:42 +0000998 transactionId = request.mTransactionId;
999 removeRequestMap(clientRequestId, transactionId, clientInfo);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001000
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001001 // Note isMdnsAdvertiserEnabled may have changed to false at this point,
1002 // so this needs to check the type of the original request to unregister
1003 // instead of looking at the flag value.
1004 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +08001005 final AdvertiserMetrics metrics =
1006 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00001007 mAdvertiser.removeService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08001008 clientInfo.onUnregisterServiceSucceeded(
1009 clientRequestId, request, metrics);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001010 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001011 if (unregisterService(transactionId)) {
Paul Hu508a0122023-09-11 15:31:33 +08001012 clientInfo.onUnregisterServiceSucceeded(clientRequestId, request,
Paul Hu043bcd42023-07-14 16:38:25 +08001013 new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */,
1014 NO_PACKET /* sentPacketCount */,
1015 0 /* conflictDuringProbingCount */,
1016 0 /* conflictAfterProbingCount */));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001017 } else {
1018 clientInfo.onUnregisterServiceFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001019 clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001020 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001021 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001022 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001023 }
Paul Hu75069ed2023-01-14 00:31:09 +08001024 case NsdManager.RESOLVE_SERVICE: {
paulhub2225702021-11-17 09:35:33 +08001025 if (DBG) Log.d(TAG, "Resolve service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001026 args = (ListenerArgs) msg.obj;
1027 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +00001028 // If the binder death notification for a INsdManagerCallback was received
1029 // before any calls are received by NsdService, the clientInfo would be
1030 // cleared and cause NPE. Add a null check here to prevent this corner case.
1031 if (clientInfo == null) {
1032 Log.e(TAG, "Unknown connector in resolution");
1033 break;
1034 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001035
Paul Hu75069ed2023-01-14 00:31:09 +08001036 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001037 transactionId = getUniqueId();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001038 final Pair<String, String> typeSubtype =
1039 parseTypeAndSubtype(info.getServiceType());
1040 final String serviceType = typeSubtype == null
1041 ? null : typeSubtype.first;
Paul Hu2e0a88c2023-03-09 16:05:01 +08001042 if (clientInfo.mUseJavaBackend
1043 || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001044 || useDiscoveryManagerForType(serviceType)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001045 if (serviceType == null) {
Paul Hu508a0122023-09-11 15:31:33 +08001046 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1047 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001048 break;
1049 }
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001050 final String resolveServiceType = serviceType + ".local";
Paul Hu75069ed2023-01-14 00:31:09 +08001051
1052 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001053 final MdnsListener listener = new ResolutionListener(clientRequestId,
1054 transactionId, info, resolveServiceType);
Paul Hu75069ed2023-01-14 00:31:09 +08001055 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1056 .setNetwork(info.getNetwork())
1057 .setIsPassiveMode(true)
Remi NGUYEN VANbb62b1d2023-02-27 12:18:27 +09001058 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001059 .setRemoveExpiredService(true)
Paul Hu75069ed2023-01-14 00:31:09 +08001060 .build();
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001061 mMdnsDiscoveryManager.registerListener(
1062 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001063 storeDiscoveryManagerRequestMap(clientRequestId, transactionId,
1064 listener, clientInfo, info.getNetwork());
1065 clientInfo.log("Register a ResolutionListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001066 + " for service type:" + resolveServiceType);
Irfan Sheriff75006652012-04-17 23:15:29 -07001067 } else {
Paul Hu75069ed2023-01-14 00:31:09 +08001068 if (clientInfo.mResolvedService != null) {
Paul Hu508a0122023-09-11 15:31:33 +08001069 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1070 NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001071 break;
1072 }
1073
1074 maybeStartDaemon();
Paul Hud44e1b72023-06-16 02:07:42 +00001075 if (resolveService(transactionId, info)) {
Paul Hu75069ed2023-01-14 00:31:09 +08001076 clientInfo.mResolvedService = new NsdServiceInfo();
Paul Hua6bc4632023-06-26 01:18:29 +00001077 storeLegacyRequestMap(clientRequestId, transactionId, clientInfo,
1078 msg.what, mClock.elapsedRealtime());
Paul Hu75069ed2023-01-14 00:31:09 +08001079 } else {
Paul Hu508a0122023-09-11 15:31:33 +08001080 clientInfo.onResolveServiceFailedImmediately(clientRequestId,
1081 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */);
Paul Hu75069ed2023-01-14 00:31:09 +08001082 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001083 }
1084 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001085 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001086 case NsdManager.STOP_RESOLUTION: {
Paul Hub58deb72022-12-26 09:24:42 +00001087 if (DBG) Log.d(TAG, "Stop service resolution");
1088 args = (ListenerArgs) msg.obj;
1089 clientInfo = mClients.get(args.connector);
1090 // If the binder death notification for a INsdManagerCallback was received
1091 // before any calls are received by NsdService, the clientInfo would be
1092 // cleared and cause NPE. Add a null check here to prevent this corner case.
1093 if (clientInfo == null) {
1094 Log.e(TAG, "Unknown connector in stop resolution");
1095 break;
1096 }
1097
Paul Hud44e1b72023-06-16 02:07:42 +00001098 final ClientRequest request =
1099 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001100 if (request == null) {
1101 Log.e(TAG, "Unknown client request in STOP_RESOLUTION");
1102 break;
1103 }
Paul Hud44e1b72023-06-16 02:07:42 +00001104 transactionId = request.mTransactionId;
Paul Hue4f5f252023-02-16 21:13:47 +08001105 // Note isMdnsDiscoveryManagerEnabled may have changed to false at this
1106 // point, so this needs to check the type of the original request to
1107 // unregister instead of looking at the flag value.
1108 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001109 stopDiscoveryManagerRequest(
1110 request, clientRequestId, transactionId, clientInfo);
Paul Hu60149052023-07-31 14:26:08 +08001111 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001112 clientInfo.log("Unregister the ResolutionListener " + transactionId);
Paul Hub58deb72022-12-26 09:24:42 +00001113 } else {
Paul Hud44e1b72023-06-16 02:07:42 +00001114 removeRequestMap(clientRequestId, transactionId, clientInfo);
1115 if (stopResolveService(transactionId)) {
Paul Hu60149052023-07-31 14:26:08 +08001116 clientInfo.onStopResolutionSucceeded(clientRequestId, request);
Paul Hue4f5f252023-02-16 21:13:47 +08001117 } else {
1118 clientInfo.onStopResolutionFailed(
Paul Hud44e1b72023-06-16 02:07:42 +00001119 clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
Paul Hue4f5f252023-02-16 21:13:47 +08001120 }
1121 clientInfo.mResolvedService = null;
Paul Hub58deb72022-12-26 09:24:42 +00001122 }
Paul Hub58deb72022-12-26 09:24:42 +00001123 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001124 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001125 case NsdManager.REGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001126 if (DBG) Log.d(TAG, "Register a service callback");
1127 args = (ListenerArgs) msg.obj;
1128 clientInfo = mClients.get(args.connector);
1129 // If the binder death notification for a INsdManagerCallback was received
1130 // before any calls are received by NsdService, the clientInfo would be
1131 // cleared and cause NPE. Add a null check here to prevent this corner case.
1132 if (clientInfo == null) {
1133 Log.e(TAG, "Unknown connector in callback registration");
1134 break;
1135 }
1136
Paul Hu30bd70d2023-02-07 13:20:56 +00001137 final NsdServiceInfo info = args.serviceInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001138 transactionId = getUniqueId();
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001139 final Pair<String, String> typeAndSubtype =
1140 parseTypeAndSubtype(info.getServiceType());
1141 final String serviceType = typeAndSubtype == null
1142 ? null : typeAndSubtype.first;
Paul Hu30bd70d2023-02-07 13:20:56 +00001143 if (serviceType == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001144 clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId,
Paul Hu30bd70d2023-02-07 13:20:56 +00001145 NsdManager.FAILURE_BAD_PARAMETERS);
Paul Hu18aeccc2022-12-27 08:48:48 +00001146 break;
1147 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001148 final String resolveServiceType = serviceType + ".local";
Paul Hu18aeccc2022-12-27 08:48:48 +00001149
Paul Hu30bd70d2023-02-07 13:20:56 +00001150 maybeStartMonitoringSockets();
Paul Hud44e1b72023-06-16 02:07:42 +00001151 final MdnsListener listener = new ServiceInfoListener(clientRequestId,
1152 transactionId, info, resolveServiceType);
Paul Hu30bd70d2023-02-07 13:20:56 +00001153 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
1154 .setNetwork(info.getNetwork())
1155 .setIsPassiveMode(true)
1156 .setResolveInstanceName(info.getServiceName())
Yuyang Huangff963222023-06-01 18:42:42 +09001157 .setRemoveExpiredService(true)
Paul Hu30bd70d2023-02-07 13:20:56 +00001158 .build();
1159 mMdnsDiscoveryManager.registerListener(
1160 resolveServiceType, listener, options);
Paul Hud44e1b72023-06-16 02:07:42 +00001161 storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener,
1162 clientInfo, info.getNetwork());
Paul Huddce5912023-08-01 10:26:49 +08001163 clientInfo.onServiceInfoCallbackRegistered(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00001164 clientInfo.log("Register a ServiceInfoListener " + transactionId
Paul Hub2e67d32023-04-18 05:50:14 +00001165 + " for service type:" + resolveServiceType);
Paul Hu18aeccc2022-12-27 08:48:48 +00001166 break;
Paul Hu30bd70d2023-02-07 13:20:56 +00001167 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001168 case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
Paul Hu18aeccc2022-12-27 08:48:48 +00001169 if (DBG) Log.d(TAG, "Unregister a service callback");
1170 args = (ListenerArgs) msg.obj;
1171 clientInfo = mClients.get(args.connector);
1172 // If the binder death notification for a INsdManagerCallback was received
1173 // before any calls are received by NsdService, the clientInfo would be
1174 // cleared and cause NPE. Add a null check here to prevent this corner case.
1175 if (clientInfo == null) {
1176 Log.e(TAG, "Unknown connector in callback unregistration");
1177 break;
1178 }
1179
Paul Hud44e1b72023-06-16 02:07:42 +00001180 final ClientRequest request =
1181 clientInfo.mClientRequests.get(clientRequestId);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001182 if (request == null) {
Paul Hu30bd70d2023-02-07 13:20:56 +00001183 Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001184 break;
1185 }
Paul Hud44e1b72023-06-16 02:07:42 +00001186 transactionId = request.mTransactionId;
Paul Hu30bd70d2023-02-07 13:20:56 +00001187 if (request instanceof DiscoveryManagerRequest) {
Paul Hud44e1b72023-06-16 02:07:42 +00001188 stopDiscoveryManagerRequest(
1189 request, clientRequestId, transactionId, clientInfo);
Paul Huddce5912023-08-01 10:26:49 +08001190 clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request);
Paul Hud44e1b72023-06-16 02:07:42 +00001191 clientInfo.log("Unregister the ServiceInfoListener " + transactionId);
Paul Hu18aeccc2022-12-27 08:48:48 +00001192 } else {
Paul Hu30bd70d2023-02-07 13:20:56 +00001193 loge("Unregister failed with non-DiscoveryManagerRequest.");
Paul Hu18aeccc2022-12-27 08:48:48 +00001194 }
Paul Hu18aeccc2022-12-27 08:48:48 +00001195 break;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001196 }
paulhu2b9ed952022-02-10 21:58:32 +08001197 case MDNS_SERVICE_EVENT:
1198 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +09001199 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001200 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001201 break;
Paul Hu019621e2023-01-13 23:26:49 +08001202 case MDNS_DISCOVERY_MANAGER_EVENT:
1203 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
1204 return NOT_HANDLED;
1205 }
1206 break;
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001207 case NsdManager.REGISTER_OFFLOAD_ENGINE:
1208 offloadEngineInfo = (OffloadEngineInfo) msg.obj;
1209 // TODO: Limits the number of registrations created by a given class.
1210 mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
1211 offloadEngineInfo);
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001212 sendAllOffloadServiceInfos(offloadEngineInfo);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001213 break;
1214 case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
1215 mOffloadEngines.unregister((IOffloadEngine) msg.obj);
1216 break;
Irfan Sheriff75006652012-04-17 23:15:29 -07001217 default:
Hugo Benichif0c84092017-04-05 14:43:29 +09001218 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001219 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001220 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -07001221 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001222
Paul Hud44e1b72023-06-16 02:07:42 +00001223 private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001224 NsdServiceInfo servInfo;
Paul Hud44e1b72023-06-16 02:07:42 +00001225 ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001226 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00001227 Log.e(TAG, String.format(
1228 "transactionId %d for %d has no client mapping", transactionId, code));
Hugo Benichif0c84092017-04-05 14:43:29 +09001229 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001230 }
1231
1232 /* This goes in response as msg.arg2 */
Paul Hud44e1b72023-06-16 02:07:42 +00001233 int clientRequestId = clientInfo.getClientRequestId(transactionId);
1234 if (clientRequestId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -07001235 // This can happen because of race conditions. For example,
1236 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
1237 // and we may get in this situation.
Paul Hud44e1b72023-06-16 02:07:42 +00001238 Log.d(TAG, String.format("%d for transactionId %d that is no longer active",
1239 code, transactionId));
Hugo Benichif0c84092017-04-05 14:43:29 +09001240 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001241 }
Paul Hu812e9212023-06-20 06:24:53 +00001242 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1243 if (request == null) {
1244 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1245 return false;
1246 }
Hugo Benichi32be63d2017-04-05 14:06:11 +09001247 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00001248 Log.d(TAG, String.format(
1249 "MDns service event code:%d transactionId=%d", code, transactionId));
Hugo Benichi32be63d2017-04-05 14:06:11 +09001250 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001251 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +08001252 case IMDnsEventListener.SERVICE_FOUND: {
1253 final DiscoveryInfo info = (DiscoveryInfo) obj;
1254 final String name = info.serviceName;
1255 final String type = info.registrationType;
1256 servInfo = new NsdServiceInfo(name, type);
1257 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001258 if (foundNetId == 0L) {
1259 // Ignore services that do not have a Network: they are not usable
1260 // by apps, as they would need privileged permissions to use
1261 // interfaces that do not have an associated Network.
1262 break;
1263 }
Remi NGUYEN VAN643edb62023-01-23 19:14:57 +09001264 if (foundNetId == INetd.DUMMY_NET_ID) {
1265 // Ignore services on the dummy0 interface: they are only seen when
1266 // discovering locally advertised services, and are not reachable
1267 // through that interface.
1268 break;
1269 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001270 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001271
1272 clientInfo.onServiceFound(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001273 break;
paulhu2b9ed952022-02-10 21:58:32 +08001274 }
1275 case IMDnsEventListener.SERVICE_LOST: {
1276 final DiscoveryInfo info = (DiscoveryInfo) obj;
1277 final String name = info.serviceName;
1278 final String type = info.registrationType;
1279 final int lostNetId = info.netId;
1280 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001281 // The network could be set to null (netId 0) if it was torn down when the
1282 // service is lost
1283 // TODO: avoid returning null in that case, possibly by remembering
1284 // found services on the same interface index and their network at the time
1285 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Paul Hu812e9212023-06-20 06:24:53 +00001286 clientInfo.onServiceLost(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001287 break;
paulhu2b9ed952022-02-10 21:58:32 +08001288 }
1289 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Paul Hu812e9212023-06-20 06:24:53 +00001290 clientInfo.onDiscoverServicesFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001291 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1292 transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001293 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001294 break;
paulhu2b9ed952022-02-10 21:58:32 +08001295 case IMDnsEventListener.SERVICE_REGISTERED: {
1296 final RegistrationInfo info = (RegistrationInfo) obj;
1297 final String name = info.serviceName;
1298 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Paul Hu508a0122023-09-11 15:31:33 +08001299 clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001300 break;
paulhu2b9ed952022-02-10 21:58:32 +08001301 }
1302 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Paul Hu777ed052023-06-19 13:35:15 +00001303 clientInfo.onRegisterServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001304 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1305 transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00001306 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001307 break;
paulhu2b9ed952022-02-10 21:58:32 +08001308 case IMDnsEventListener.SERVICE_RESOLVED: {
1309 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001310 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +08001311 final String fullName = info.serviceFullName;
1312 while (index < fullName.length() && fullName.charAt(index) != '.') {
1313 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -07001314 ++index;
1315 }
1316 ++index;
1317 }
paulhu2b9ed952022-02-10 21:58:32 +08001318 if (index >= fullName.length()) {
1319 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001320 break;
1321 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001322
paulhube186602022-04-12 07:18:23 +00001323 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +08001324 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001325 String type = rest.replace(".local.", "");
1326
Paul Hu30bd70d2023-02-07 13:20:56 +00001327 final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
Paul Hu18aeccc2022-12-27 08:48:48 +00001328 serviceInfo.setServiceName(name);
1329 serviceInfo.setServiceType(type);
1330 serviceInfo.setPort(info.port);
1331 serviceInfo.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001332 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001333
Paul Hud44e1b72023-06-16 02:07:42 +00001334 stopResolveService(transactionId);
1335 removeRequestMap(clientRequestId, transactionId, clientInfo);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001336
Paul Hud44e1b72023-06-16 02:07:42 +00001337 final int transactionId2 = getUniqueId();
1338 if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) {
1339 storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo,
Paul Hua6bc4632023-06-26 01:18:29 +00001340 NsdManager.RESOLVE_SERVICE, request.mStartTimeMs);
Vinit Deshapnde4429e872013-11-12 15:36:37 -08001341 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001342 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001343 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1344 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001345 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001346 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001347 }
1348 break;
paulhu2b9ed952022-02-10 21:58:32 +08001349 }
1350 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001351 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001352 stopResolveService(transactionId);
1353 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001354 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001355 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1356 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001357 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001358 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001359 break;
paulhu2b9ed952022-02-10 21:58:32 +08001360 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001361 /* NNN resolveId errorCode */
Paul Hud44e1b72023-06-16 02:07:42 +00001362 stopGetAddrInfo(transactionId);
1363 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hua6bc4632023-06-26 01:18:29 +00001364 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001365 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1366 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001367 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu30bd70d2023-02-07 13:20:56 +00001368 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001369 break;
paulhu2b9ed952022-02-10 21:58:32 +08001370 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001371 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +08001372 final GetAddressInfo info = (GetAddressInfo) obj;
1373 final String address = info.address;
1374 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001375 InetAddress serviceHost = null;
1376 try {
paulhu2b9ed952022-02-10 21:58:32 +08001377 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001378 } catch (UnknownHostException e) {
1379 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
1380 }
1381
1382 // If the resolved service is on an interface without a network, consider it
1383 // as a failure: it would not be usable by apps as they would need
1384 // privileged permissions.
Paul Hu30bd70d2023-02-07 13:20:56 +00001385 if (netId != NETID_UNSET && serviceHost != null) {
1386 clientInfo.mResolvedService.setHost(serviceHost);
1387 setServiceNetworkForCallback(clientInfo.mResolvedService,
1388 netId, info.interfaceIdx);
1389 clientInfo.onResolveServiceSucceeded(
Paul Hua6bc4632023-06-26 01:18:29 +00001390 clientRequestId, clientInfo.mResolvedService, request);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001391 } else {
Paul Hua6bc4632023-06-26 01:18:29 +00001392 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001393 NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */,
1394 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001395 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001396 }
Paul Hud44e1b72023-06-16 02:07:42 +00001397 stopGetAddrInfo(transactionId);
1398 removeRequestMap(clientRequestId, transactionId, clientInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001399 clientInfo.mResolvedService = null;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001400 break;
paulhu2b9ed952022-02-10 21:58:32 +08001401 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001402 default:
Hugo Benichif0c84092017-04-05 14:43:29 +09001403 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001404 }
Hugo Benichif0c84092017-04-05 14:43:29 +09001405 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -07001406 }
Paul Hu019621e2023-01-13 23:26:49 +08001407
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001408 @Nullable
1409 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
1410 final MdnsEvent event, int code) {
Paul Hu019621e2023-01-13 23:26:49 +08001411 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001412 final String[] typeArray = serviceInfo.getServiceType();
1413 final String joinedType;
1414 if (typeArray.length == 0
1415 || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
1416 Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
1417 + Arrays.toString(typeArray));
1418 return null;
1419 } else {
1420 joinedType = TextUtils.join(".",
1421 Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
1422 }
1423 final String serviceType;
1424 switch (code) {
1425 case NsdManager.SERVICE_FOUND:
1426 case NsdManager.SERVICE_LOST:
1427 // For consistency with historical behavior, discovered service types have
1428 // a dot at the end.
1429 serviceType = joinedType + ".";
1430 break;
1431 case RESOLVE_SERVICE_SUCCEEDED:
1432 // For consistency with historical behavior, resolved service types have
1433 // a dot at the beginning.
1434 serviceType = "." + joinedType;
1435 break;
1436 default:
1437 serviceType = joinedType;
1438 break;
1439 }
Paul Hu019621e2023-01-13 23:26:49 +08001440 final String serviceName = serviceInfo.getServiceInstanceName();
1441 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
1442 final Network network = serviceInfo.getNetwork();
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001443 // In MdnsDiscoveryManagerEvent, the Network can be null which means it is a
1444 // network for Tethering interface. In other words, the network == null means the
1445 // network has netId = INetd.LOCAL_NET_ID.
Paul Hu019621e2023-01-13 23:26:49 +08001446 setServiceNetworkForCallback(
1447 servInfo,
Yuyang Huang3bee9d42023-04-04 13:00:54 +09001448 network == null ? INetd.LOCAL_NET_ID : network.netId,
Paul Hu019621e2023-01-13 23:26:49 +08001449 serviceInfo.getInterfaceIndex());
1450 return servInfo;
1451 }
1452
1453 private boolean handleMdnsDiscoveryManagerEvent(
1454 int transactionId, int code, Object obj) {
Paul Hud44e1b72023-06-16 02:07:42 +00001455 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Paul Hu019621e2023-01-13 23:26:49 +08001456 if (clientInfo == null) {
1457 Log.e(TAG, String.format(
1458 "id %d for %d has no client mapping", transactionId, code));
1459 return false;
1460 }
1461
1462 final MdnsEvent event = (MdnsEvent) obj;
Paul Hud44e1b72023-06-16 02:07:42 +00001463 final int clientRequestId = event.mClientRequestId;
Paul Hubad6fe92023-07-24 21:25:22 +08001464 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
1465 if (request == null) {
1466 Log.e(TAG, "Unknown client request. clientRequestId=" + clientRequestId);
1467 return false;
1468 }
1469
1470 // Deal with the discovery sent callback
1471 if (code == DISCOVERY_QUERY_SENT_CALLBACK) {
1472 request.onQuerySent();
1473 return true;
1474 }
1475
1476 // Deal with other callbacks.
Remi NGUYEN VAN2f82fcd2023-05-10 13:24:53 +09001477 final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
1478 // Errors are already logged if null
1479 if (info == null) return false;
Paul Hu83ec7f42023-06-07 18:04:09 +08001480 mServiceLogs.log(String.format(
1481 "MdnsDiscoveryManager event code=%s transactionId=%d",
1482 NsdManager.nameOf(code), transactionId));
Paul Hu019621e2023-01-13 23:26:49 +08001483 switch (code) {
1484 case NsdManager.SERVICE_FOUND:
Paul Hu812e9212023-06-20 06:24:53 +00001485 clientInfo.onServiceFound(clientRequestId, info, request);
Paul Hu319751a2023-01-13 23:56:34 +08001486 break;
1487 case NsdManager.SERVICE_LOST:
Paul Hu812e9212023-06-20 06:24:53 +00001488 clientInfo.onServiceLost(clientRequestId, info, request);
Paul Hu019621e2023-01-13 23:26:49 +08001489 break;
Paul Hu75069ed2023-01-14 00:31:09 +08001490 case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
1491 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
Paul Hu75069ed2023-01-14 00:31:09 +08001492 info.setPort(serviceInfo.getPort());
1493
1494 Map<String, String> attrs = serviceInfo.getAttributes();
1495 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1496 final String key = kv.getKey();
1497 try {
1498 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1499 } catch (IllegalArgumentException e) {
1500 Log.e(TAG, "Invalid attribute", e);
1501 }
1502 }
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001503 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu2b865912023-03-06 14:27:53 +08001504 if (addresses.size() != 0) {
1505 info.setHostAddresses(addresses);
Paul Hua6bc4632023-06-26 01:18:29 +00001506 request.setServiceFromCache(event.mIsServiceFromCache);
1507 clientInfo.onResolveServiceSucceeded(clientRequestId, info, request);
Paul Hu2b865912023-03-06 14:27:53 +08001508 } else {
1509 // No address. Notify resolution failure.
Paul Hua6bc4632023-06-26 01:18:29 +00001510 clientInfo.onResolveServiceFailed(clientRequestId,
Paul Hu508a0122023-09-11 15:31:33 +08001511 NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */,
1512 transactionId,
Paul Hua6bc4632023-06-26 01:18:29 +00001513 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hu75069ed2023-01-14 00:31:09 +08001514 }
1515
1516 // Unregister the listener immediately like IMDnsEventListener design
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09001517 if (!(request instanceof DiscoveryManagerRequest)) {
1518 Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event");
1519 break;
1520 }
Paul Hud44e1b72023-06-16 02:07:42 +00001521 stopDiscoveryManagerRequest(
1522 request, clientRequestId, transactionId, clientInfo);
Paul Hu75069ed2023-01-14 00:31:09 +08001523 break;
1524 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001525 case NsdManager.SERVICE_UPDATED: {
1526 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
1527 info.setPort(serviceInfo.getPort());
1528
1529 Map<String, String> attrs = serviceInfo.getAttributes();
1530 for (Map.Entry<String, String> kv : attrs.entrySet()) {
1531 final String key = kv.getKey();
1532 try {
1533 info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
1534 } catch (IllegalArgumentException e) {
1535 Log.e(TAG, "Invalid attribute", e);
1536 }
1537 }
1538
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001539 final List<InetAddress> addresses = getInetAddresses(serviceInfo);
Paul Hu30bd70d2023-02-07 13:20:56 +00001540 info.setHostAddresses(addresses);
Paul Huddce5912023-08-01 10:26:49 +08001541 clientInfo.onServiceUpdated(clientRequestId, info, request);
1542 // Set the ServiceFromCache flag only if the service is actually being
1543 // retrieved from the cache. This flag should not be overridden by later
1544 // service updates, which may not be cached.
1545 if (event.mIsServiceFromCache) {
1546 request.setServiceFromCache(true);
1547 }
Paul Hu30bd70d2023-02-07 13:20:56 +00001548 break;
1549 }
1550 case NsdManager.SERVICE_UPDATED_LOST:
Paul Huddce5912023-08-01 10:26:49 +08001551 clientInfo.onServiceUpdatedLost(clientRequestId, request);
Paul Hu30bd70d2023-02-07 13:20:56 +00001552 break;
Paul Hu019621e2023-01-13 23:26:49 +08001553 default:
1554 return false;
1555 }
1556 return true;
1557 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001558 }
1559 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001560
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001561 @NonNull
1562 private static List<InetAddress> getInetAddresses(@NonNull MdnsServiceInfo serviceInfo) {
1563 final List<String> v4Addrs = serviceInfo.getIpv4Addresses();
1564 final List<String> v6Addrs = serviceInfo.getIpv6Addresses();
1565 final List<InetAddress> addresses = new ArrayList<>(v4Addrs.size() + v6Addrs.size());
1566 for (String ipv4Address : v4Addrs) {
1567 try {
1568 addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
1569 } catch (IllegalArgumentException e) {
1570 Log.wtf(TAG, "Invalid ipv4 address", e);
1571 }
1572 }
1573 for (String ipv6Address : v6Addrs) {
1574 try {
Yuyang Huanga6a6ff92023-04-24 13:33:34 +09001575 final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
1576 ipv6Address);
1577 addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
1578 } catch (IllegalArgumentException e) {
Yuyang Huangaa0e9602023-03-17 12:43:09 +09001579 Log.wtf(TAG, "Invalid ipv6 address", e);
1580 }
1581 }
1582 return addresses;
1583 }
1584
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001585 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
1586 switch (netId) {
1587 case NETID_UNSET:
1588 info.setNetwork(null);
1589 break;
1590 case INetd.LOCAL_NET_ID:
1591 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
1592 // visible / usable for apps, so do not return it. Store the interface
1593 // index instead, so at least if the client tries to resolve the service
1594 // with that NsdServiceInfo, it will be done on the same interface.
1595 // If they recreate the NsdServiceInfo themselves, resolution would be
1596 // done on all interfaces as before T, which should also work.
1597 info.setNetwork(null);
1598 info.setInterfaceIndex(ifaceIdx);
1599 break;
1600 default:
1601 info.setNetwork(new Network(netId));
1602 }
1603 }
1604
paulhube186602022-04-12 07:18:23 +00001605 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
1606 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
1607 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
1608 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
1609 private String unescape(String s) {
1610 StringBuilder sb = new StringBuilder(s.length());
1611 for (int i = 0; i < s.length(); ++i) {
1612 char c = s.charAt(i);
1613 if (c == '\\') {
1614 if (++i >= s.length()) {
1615 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1616 break;
1617 }
1618 c = s.charAt(i);
1619 if (c != '.' && c != '\\') {
1620 if (i + 2 >= s.length()) {
1621 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
1622 break;
1623 }
1624 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
1625 + (s.charAt(i + 2) - '0'));
1626 i += 2;
1627 }
1628 }
1629 sb.append(c);
1630 }
1631 return sb.toString();
1632 }
1633
Paul Hu7445e3d2023-03-03 15:14:00 +08001634 /**
1635 * Check the given service type is valid and construct it to a service type
1636 * which can use for discovery / resolution service.
1637 *
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001638 * <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 +08001639 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
1640 * underscore; they are alphanumerical characters or dashes or underscore, except the
1641 * last one that is just alphanumerical. The last label must be _tcp or _udp.
1642 *
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001643 * <p>The subtype may also be specified with a comma after the service type, for example
1644 * _type._tcp,_subtype.
1645 *
Paul Hu7445e3d2023-03-03 15:14:00 +08001646 * @param serviceType the request service type for discovery / resolution service
1647 * @return constructed service type or null if the given service type is invalid.
1648 */
1649 @Nullable
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001650 public static Pair<String, String> parseTypeAndSubtype(String serviceType) {
Paul Hu7445e3d2023-03-03 15:14:00 +08001651 if (TextUtils.isEmpty(serviceType)) return null;
1652
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001653 final String typeOrSubtypePattern = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]";
Paul Hu7445e3d2023-03-03 15:14:00 +08001654 final Pattern serviceTypePattern = Pattern.compile(
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001655 // Optional leading subtype (_subtype._type._tcp)
1656 // (?: xxx) is a non-capturing parenthesis, don't capture the dot
1657 "^(?:(" + typeOrSubtypePattern + ")\\.)?"
1658 // Actual type (_type._tcp.local)
1659 + "(" + typeOrSubtypePattern + "\\._(?:tcp|udp))"
Paul Hu7445e3d2023-03-03 15:14:00 +08001660 // Drop '.' at the end of service type that is compatible with old backend.
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001661 // e.g. allow "_type._tcp.local."
1662 + "\\.?"
1663 // Optional subtype after comma, for "_type._tcp,_subtype" format
1664 + "(?:,(" + typeOrSubtypePattern + "))?"
1665 + "$");
Paul Hu7445e3d2023-03-03 15:14:00 +08001666 final Matcher matcher = serviceTypePattern.matcher(serviceType);
1667 if (!matcher.matches()) return null;
Remi NGUYEN VANf2d06412023-05-11 19:18:44 +09001668 // Use the subtype either at the beginning or after the comma
1669 final String subtype = matcher.group(1) != null ? matcher.group(1) : matcher.group(3);
1670 return new Pair<>(matcher.group(2), subtype);
Paul Hu7445e3d2023-03-03 15:14:00 +08001671 }
1672
Hugo Benichi803a2f02017-04-24 11:35:06 +09001673 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +08001674 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Paul Hu4bd98ef2023-01-12 13:42:07 +08001675 this(ctx, handler, cleanupDelayMs, new Dependencies());
1676 }
1677
1678 @VisibleForTesting
1679 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
Luke Huang05298582021-06-13 16:52:05 +00001680 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001681 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +09001682 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -07001683 mNsdStateMachine.start();
paulhu2b9ed952022-02-10 21:58:32 +08001684 mMDnsManager = ctx.getSystemService(MDnsManager.class);
1685 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001686 mDeps = deps;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001687
Paul Hu14667de2023-04-17 22:42:47 +08001688 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001689 LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor());
Yuyang Huang700778b2023-03-08 16:17:05 +09001690 // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
Yuyang Huangfca402a2023-05-24 14:45:59 +09001691 // address events are received. When the netlink monitor starts, any IP addresses already
1692 // on the interfaces will not be seen. In practice, the network will not connect at boot
1693 // time As a result, all the netlink message should be observed if the netlink monitor
1694 // starts here.
Yuyang Huang700778b2023-03-08 16:17:05 +09001695 handler.post(mMdnsSocketProvider::startNetLinkMonitor);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001696
1697 // NsdService is started after ActivityManager (startOtherServices in SystemServer, vs.
1698 // startBootstrapServices).
1699 mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt(
1700 MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF,
1701 DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF);
1702 final ActivityManager am = ctx.getSystemService(ActivityManager.class);
1703 am.addOnUidImportanceListener(new UidImportanceListener(handler),
1704 mRunningAppActiveImportanceCutoff);
1705
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001706 mMdnsSocketClient =
Yuyang Huang7ddf2932023-08-01 18:16:30 +09001707 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider,
1708 LOGGER.forSubComponent("MdnsMultinetworkSocketClient"));
Paul Hu14667de2023-04-17 22:42:47 +08001709 mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
Yuyang Huang243d1a52023-05-23 17:26:52 +09001710 mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001711 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
Yuyang Huangb96a0712023-09-07 15:13:15 +09001712 MdnsFeatureFlags flags = new MdnsFeatureFlags.Builder().setIsMdnsOffloadFeatureEnabled(
Motomu Utsumied4e7ec2023-09-13 14:58:32 +09001713 mDeps.isTetheringFeatureNotChickenedOut(mContext,
Yuyang Huangb96a0712023-09-07 15:13:15 +09001714 MdnsFeatureFlags.NSD_FORCE_DISABLE_MDNS_OFFLOAD)).build();
Remi NGUYEN VANa8a777b2023-01-18 18:57:41 +09001715 mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
Yuyang Huangb96a0712023-09-07 15:13:15 +09001716 new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"), flags);
Paul Hu777ed052023-06-19 13:35:15 +00001717 mClock = deps.makeClock();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001718 }
1719
1720 /**
1721 * Dependencies of NsdService, for injection in tests.
1722 */
1723 @VisibleForTesting
1724 public static class Dependencies {
1725 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001726 * Check whether the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001727 *
1728 * @param context The global context information about an app environment.
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001729 * @return true if the MdnsDiscoveryManager feature is enabled.
Paul Hu4bd98ef2023-01-12 13:42:07 +08001730 */
1731 public boolean isMdnsDiscoveryManagerEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001732 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001733 MDNS_DISCOVERY_MANAGER_VERSION);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001734 }
1735
1736 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001737 * Check whether the MdnsAdvertiser feature is enabled.
1738 *
1739 * @param context The global context information about an app environment.
1740 * @return true if the MdnsAdvertiser feature is enabled.
1741 */
1742 public boolean isMdnsAdvertiserEnabled(Context context) {
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001743 return isAtLeastU() || DeviceConfigUtils.isTetheringFeatureEnabled(context,
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001744 MDNS_ADVERTISER_VERSION);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001745 }
1746
1747 /**
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001748 * Get the type allowlist flag value.
1749 * @see #MDNS_TYPE_ALLOWLIST_FLAGS
1750 */
1751 @Nullable
1752 public String getTypeAllowlistFlags() {
1753 return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
1754 MDNS_TYPE_ALLOWLIST_FLAGS, null);
1755 }
1756
1757 /**
Motomu Utsumi624aeb42023-08-15 15:52:27 +09001758 * @see DeviceConfigUtils#isTetheringFeatureEnabled
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001759 */
1760 public boolean isFeatureEnabled(Context context, String feature) {
Motomu Utsumi3e0be392023-08-15 16:32:44 +09001761 return DeviceConfigUtils.isTetheringFeatureEnabled(context, feature);
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001762 }
1763
1764 /**
Yuyang Huangb96a0712023-09-07 15:13:15 +09001765 * @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut
1766 */
Motomu Utsumied4e7ec2023-09-13 14:58:32 +09001767 public boolean isTetheringFeatureNotChickenedOut(Context context, String feature) {
1768 return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, feature);
Yuyang Huangb96a0712023-09-07 15:13:15 +09001769 }
1770
1771 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001772 * @see MdnsDiscoveryManager
1773 */
1774 public MdnsDiscoveryManager makeMdnsDiscoveryManager(
Paul Hu14667de2023-04-17 22:42:47 +08001775 @NonNull ExecutorProvider executorProvider,
Yuyang Huang243d1a52023-05-23 17:26:52 +09001776 @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog) {
1777 return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog);
Paul Hu4bd98ef2023-01-12 13:42:07 +08001778 }
1779
1780 /**
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001781 * @see MdnsAdvertiser
1782 */
1783 public MdnsAdvertiser makeMdnsAdvertiser(
1784 @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
Yuyang Huangb96a0712023-09-07 15:13:15 +09001785 @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog,
1786 MdnsFeatureFlags featureFlags) {
1787 return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog, featureFlags);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001788 }
1789
1790 /**
Paul Hu4bd98ef2023-01-12 13:42:07 +08001791 * @see MdnsSocketProvider
1792 */
Paul Hu14667de2023-04-17 22:42:47 +08001793 public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09001794 @NonNull Looper looper, @NonNull SharedLog sharedLog,
1795 @NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) {
1796 return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback);
1797 }
1798
1799 /**
1800 * @see DeviceConfig#getInt(String, String, int)
1801 */
1802 public int getDeviceConfigInt(@NonNull String config, int defaultValue) {
1803 return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue);
1804 }
1805
1806 /**
1807 * @see Binder#getCallingUid()
1808 */
1809 public int getCallingUid() {
1810 return Binder.getCallingUid();
Paul Hu4bd98ef2023-01-12 13:42:07 +08001811 }
Paul Hu777ed052023-06-19 13:35:15 +00001812
1813 /**
1814 * @see NetworkNsdReportedMetrics
1815 */
Paul Hu508a0122023-09-11 15:31:33 +08001816 public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(int clientId) {
1817 return new NetworkNsdReportedMetrics(clientId);
Paul Hu777ed052023-06-19 13:35:15 +00001818 }
1819
1820 /**
1821 * @see MdnsUtils.Clock
1822 */
1823 public Clock makeClock() {
1824 return new Clock();
1825 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001826 }
1827
Remi NGUYEN VAN151d0a52023-03-03 17:50:50 +09001828 /**
1829 * Return whether a type is allowlisted to use the Java backend.
1830 * @param type The service type
1831 * @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
1832 * {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
1833 */
1834 private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
1835 @NonNull String flagPrefix) {
1836 if (type == null) return false;
1837 final String typesConfig = mDeps.getTypeAllowlistFlags();
1838 if (TextUtils.isEmpty(typesConfig)) return false;
1839
1840 final String mappingPrefix = type + ":";
1841 String mappedFlag = null;
1842 for (String mapping : TextUtils.split(typesConfig, ",")) {
1843 if (mapping.startsWith(mappingPrefix)) {
1844 mappedFlag = mapping.substring(mappingPrefix.length());
1845 break;
1846 }
1847 }
1848
1849 if (mappedFlag == null) return false;
1850
1851 return mDeps.isFeatureEnabled(mContext,
1852 flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
1853 }
1854
1855 private boolean useDiscoveryManagerForType(@Nullable String type) {
1856 return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
1857 }
1858
1859 private boolean useAdvertiserForType(@Nullable String type) {
1860 return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
1861 }
1862
paulhu1b35e822022-04-08 14:48:41 +08001863 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09001864 HandlerThread thread = new HandlerThread(TAG);
1865 thread.start();
1866 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +08001867 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001868 return service;
1869 }
1870
paulhu2b9ed952022-02-10 21:58:32 +08001871 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
1872 private final StateMachine mStateMachine;
1873
1874 MDnsEventCallback(StateMachine sm) {
1875 mStateMachine = sm;
1876 }
1877
1878 @Override
1879 public void onServiceRegistrationStatus(final RegistrationInfo status) {
1880 mStateMachine.sendMessage(
1881 MDNS_SERVICE_EVENT, status.result, status.id, status);
1882 }
1883
1884 @Override
1885 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
1886 mStateMachine.sendMessage(
1887 MDNS_SERVICE_EVENT, status.result, status.id, status);
1888 }
1889
1890 @Override
1891 public void onServiceResolutionStatus(final ResolutionInfo status) {
1892 mStateMachine.sendMessage(
1893 MDNS_SERVICE_EVENT, status.result, status.id, status);
1894 }
1895
1896 @Override
1897 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
1898 mStateMachine.sendMessage(
1899 MDNS_SERVICE_EVENT, status.result, status.id, status);
1900 }
1901
1902 @Override
1903 public int getInterfaceVersion() throws RemoteException {
1904 return this.VERSION;
1905 }
1906
1907 @Override
1908 public String getInterfaceHash() throws RemoteException {
1909 return this.HASH;
1910 }
1911 }
1912
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001913 private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) {
1914 final String targetInterface = offloadEngineInfo.mInterfaceName;
1915 final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine;
1916 final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers =
1917 mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface);
1918 for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) {
1919 try {
1920 offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo);
1921 } catch (RemoteException e) {
1922 // Can happen in regular cases, do not log a stacktrace
1923 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
1924 }
1925 }
1926 }
1927
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001928 private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
1929 @NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
1930 final int count = mOffloadEngines.beginBroadcast();
1931 try {
1932 for (int i = 0; i < count; i++) {
1933 final OffloadEngineInfo offloadEngineInfo =
1934 (OffloadEngineInfo) mOffloadEngines.getBroadcastCookie(i);
1935 final String interfaceName = offloadEngineInfo.mInterfaceName;
1936 if (!targetInterfaceName.equals(interfaceName)
1937 || ((offloadEngineInfo.mOffloadType
1938 & offloadServiceInfo.getOffloadType()) == 0)) {
1939 continue;
1940 }
1941 try {
1942 if (isRemove) {
1943 mOffloadEngines.getBroadcastItem(i).onOffloadServiceRemoved(
1944 offloadServiceInfo);
1945 } else {
1946 mOffloadEngines.getBroadcastItem(i).onOffloadServiceUpdated(
1947 offloadServiceInfo);
1948 }
1949 } catch (RemoteException e) {
1950 // Can happen in regular cases, do not log a stacktrace
Yuyang Huangc275a9e2023-08-25 18:03:22 +09001951 Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001952 }
1953 }
1954 } finally {
1955 mOffloadEngines.finishBroadcast();
1956 }
1957 }
1958
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001959 private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001960 // TODO: add a callback to notify when a service is being added on each interface (as soon
1961 // as probing starts), and call mOffloadCallbacks. This callback is for
1962 // OFFLOAD_CAPABILITY_FILTER_REPLIES offload type.
1963
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001964 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00001965 public void onRegisterServiceSucceeded(int transactionId, NsdServiceInfo registeredInfo) {
1966 mServiceLogs.log("onRegisterServiceSucceeded: transactionId " + transactionId);
1967 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001968 if (clientInfo == null) return;
1969
Paul Hud44e1b72023-06-16 02:07:42 +00001970 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
1971 if (clientRequestId < 0) return;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001972
1973 // onRegisterServiceSucceeded only has the service name in its info. This aligns with
1974 // historical behavior.
1975 final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
Paul Hu777ed052023-06-19 13:35:15 +00001976 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu508a0122023-09-11 15:31:33 +08001977 clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo, request);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001978 }
1979
1980 @Override
Paul Hud44e1b72023-06-16 02:07:42 +00001981 public void onRegisterServiceFailed(int transactionId, int errorCode) {
1982 final ClientInfo clientInfo = getClientInfoOrLog(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001983 if (clientInfo == null) return;
1984
Paul Hud44e1b72023-06-16 02:07:42 +00001985 final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
1986 if (clientRequestId < 0) return;
Paul Hu777ed052023-06-19 13:35:15 +00001987 final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
Paul Hu508a0122023-09-11 15:31:33 +08001988 clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, false /* isLegacy */,
1989 transactionId, request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09001990 }
1991
Yuyang Huang33fa4d22023-02-14 22:59:37 +09001992 @Override
1993 public void onOffloadStartOrUpdate(@NonNull String interfaceName,
1994 @NonNull OffloadServiceInfo offloadServiceInfo) {
1995 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, false /* isRemove */);
1996 }
1997
1998 @Override
1999 public void onOffloadStop(@NonNull String interfaceName,
2000 @NonNull OffloadServiceInfo offloadServiceInfo) {
2001 sendOffloadServiceInfosUpdate(interfaceName, offloadServiceInfo, true /* isRemove */);
2002 }
2003
Paul Hud44e1b72023-06-16 02:07:42 +00002004 private ClientInfo getClientInfoOrLog(int transactionId) {
2005 final ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId);
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002006 if (clientInfo == null) {
Paul Hud44e1b72023-06-16 02:07:42 +00002007 Log.e(TAG, String.format("Callback for service %d has no client", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002008 }
2009 return clientInfo;
2010 }
2011
Paul Hud44e1b72023-06-16 02:07:42 +00002012 private int getClientRequestIdOrLog(@NonNull ClientInfo info, int transactionId) {
2013 final int clientRequestId = info.getClientRequestId(transactionId);
2014 if (clientRequestId < 0) {
2015 Log.e(TAG, String.format(
2016 "Client request ID not found for service %d", transactionId));
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002017 }
Paul Hud44e1b72023-06-16 02:07:42 +00002018 return clientRequestId;
Remi NGUYEN VAN5b9074c2023-01-18 15:58:03 +09002019 }
2020 }
2021
Paul Hu2e0a88c2023-03-09 16:05:01 +08002022 private static class ConnectorArgs {
2023 @NonNull public final NsdServiceConnector connector;
2024 @NonNull public final INsdManagerCallback callback;
2025 public final boolean useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002026 public final int uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002027
2028 ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
Paul Hub2e67d32023-04-18 05:50:14 +00002029 boolean useJavaBackend, int uid) {
Paul Hu2e0a88c2023-03-09 16:05:01 +08002030 this.connector = connector;
2031 this.callback = callback;
2032 this.useJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002033 this.uid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002034 }
2035 }
2036
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002037 @Override
Paul Hu2e0a88c2023-03-09 16:05:01 +08002038 public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09002039 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Paul Hu101dbf52023-08-09 16:05:20 +08002040 final int uid = mDeps.getCallingUid();
2041 if (cb == null) {
2042 throw new IllegalArgumentException("Unknown client callback from uid=" + uid);
2043 }
Paul Hu2e0a88c2023-03-09 16:05:01 +08002044 if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002045 final INsdServiceConnector connector = new NsdServiceConnector();
Paul Hub2e67d32023-04-18 05:50:14 +00002046 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
Paul Hu101dbf52023-08-09 16:05:20 +08002047 new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend, uid)));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002048 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -07002049 }
2050
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002051 private static class ListenerArgs {
2052 public final NsdServiceConnector connector;
2053 public final NsdServiceInfo serviceInfo;
2054 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
2055 this.connector = connector;
2056 this.serviceInfo = serviceInfo;
2057 }
2058 }
2059
2060 private class NsdServiceConnector extends INsdServiceConnector.Stub
2061 implements IBinder.DeathRecipient {
2062 @Override
2063 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
2064 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2065 NsdManager.REGISTER_SERVICE, 0, listenerKey,
2066 new ListenerArgs(this, serviceInfo)));
2067 }
2068
2069 @Override
2070 public void unregisterService(int listenerKey) {
2071 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2072 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
2073 new ListenerArgs(this, null)));
2074 }
2075
2076 @Override
2077 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
2078 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2079 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
2080 new ListenerArgs(this, serviceInfo)));
2081 }
2082
2083 @Override
2084 public void stopDiscovery(int listenerKey) {
2085 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2086 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
2087 }
2088
2089 @Override
2090 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
2091 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2092 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
2093 new ListenerArgs(this, serviceInfo)));
2094 }
2095
2096 @Override
Paul Hub58deb72022-12-26 09:24:42 +00002097 public void stopResolution(int listenerKey) {
2098 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2099 NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null)));
2100 }
2101
2102 @Override
Paul Hu18aeccc2022-12-27 08:48:48 +00002103 public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
2104 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2105 NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
2106 new ListenerArgs(this, serviceInfo)));
2107 }
2108
2109 @Override
2110 public void unregisterServiceInfoCallback(int listenerKey) {
2111 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2112 NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
2113 new ListenerArgs(this, null)));
2114 }
2115
2116 @Override
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002117 public void startDaemon() {
2118 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
2119 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
2120 }
2121
2122 @Override
2123 public void binderDied() {
2124 mNsdStateMachine.sendMessage(
2125 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002126
2127 }
2128
2129 @Override
2130 public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
2131 @OffloadEngine.OffloadCapability long offloadCapabilities,
2132 @OffloadEngine.OffloadType long offloadTypes) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002133 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002134 Objects.requireNonNull(ifaceName);
2135 Objects.requireNonNull(cb);
2136 mNsdStateMachine.sendMessage(
2137 mNsdStateMachine.obtainMessage(NsdManager.REGISTER_OFFLOAD_ENGINE,
2138 new OffloadEngineInfo(cb, ifaceName, offloadCapabilities,
2139 offloadTypes)));
2140 }
2141
2142 @Override
2143 public void unregisterOffloadEngine(IOffloadEngine cb) {
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002144 checkOffloadEnginePermission(mContext);
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002145 Objects.requireNonNull(cb);
2146 mNsdStateMachine.sendMessage(
2147 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002148 }
Yuyang Huang8e6fbc82023-08-07 17:46:19 +09002149
2150 private static void checkOffloadEnginePermission(Context context) {
2151 if (!SdkLevel.isAtLeastT()) {
2152 throw new SecurityException("API is not available in before API level 33");
2153 }
2154 // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V, but may
2155 // be back ported to older builds: accept it as long as it's signature-protected
2156 if (PermissionUtils.checkAnyPermissionOf(context, REGISTER_NSD_OFFLOAD_ENGINE)
2157 && (SdkLevel.isAtLeastV() || PermissionUtils.isSystemSignaturePermission(
2158 context, REGISTER_NSD_OFFLOAD_ENGINE))) {
2159 return;
2160 }
2161 if (PermissionUtils.checkAnyPermissionOf(context, NETWORK_STACK,
2162 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)) {
2163 return;
2164 }
2165 throw new SecurityException("Requires one of the following permissions: "
2166 + String.join(", ", List.of(REGISTER_NSD_OFFLOAD_ENGINE, NETWORK_STACK,
2167 PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)) + ".");
2168 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002169 }
2170
Hugo Benichi912db992017-04-24 16:41:03 +09002171 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -07002172 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -07002173 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +09002174 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
2175 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -07002176 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -07002177 }
2178
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002179 private int getUniqueId() {
2180 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
2181 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002182 }
2183
Paul Hud44e1b72023-06-16 02:07:42 +00002184 private boolean registerService(int transactionId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +09002185 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002186 Log.d(TAG, "registerService: " + transactionId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002187 }
Hugo Benichi6d706442017-04-24 16:19:58 +09002188 String name = service.getServiceName();
2189 String type = service.getServiceType();
2190 int port = service.getPort();
2191 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002192 final int registerInterface = getNetworkInterfaceIndex(service);
2193 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +08002194 Log.e(TAG, "Interface to register service on not found");
2195 return false;
2196 }
Paul Hud44e1b72023-06-16 02:07:42 +00002197 return mMDnsManager.registerService(
2198 transactionId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002199 }
2200
Paul Hud44e1b72023-06-16 02:07:42 +00002201 private boolean unregisterService(int transactionId) {
2202 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002203 }
2204
Paul Hud44e1b72023-06-16 02:07:42 +00002205 private boolean discoverServices(int transactionId, NsdServiceInfo serviceInfo) {
paulhu2b9ed952022-02-10 21:58:32 +08002206 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002207 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
2208 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002209 Log.e(TAG, "Interface to discover service on not found");
2210 return false;
2211 }
Paul Hud44e1b72023-06-16 02:07:42 +00002212 return mMDnsManager.discover(transactionId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002213 }
2214
Paul Hud44e1b72023-06-16 02:07:42 +00002215 private boolean stopServiceDiscovery(int transactionId) {
2216 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002217 }
2218
Paul Hud44e1b72023-06-16 02:07:42 +00002219 private boolean resolveService(int transactionId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002220 final String name = service.getServiceName();
2221 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002222 final int resolveInterface = getNetworkInterfaceIndex(service);
2223 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002224 Log.e(TAG, "Interface to resolve service on not found");
2225 return false;
2226 }
Paul Hud44e1b72023-06-16 02:07:42 +00002227 return mMDnsManager.resolve(transactionId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002228 }
2229
2230 /**
2231 * Guess the interface to use to resolve or discover a service on a specific network.
2232 *
2233 * This is an imperfect guess, as for example the network may be gone or not yet fully
2234 * registered. This is fine as failing is correct if the network is gone, and a client
2235 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
2236 * this is to support the legacy mdnsresponder implementation, which historically resolved
2237 * services on an unspecified network.
2238 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09002239 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
2240 final Network network = serviceInfo.getNetwork();
2241 if (network == null) {
2242 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
2243 // provided by NsdService from discovery results, and the service was found on an
2244 // interface that has no app-usable Network).
2245 if (serviceInfo.getInterfaceIndex() != 0) {
2246 return serviceInfo.getInterfaceIndex();
2247 }
2248 return IFACE_IDX_ANY;
2249 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002250
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002251 String interfaceName = getNetworkInterfaceName(network);
2252 if (interfaceName == null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002253 return IFACE_IDX_ANY;
2254 }
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002255 return getNetworkInterfaceIndexByName(interfaceName);
2256 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002257
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002258 private String getNetworkInterfaceName(@Nullable Network network) {
2259 if (network == null) {
2260 return null;
2261 }
2262 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
2263 if (cm == null) {
2264 Log.wtf(TAG, "No ConnectivityManager");
2265 return null;
2266 }
2267 final LinkProperties lp = cm.getLinkProperties(network);
2268 if (lp == null) {
2269 return null;
2270 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002271 // Only resolve on non-stacked interfaces
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002272 return lp.getInterfaceName();
2273 }
2274
2275 private int getNetworkInterfaceIndexByName(final String ifaceName) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002276 final NetworkInterface iface;
2277 try {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002278 iface = NetworkInterface.getByName(ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002279 } catch (SocketException e) {
2280 Log.e(TAG, "Error querying interface", e);
2281 return IFACE_IDX_ANY;
2282 }
2283
2284 if (iface == null) {
Yuyang Huang33fa4d22023-02-14 22:59:37 +09002285 Log.e(TAG, "Interface not found: " + ifaceName);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09002286 return IFACE_IDX_ANY;
2287 }
2288
2289 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002290 }
2291
Paul Hud44e1b72023-06-16 02:07:42 +00002292 private boolean stopResolveService(int transactionId) {
2293 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002294 }
2295
Paul Hud44e1b72023-06-16 02:07:42 +00002296 private boolean getAddrInfo(int transactionId, String hostname, int interfaceIdx) {
2297 return mMDnsManager.getServiceAddress(transactionId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002298 }
2299
Paul Hud44e1b72023-06-16 02:07:42 +00002300 private boolean stopGetAddrInfo(int transactionId) {
2301 return mMDnsManager.stopOperation(transactionId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002302 }
2303
2304 @Override
Paul Hub2e67d32023-04-18 05:50:14 +00002305 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2306 if (!PermissionUtils.checkDumpPermission(mContext, TAG, writer)) return;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002307
Paul Hub2e67d32023-04-18 05:50:14 +00002308 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
2309 // Dump state machine logs
Irfan Sheriff75006652012-04-17 23:15:29 -07002310 mNsdStateMachine.dump(fd, pw, args);
Paul Hub2e67d32023-04-18 05:50:14 +00002311
2312 // Dump service and clients logs
2313 pw.println();
Paul Hu14667de2023-04-17 22:42:47 +08002314 pw.println("Logs:");
Paul Hub2e67d32023-04-18 05:50:14 +00002315 pw.increaseIndent();
2316 mServiceLogs.reverseDump(pw);
2317 pw.decreaseIndent();
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002318 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002319
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002320 private abstract static class ClientRequest {
Paul Hud44e1b72023-06-16 02:07:42 +00002321 private final int mTransactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002322 private final long mStartTimeMs;
Paul Hu812e9212023-06-20 06:24:53 +00002323 private int mFoundServiceCount = 0;
2324 private int mLostServiceCount = 0;
2325 private final Set<String> mServices = new ArraySet<>();
Paul Hua6bc4632023-06-26 01:18:29 +00002326 private boolean mIsServiceFromCache = false;
Paul Hubad6fe92023-07-24 21:25:22 +08002327 private int mSentQueryCount = NO_SENT_QUERY_COUNT;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002328
Paul Hu812e9212023-06-20 06:24:53 +00002329 private ClientRequest(int transactionId, long startTimeMs) {
Paul Hud44e1b72023-06-16 02:07:42 +00002330 mTransactionId = transactionId;
Paul Hu777ed052023-06-19 13:35:15 +00002331 mStartTimeMs = startTimeMs;
2332 }
2333
Paul Hu812e9212023-06-20 06:24:53 +00002334 public long calculateRequestDurationMs(long stopTimeMs) {
Paul Hu777ed052023-06-19 13:35:15 +00002335 return stopTimeMs - mStartTimeMs;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002336 }
Paul Hu812e9212023-06-20 06:24:53 +00002337
2338 public void onServiceFound(String serviceName) {
2339 mFoundServiceCount++;
2340 if (mServices.size() <= MAX_SERVICES_COUNT_METRIC_PER_CLIENT) {
2341 mServices.add(serviceName);
2342 }
2343 }
2344
2345 public void onServiceLost() {
2346 mLostServiceCount++;
2347 }
2348
2349 public int getFoundServiceCount() {
2350 return mFoundServiceCount;
2351 }
2352
2353 public int getLostServiceCount() {
2354 return mLostServiceCount;
2355 }
2356
2357 public int getServicesCount() {
2358 return mServices.size();
2359 }
Paul Hua6bc4632023-06-26 01:18:29 +00002360
2361 public void setServiceFromCache(boolean isServiceFromCache) {
2362 mIsServiceFromCache = isServiceFromCache;
2363 }
2364
2365 public boolean isServiceFromCache() {
2366 return mIsServiceFromCache;
2367 }
Paul Hubad6fe92023-07-24 21:25:22 +08002368
2369 public void onQuerySent() {
2370 mSentQueryCount++;
2371 }
2372
2373 public int getSentQueryCount() {
2374 return mSentQueryCount;
2375 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002376 }
2377
2378 private static class LegacyClientRequest extends ClientRequest {
2379 private final int mRequestCode;
2380
Paul Hu812e9212023-06-20 06:24:53 +00002381 private LegacyClientRequest(int transactionId, int requestCode, long startTimeMs) {
2382 super(transactionId, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002383 mRequestCode = requestCode;
2384 }
2385 }
2386
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002387 private abstract static class JavaBackendClientRequest extends ClientRequest {
2388 @Nullable
2389 private final Network mRequestedNetwork;
2390
Paul Hu777ed052023-06-19 13:35:15 +00002391 private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002392 long startTimeMs) {
2393 super(transactionId, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002394 mRequestedNetwork = requestedNetwork;
2395 }
2396
2397 @Nullable
2398 public Network getRequestedNetwork() {
2399 return mRequestedNetwork;
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002400 }
2401 }
2402
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002403 private static class AdvertiserClientRequest extends JavaBackendClientRequest {
Paul Hu777ed052023-06-19 13:35:15 +00002404 private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
Paul Hu812e9212023-06-20 06:24:53 +00002405 long startTimeMs) {
2406 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002407 }
2408 }
2409
2410 private static class DiscoveryManagerRequest extends JavaBackendClientRequest {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002411 @NonNull
2412 private final MdnsListener mListener;
2413
Paul Hud44e1b72023-06-16 02:07:42 +00002414 private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
Paul Hu812e9212023-06-20 06:24:53 +00002415 @Nullable Network requestedNetwork, long startTimeMs) {
2416 super(transactionId, requestedNetwork, startTimeMs);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002417 mListener = listener;
2418 }
2419 }
2420
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002421 /* Information tracked per client */
2422 private class ClientInfo {
2423
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002424 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002425 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002426 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07002427 private NsdServiceInfo mResolvedService;
2428
Paul Hud44e1b72023-06-16 02:07:42 +00002429 /* A map from client request ID (listenerKey) to the request */
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002430 private final SparseArray<ClientRequest> mClientRequests = new SparseArray<>();
Paul Hu23fa2022023-01-13 22:57:24 +08002431
Luke Huangf7277ed2021-07-12 21:15:10 +08002432 // The target SDK of this client < Build.VERSION_CODES.S
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002433 private boolean mIsPreSClient = false;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002434 private final int mUid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002435 // The flag of using java backend if the client's target SDK >= U
2436 private final boolean mUseJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002437 // Store client logs
2438 private final SharedLog mClientLogs;
Paul Hucdef3532023-06-18 14:47:35 +00002439 // Report the nsd metrics data
2440 private final NetworkNsdReportedMetrics mMetrics;
Luke Huangf7277ed2021-07-12 21:15:10 +08002441
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002442 private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
Paul Hucdef3532023-06-18 14:47:35 +00002443 SharedLog sharedLog, NetworkNsdReportedMetrics metrics) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002444 mCb = cb;
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002445 mUid = uid;
Paul Hu2e0a88c2023-03-09 16:05:01 +08002446 mUseJavaBackend = useJavaBackend;
Paul Hub2e67d32023-04-18 05:50:14 +00002447 mClientLogs = sharedLog;
2448 mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
Paul Hucdef3532023-06-18 14:47:35 +00002449 mMetrics = metrics;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002450 }
Irfan Sheriff75006652012-04-17 23:15:29 -07002451
2452 @Override
2453 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -06002454 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -07002455 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002456 sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002457 sb.append("mUseJavaBackend ").append(mUseJavaBackend).append("\n");
2458 sb.append("mUid ").append(mUid).append("\n");
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002459 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002460 int clientRequestId = mClientRequests.keyAt(i);
2461 sb.append("clientRequestId ")
2462 .append(clientRequestId)
2463 .append(" transactionId ").append(mClientRequests.valueAt(i).mTransactionId)
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002464 .append(" type ").append(
2465 mClientRequests.valueAt(i).getClass().getSimpleName())
2466 .append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -07002467 }
2468 return sb.toString();
2469 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002470
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002471 private boolean isPreSClient() {
2472 return mIsPreSClient;
Luke Huangf7277ed2021-07-12 21:15:10 +08002473 }
2474
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002475 private void setPreSClient() {
2476 mIsPreSClient = true;
Luke Huangf7277ed2021-07-12 21:15:10 +08002477 }
2478
Paul Hu812e9212023-06-20 06:24:53 +00002479 private MdnsListener unregisterMdnsListenerFromRequest(ClientRequest request) {
Paul Hue4f5f252023-02-16 21:13:47 +08002480 final MdnsListener listener =
2481 ((DiscoveryManagerRequest) request).mListener;
2482 mMdnsDiscoveryManager.unregisterListener(
2483 listener.getListenedServiceType(), listener);
Paul Hu812e9212023-06-20 06:24:53 +00002484 return listener;
Paul Hue4f5f252023-02-16 21:13:47 +08002485 }
2486
Dave Plattfeff2af2014-03-07 14:48:22 -08002487 // Remove any pending requests from the global map when we get rid of a client,
2488 // and send cancellations to the daemon.
2489 private void expungeAllRequests() {
Paul Hub2e67d32023-04-18 05:50:14 +00002490 mClientLogs.log("Client unregistered. expungeAllRequests!");
Hugo Benichid2552ae2017-04-11 14:42:47 +09002491 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002492 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002493 final int clientRequestId = mClientRequests.keyAt(i);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002494 final ClientRequest request = mClientRequests.valueAt(i);
Paul Hud44e1b72023-06-16 02:07:42 +00002495 final int transactionId = request.mTransactionId;
2496 mTransactionIdToClientInfoMap.remove(transactionId);
paulhub2225702021-11-17 09:35:33 +08002497 if (DBG) {
Paul Hud44e1b72023-06-16 02:07:42 +00002498 Log.d(TAG, "Terminating clientRequestId " + clientRequestId
2499 + " transactionId " + transactionId
2500 + " type " + mClientRequests.get(clientRequestId));
paulhub2225702021-11-17 09:35:33 +08002501 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002502
2503 if (request instanceof DiscoveryManagerRequest) {
Paul Hu812e9212023-06-20 06:24:53 +00002504 final MdnsListener listener = unregisterMdnsListenerFromRequest(request);
2505 if (listener instanceof DiscoveryListener) {
Paul Hu508a0122023-09-11 15:31:33 +08002506 mMetrics.reportServiceDiscoveryStop(false /* isLegacy */, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00002507 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2508 request.getFoundServiceCount(),
2509 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002510 request.getServicesCount(),
2511 request.getSentQueryCount());
Paul Hu60149052023-07-31 14:26:08 +08002512 } else if (listener instanceof ResolutionListener) {
Paul Hu508a0122023-09-11 15:31:33 +08002513 mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
Paul Hu60149052023-07-31 14:26:08 +08002514 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Huddce5912023-08-01 10:26:49 +08002515 } else if (listener instanceof ServiceInfoListener) {
2516 mMetrics.reportServiceInfoCallbackUnregistered(transactionId,
2517 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2518 request.getFoundServiceCount(),
2519 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002520 request.isServiceFromCache(),
2521 request.getSentQueryCount());
Paul Hu812e9212023-06-20 06:24:53 +00002522 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002523 continue;
2524 }
2525
2526 if (request instanceof AdvertiserClientRequest) {
Paul Hu043bcd42023-07-14 16:38:25 +08002527 final AdvertiserMetrics metrics =
2528 mAdvertiser.getAdvertiserMetrics(transactionId);
Paul Hud44e1b72023-06-16 02:07:42 +00002529 mAdvertiser.removeService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002530 mMetrics.reportServiceUnregistration(false /* isLegacy */, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002531 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2532 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2533 metrics.mConflictDuringProbingCount,
2534 metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002535 continue;
2536 }
2537
2538 if (!(request instanceof LegacyClientRequest)) {
2539 throw new IllegalStateException("Unknown request type: " + request.getClass());
2540 }
2541
2542 switch (((LegacyClientRequest) request).mRequestCode) {
Dave Plattfeff2af2014-03-07 14:48:22 -08002543 case NsdManager.DISCOVER_SERVICES:
Paul Hud44e1b72023-06-16 02:07:42 +00002544 stopServiceDiscovery(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002545 mMetrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId,
Paul Hu812e9212023-06-20 06:24:53 +00002546 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2547 request.getFoundServiceCount(),
2548 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002549 request.getServicesCount(),
2550 NO_SENT_QUERY_COUNT);
Dave Plattfeff2af2014-03-07 14:48:22 -08002551 break;
2552 case NsdManager.RESOLVE_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002553 stopResolveService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002554 mMetrics.reportServiceResolutionStop(true /* isLegacy */, transactionId,
Paul Hu60149052023-07-31 14:26:08 +08002555 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Dave Plattfeff2af2014-03-07 14:48:22 -08002556 break;
2557 case NsdManager.REGISTER_SERVICE:
Paul Hud44e1b72023-06-16 02:07:42 +00002558 unregisterService(transactionId);
Paul Hu508a0122023-09-11 15:31:33 +08002559 mMetrics.reportServiceUnregistration(true /* isLegacy */, transactionId,
Paul Hu043bcd42023-07-14 16:38:25 +08002560 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2561 NO_PACKET /* repliedRequestsCount */,
2562 NO_PACKET /* sentPacketCount */,
2563 0 /* conflictDuringProbingCount */,
2564 0 /* conflictAfterProbingCount */);
Dave Plattfeff2af2014-03-07 14:48:22 -08002565 break;
2566 default:
2567 break;
2568 }
2569 }
Dave Plattfeff2af2014-03-07 14:48:22 -08002570 mClientRequests.clear();
Remi NGUYEN VANa8efbe02023-05-26 11:43:20 +09002571 updateMulticastLock();
2572 }
2573
2574 /**
2575 * Returns true if this client has any Java backend request that requests one of the given
2576 * networks.
2577 */
2578 boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) {
2579 for (int i = 0; i < mClientRequests.size(); i++) {
2580 final ClientRequest req = mClientRequests.valueAt(i);
2581 if (!(req instanceof JavaBackendClientRequest)) {
2582 continue;
2583 }
2584 final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i))
2585 .getRequestedNetwork();
2586 if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) {
2587 return true;
2588 }
2589 }
2590 return false;
Dave Plattfeff2af2014-03-07 14:48:22 -08002591 }
2592
Paul Hud44e1b72023-06-16 02:07:42 +00002593 // mClientRequests is a sparse array of client request id -> ClientRequest. For a given
2594 // transaction id, return the corresponding client request id.
2595 private int getClientRequestId(final int transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002596 for (int i = 0; i < mClientRequests.size(); i++) {
Paul Hud44e1b72023-06-16 02:07:42 +00002597 if (mClientRequests.valueAt(i).mTransactionId == transactionId) {
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002598 return mClientRequests.keyAt(i);
2599 }
Christopher Lane74411222014-04-25 18:39:07 -07002600 }
Remi NGUYEN VAN8f453b92023-01-18 17:44:36 +09002601 return -1;
Christopher Lane74411222014-04-25 18:39:07 -07002602 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002603
Paul Hub2e67d32023-04-18 05:50:14 +00002604 private void log(String message) {
2605 mClientLogs.log(message);
2606 }
2607
Paul Hu508a0122023-09-11 15:31:33 +08002608 private static boolean isLegacyClientRequest(@NonNull ClientRequest request) {
2609 return !(request instanceof DiscoveryManagerRequest)
2610 && !(request instanceof AdvertiserClientRequest);
2611 }
2612
2613 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info,
2614 ClientRequest request) {
2615 mMetrics.reportServiceDiscoveryStarted(
2616 isLegacyClientRequest(request), request.mTransactionId);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002617 try {
2618 mCb.onDiscoverServicesStarted(listenerKey, info);
2619 } catch (RemoteException e) {
2620 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
2621 }
2622 }
Paul Hu508a0122023-09-11 15:31:33 +08002623 void onDiscoverServicesFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2624 onDiscoverServicesFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2625 0L /* durationMs */);
Paul Hu812e9212023-06-20 06:24:53 +00002626 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002627
Paul Hu508a0122023-09-11 15:31:33 +08002628 void onDiscoverServicesFailed(int listenerKey, int error, boolean isLegacy,
2629 int transactionId, long durationMs) {
2630 mMetrics.reportServiceDiscoveryFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002631 try {
2632 mCb.onDiscoverServicesFailed(listenerKey, error);
2633 } catch (RemoteException e) {
2634 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
2635 }
2636 }
2637
Paul Hu812e9212023-06-20 06:24:53 +00002638 void onServiceFound(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2639 request.onServiceFound(info.getServiceName());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002640 try {
2641 mCb.onServiceFound(listenerKey, info);
2642 } catch (RemoteException e) {
2643 Log.e(TAG, "Error calling onServiceFound(", e);
2644 }
2645 }
2646
Paul Hu812e9212023-06-20 06:24:53 +00002647 void onServiceLost(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2648 request.onServiceLost();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002649 try {
2650 mCb.onServiceLost(listenerKey, info);
2651 } catch (RemoteException e) {
2652 Log.e(TAG, "Error calling onServiceLost(", e);
2653 }
2654 }
2655
2656 void onStopDiscoveryFailed(int listenerKey, int error) {
2657 try {
2658 mCb.onStopDiscoveryFailed(listenerKey, error);
2659 } catch (RemoteException e) {
2660 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
2661 }
2662 }
2663
Paul Hu812e9212023-06-20 06:24:53 +00002664 void onStopDiscoverySucceeded(int listenerKey, ClientRequest request) {
2665 mMetrics.reportServiceDiscoveryStop(
Paul Hu508a0122023-09-11 15:31:33 +08002666 isLegacyClientRequest(request),
Paul Hu812e9212023-06-20 06:24:53 +00002667 request.mTransactionId,
2668 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2669 request.getFoundServiceCount(),
2670 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002671 request.getServicesCount(),
2672 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002673 try {
2674 mCb.onStopDiscoverySucceeded(listenerKey);
2675 } catch (RemoteException e) {
2676 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
2677 }
2678 }
2679
Paul Hu508a0122023-09-11 15:31:33 +08002680 void onRegisterServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2681 onRegisterServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2682 0L /* durationMs */);
Paul Hu777ed052023-06-19 13:35:15 +00002683 }
2684
Paul Hu508a0122023-09-11 15:31:33 +08002685 void onRegisterServiceFailed(int listenerKey, int error, boolean isLegacy,
2686 int transactionId, long durationMs) {
2687 mMetrics.reportServiceRegistrationFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002688 try {
2689 mCb.onRegisterServiceFailed(listenerKey, error);
2690 } catch (RemoteException e) {
2691 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
2692 }
2693 }
2694
Paul Hu508a0122023-09-11 15:31:33 +08002695 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info,
2696 ClientRequest request) {
2697 mMetrics.reportServiceRegistrationSucceeded(isLegacyClientRequest(request),
2698 request.mTransactionId,
2699 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002700 try {
2701 mCb.onRegisterServiceSucceeded(listenerKey, info);
2702 } catch (RemoteException e) {
2703 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
2704 }
2705 }
2706
2707 void onUnregisterServiceFailed(int listenerKey, int error) {
2708 try {
2709 mCb.onUnregisterServiceFailed(listenerKey, error);
2710 } catch (RemoteException e) {
2711 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
2712 }
2713 }
2714
Paul Hu508a0122023-09-11 15:31:33 +08002715 void onUnregisterServiceSucceeded(int listenerKey, ClientRequest request,
Paul Hu043bcd42023-07-14 16:38:25 +08002716 AdvertiserMetrics metrics) {
Paul Hu508a0122023-09-11 15:31:33 +08002717 mMetrics.reportServiceUnregistration(isLegacyClientRequest(request),
2718 request.mTransactionId,
2719 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hu043bcd42023-07-14 16:38:25 +08002720 metrics.mRepliedRequestsCount, metrics.mSentPacketCount,
2721 metrics.mConflictDuringProbingCount, metrics.mConflictAfterProbingCount);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002722 try {
2723 mCb.onUnregisterServiceSucceeded(listenerKey);
2724 } catch (RemoteException e) {
2725 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
2726 }
2727 }
2728
Paul Hu508a0122023-09-11 15:31:33 +08002729 void onResolveServiceFailedImmediately(int listenerKey, int error, boolean isLegacy) {
2730 onResolveServiceFailed(listenerKey, error, isLegacy, NO_TRANSACTION,
2731 0L /* durationMs */);
Paul Hua6bc4632023-06-26 01:18:29 +00002732 }
2733
Paul Hu508a0122023-09-11 15:31:33 +08002734 void onResolveServiceFailed(int listenerKey, int error, boolean isLegacy,
2735 int transactionId, long durationMs) {
2736 mMetrics.reportServiceResolutionFailed(isLegacy, transactionId, durationMs);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002737 try {
2738 mCb.onResolveServiceFailed(listenerKey, error);
2739 } catch (RemoteException e) {
2740 Log.e(TAG, "Error calling onResolveServiceFailed", e);
2741 }
2742 }
2743
Paul Hua6bc4632023-06-26 01:18:29 +00002744 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info,
2745 ClientRequest request) {
2746 mMetrics.reportServiceResolved(
Paul Hu508a0122023-09-11 15:31:33 +08002747 isLegacyClientRequest(request),
Paul Hua6bc4632023-06-26 01:18:29 +00002748 request.mTransactionId,
2749 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
Paul Hubad6fe92023-07-24 21:25:22 +08002750 request.isServiceFromCache(),
2751 request.getSentQueryCount());
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002752 try {
2753 mCb.onResolveServiceSucceeded(listenerKey, info);
2754 } catch (RemoteException e) {
2755 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
2756 }
2757 }
Paul Hub58deb72022-12-26 09:24:42 +00002758
2759 void onStopResolutionFailed(int listenerKey, int error) {
2760 try {
2761 mCb.onStopResolutionFailed(listenerKey, error);
2762 } catch (RemoteException e) {
2763 Log.e(TAG, "Error calling onStopResolutionFailed", e);
2764 }
2765 }
2766
Paul Hu60149052023-07-31 14:26:08 +08002767 void onStopResolutionSucceeded(int listenerKey, ClientRequest request) {
2768 mMetrics.reportServiceResolutionStop(
Paul Hu508a0122023-09-11 15:31:33 +08002769 isLegacyClientRequest(request),
Paul Hu60149052023-07-31 14:26:08 +08002770 request.mTransactionId,
2771 request.calculateRequestDurationMs(mClock.elapsedRealtime()));
Paul Hub58deb72022-12-26 09:24:42 +00002772 try {
2773 mCb.onStopResolutionSucceeded(listenerKey);
2774 } catch (RemoteException e) {
2775 Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
2776 }
2777 }
Paul Hu18aeccc2022-12-27 08:48:48 +00002778
2779 void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
Paul Huddce5912023-08-01 10:26:49 +08002780 mMetrics.reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION);
Paul Hu18aeccc2022-12-27 08:48:48 +00002781 try {
2782 mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
2783 } catch (RemoteException e) {
2784 Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
2785 }
2786 }
2787
Paul Huddce5912023-08-01 10:26:49 +08002788 void onServiceInfoCallbackRegistered(int transactionId) {
2789 mMetrics.reportServiceInfoCallbackRegistered(transactionId);
2790 }
2791
2792 void onServiceUpdated(int listenerKey, NsdServiceInfo info, ClientRequest request) {
2793 request.onServiceFound(info.getServiceName());
Paul Hu18aeccc2022-12-27 08:48:48 +00002794 try {
2795 mCb.onServiceUpdated(listenerKey, info);
2796 } catch (RemoteException e) {
2797 Log.e(TAG, "Error calling onServiceUpdated", e);
2798 }
2799 }
2800
Paul Huddce5912023-08-01 10:26:49 +08002801 void onServiceUpdatedLost(int listenerKey, ClientRequest request) {
2802 request.onServiceLost();
Paul Hu18aeccc2022-12-27 08:48:48 +00002803 try {
2804 mCb.onServiceUpdatedLost(listenerKey);
2805 } catch (RemoteException e) {
2806 Log.e(TAG, "Error calling onServiceUpdatedLost", e);
2807 }
2808 }
2809
Paul Huddce5912023-08-01 10:26:49 +08002810 void onServiceInfoCallbackUnregistered(int listenerKey, ClientRequest request) {
2811 mMetrics.reportServiceInfoCallbackUnregistered(
2812 request.mTransactionId,
2813 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
2814 request.getFoundServiceCount(),
2815 request.getLostServiceCount(),
Paul Hubad6fe92023-07-24 21:25:22 +08002816 request.isServiceFromCache(),
2817 request.getSentQueryCount());
Paul Hu18aeccc2022-12-27 08:48:48 +00002818 try {
2819 mCb.onServiceInfoCallbackUnregistered(listenerKey);
2820 } catch (RemoteException e) {
2821 Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
2822 }
2823 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07002824 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07002825}