1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
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 
17 package com.android.bips.discovery;
18 
19 import android.content.Context;
20 import android.net.nsd.NsdManager;
21 import android.net.nsd.NsdServiceInfo;
22 import android.os.Handler;
23 import android.util.Log;
24 
25 import java.util.LinkedList;
26 
27 /**
28  * Queues Nsd resolve requests to prevent multiple simultaneous requests to NsdManager
29  */
30 public class NsdResolveQueue {
31     private static final String TAG = NsdResolveQueue.class.getSimpleName();
32     private static final boolean DEBUG = false;
33 
34     private final NsdManager mNsdManager;
35     private final Handler mMainHandler;
36 
37     /** Current set of registered service info resolve attempts */
38     private LinkedList<NsdResolveRequest> mResolveRequests = new LinkedList<>();
39 
NsdResolveQueue(Context context, NsdManager nsdManager)40     public NsdResolveQueue(Context context, NsdManager nsdManager) {
41         mNsdManager = nsdManager;
42         mMainHandler = new Handler(context.getMainLooper());
43     }
44 
45     /** Return the {@link NsdManager} used by this queue */
getNsdManager()46     NsdManager getNsdManager() {
47         return mNsdManager;
48     }
49 
50     /**
51      * Resolve a serviceInfo or queue the request if there is a request currently in flight.
52      *
53      * @param serviceInfo The service info to resolve
54      * @param listener    The listener to call back once the info is resolved.
55      */
resolve(NsdServiceInfo serviceInfo, NsdManager.ResolveListener listener)56     public NsdResolveRequest resolve(NsdServiceInfo serviceInfo,
57             NsdManager.ResolveListener listener) {
58         if (DEBUG) {
59             Log.d(TAG, "Adding resolve of " + serviceInfo.getServiceName() + " to queue size="
60                     + mResolveRequests.size());
61         }
62         NsdResolveRequest request = new NsdResolveRequest(mNsdManager, serviceInfo, listener);
63         mResolveRequests.addLast(request);
64         if (mResolveRequests.size() == 1) {
65             resolveNextRequest();
66         }
67         return request;
68     }
69 
70     /**
71      * Resolve the next request if there is one.
72      */
resolveNextRequest()73     private void resolveNextRequest() {
74         if (!mResolveRequests.isEmpty()) {
75             mResolveRequests.getFirst().start();
76         }
77     }
78 
79     /**
80      * Holds a request to resolve a {@link NsdServiceInfo}
81      */
82     class NsdResolveRequest implements NsdManager.ResolveListener {
83         private final NsdManager mNsdManager;
84         private final NsdServiceInfo mServiceInfo;
85         private final NsdManager.ResolveListener mListener;
86         private long mStartTime;
87 
NsdResolveRequest(NsdManager nsdManager, NsdServiceInfo serviceInfo, NsdManager.ResolveListener listener)88         private NsdResolveRequest(NsdManager nsdManager,
89                 NsdServiceInfo serviceInfo,
90                 NsdManager.ResolveListener listener) {
91             mNsdManager = nsdManager;
92             mServiceInfo = serviceInfo;
93             mListener = listener;
94         }
95 
start()96         private void start() {
97             mStartTime = System.currentTimeMillis();
98             if (DEBUG) Log.d(TAG, "resolveService " + mServiceInfo.getServiceName());
99             mNsdManager.resolveService(mServiceInfo, this);
100         }
101 
cancel()102         void cancel() {
103             // Note: resolve requests can only be cancelled if they have not yet begun
104             if (!mResolveRequests.isEmpty() && mResolveRequests.get(0) != this) {
105                 mResolveRequests.remove(this);
106             }
107         }
108 
109         @Override
onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)110         public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
111             if (DEBUG) {
112                 Log.d(TAG, "onResolveFailed " + serviceInfo.getServiceName() + " errorCode="
113                         + errorCode + " (" + (System.currentTimeMillis() - mStartTime) + " ms)");
114             }
115             mMainHandler.post(() -> {
116                 mListener.onResolveFailed(serviceInfo, errorCode);
117                 mResolveRequests.pop();
118                 resolveNextRequest();
119             });
120         }
121 
122         @Override
onServiceResolved(NsdServiceInfo serviceInfo)123         public void onServiceResolved(NsdServiceInfo serviceInfo) {
124             if (DEBUG) {
125                 Log.d(TAG, "onServiceResolved " + serviceInfo.getServiceName()
126                         + " (" + (System.currentTimeMillis() - mStartTime) + " ms)");
127             }
128             mMainHandler.post(() -> {
129                 mListener.onServiceResolved(serviceInfo);
130                 mResolveRequests.pop();
131                 resolveNextRequest();
132             });
133         }
134     }
135 }
136