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 * Nsd resolve requests for the same info cancel each other. Hence this class synchronizes the 29 * resolutions to hide this effect. 30 */ 31 class NsdResolveQueue { 32 private static final String TAG = NsdResolveQueue.class.getSimpleName(); 33 private static final boolean DEBUG = false; 34 35 /** Lock for {@link #sInstance} */ 36 private static final Object sLock = new Object(); 37 38 /** Instance of this singleton */ 39 private static NsdResolveQueue sInstance; 40 41 /** Current set of registered service info resolve attempts */ 42 final LinkedList<NsdResolveRequest> mResolveRequests = new LinkedList<>(); 43 44 private final Handler mMainHandler; 45 46 public static NsdResolveQueue getInstance(Context context) { 47 synchronized (sLock) { 48 if (sInstance == null) { 49 sInstance = new NsdResolveQueue(context); 50 } 51 return sInstance; 52 } 53 } 54 55 private NsdResolveQueue(Context context) { 56 mMainHandler = new Handler(context.getMainLooper()); 57 } 58 59 /** 60 * Resolve a serviceInfo or queue the request if there is a request currently in flight. 61 * 62 * @param nsdManager The nsd manager to use 63 * @param serviceInfo The service info to resolve 64 * @param listener The listener to call back once the info is resolved. 65 */ 66 public void resolve(NsdManager nsdManager, NsdServiceInfo serviceInfo, 67 NsdManager.ResolveListener listener) { 68 if (DEBUG) { 69 Log.d(TAG, "Adding resolve of " + serviceInfo.getServiceName() + " to queue size=" + 70 mResolveRequests.size()); 71 } 72 mResolveRequests.addLast(new NsdResolveRequest(nsdManager, serviceInfo, listener)); 73 if (mResolveRequests.size() == 1) { 74 resolveNextRequest(); 75 } 76 } 77 78 /** Immediately reject all unstarted requests */ 79 void clear() { 80 while (mResolveRequests.size() > 1) { 81 mResolveRequests.remove(1); 82 } 83 } 84 85 /** 86 * Resolve the next request if there is one. 87 */ 88 private void resolveNextRequest() { 89 if (!mResolveRequests.isEmpty()) { 90 mResolveRequests.getFirst().start(); 91 } 92 } 93 94 /** 95 * Holds a request to resolve a {@link NsdServiceInfo} 96 */ 97 private class NsdResolveRequest implements NsdManager.ResolveListener { 98 final NsdManager nsdManager; 99 final NsdServiceInfo serviceInfo; 100 final NsdManager.ResolveListener listener; 101 private long mStartTime; 102 103 private NsdResolveRequest(NsdManager nsdManager, 104 NsdServiceInfo serviceInfo, 105 NsdManager.ResolveListener listener) { 106 this.nsdManager = nsdManager; 107 this.serviceInfo = serviceInfo; 108 this.listener = listener; 109 } 110 111 public void start() { 112 mStartTime = System.currentTimeMillis(); 113 if (DEBUG) Log.d(TAG, "resolveService " + serviceInfo.getServiceName()); 114 nsdManager.resolveService(serviceInfo, this); 115 } 116 117 @Override 118 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { 119 if (DEBUG) { 120 Log.d(TAG, "onResolveFailed " + serviceInfo.getServiceName() + " errorCode=" + 121 errorCode + " (" + (System.currentTimeMillis() - mStartTime) + " ms)"); 122 } 123 mMainHandler.post(() -> { 124 listener.onResolveFailed(serviceInfo, errorCode); 125 mResolveRequests.pop(); 126 resolveNextRequest(); 127 }); 128 } 129 130 @Override 131 public void onServiceResolved(NsdServiceInfo serviceInfo) { 132 if (DEBUG) { 133 Log.d(TAG, "onServiceResolved " + serviceInfo.getServiceName() + 134 " (" + (System.currentTimeMillis() - mStartTime) + " ms)"); 135 } 136 mMainHandler.post(() -> { 137 listener.onServiceResolved(serviceInfo); 138 mResolveRequests.pop(); 139 resolveNextRequest(); 140 }); 141 } 142 } 143 }