13bf66744d61d18c66d46f2608de0467ad3df0268Mopria/*
23bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Copyright (C) 2016 The Android Open Source Project
33bf66744d61d18c66d46f2608de0467ad3df0268Mopria *
43bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Licensed under the Apache License, Version 2.0 (the "License");
53bf66744d61d18c66d46f2608de0467ad3df0268Mopria * you may not use this file except in compliance with the License.
63bf66744d61d18c66d46f2608de0467ad3df0268Mopria * You may obtain a copy of the License at
73bf66744d61d18c66d46f2608de0467ad3df0268Mopria *
83bf66744d61d18c66d46f2608de0467ad3df0268Mopria *      http://www.apache.org/licenses/LICENSE-2.0
93bf66744d61d18c66d46f2608de0467ad3df0268Mopria *
103bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Unless required by applicable law or agreed to in writing, software
113bf66744d61d18c66d46f2608de0467ad3df0268Mopria * distributed under the License is distributed on an "AS IS" BASIS,
123bf66744d61d18c66d46f2608de0467ad3df0268Mopria * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133bf66744d61d18c66d46f2608de0467ad3df0268Mopria * See the License for the specific language governing permissions and
143bf66744d61d18c66d46f2608de0467ad3df0268Mopria * limitations under the License.
153bf66744d61d18c66d46f2608de0467ad3df0268Mopria */
163bf66744d61d18c66d46f2608de0467ad3df0268Mopria
173bf66744d61d18c66d46f2608de0467ad3df0268Mopriapackage com.android.bips.discovery;
183bf66744d61d18c66d46f2608de0467ad3df0268Mopria
193bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.content.Context;
203bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.net.nsd.NsdManager;
213bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.net.nsd.NsdServiceInfo;
223bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.os.Handler;
233bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.util.Log;
243bf66744d61d18c66d46f2608de0467ad3df0268Mopria
253bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport java.util.LinkedList;
263bf66744d61d18c66d46f2608de0467ad3df0268Mopria
273bf66744d61d18c66d46f2608de0467ad3df0268Mopria/**
283bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Nsd resolve requests for the same info cancel each other. Hence this class synchronizes the
293bf66744d61d18c66d46f2608de0467ad3df0268Mopria * resolutions to hide this effect.
303bf66744d61d18c66d46f2608de0467ad3df0268Mopria */
313bf66744d61d18c66d46f2608de0467ad3df0268Mopriaclass NsdResolveQueue {
323bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private static final String TAG = NsdResolveQueue.class.getSimpleName();
333bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private static final boolean DEBUG = false;
343bf66744d61d18c66d46f2608de0467ad3df0268Mopria
353bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /** Lock for {@link #sInstance} */
363bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private static final Object sLock = new Object();
373bf66744d61d18c66d46f2608de0467ad3df0268Mopria
383bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /** Instance of this singleton */
393bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private static NsdResolveQueue sInstance;
403bf66744d61d18c66d46f2608de0467ad3df0268Mopria
413bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /** Current set of registered service info resolve attempts */
423bf66744d61d18c66d46f2608de0467ad3df0268Mopria    final LinkedList<NsdResolveRequest> mResolveRequests = new LinkedList<>();
433bf66744d61d18c66d46f2608de0467ad3df0268Mopria
443bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private final Handler mMainHandler;
453bf66744d61d18c66d46f2608de0467ad3df0268Mopria
463bf66744d61d18c66d46f2608de0467ad3df0268Mopria    public static NsdResolveQueue getInstance(Context context) {
473bf66744d61d18c66d46f2608de0467ad3df0268Mopria        synchronized (sLock) {
483bf66744d61d18c66d46f2608de0467ad3df0268Mopria            if (sInstance == null) {
493bf66744d61d18c66d46f2608de0467ad3df0268Mopria                sInstance = new NsdResolveQueue(context);
503bf66744d61d18c66d46f2608de0467ad3df0268Mopria            }
513bf66744d61d18c66d46f2608de0467ad3df0268Mopria            return sInstance;
523bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
533bf66744d61d18c66d46f2608de0467ad3df0268Mopria    }
543bf66744d61d18c66d46f2608de0467ad3df0268Mopria
553bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private NsdResolveQueue(Context context) {
563bf66744d61d18c66d46f2608de0467ad3df0268Mopria        mMainHandler = new Handler(context.getMainLooper());
573bf66744d61d18c66d46f2608de0467ad3df0268Mopria    }
583bf66744d61d18c66d46f2608de0467ad3df0268Mopria
593bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /**
603bf66744d61d18c66d46f2608de0467ad3df0268Mopria     * Resolve a serviceInfo or queue the request if there is a request currently in flight.
613bf66744d61d18c66d46f2608de0467ad3df0268Mopria     *
623bf66744d61d18c66d46f2608de0467ad3df0268Mopria     * @param nsdManager  The nsd manager to use
633bf66744d61d18c66d46f2608de0467ad3df0268Mopria     * @param serviceInfo The service info to resolve
643bf66744d61d18c66d46f2608de0467ad3df0268Mopria     * @param listener    The listener to call back once the info is resolved.
653bf66744d61d18c66d46f2608de0467ad3df0268Mopria     */
663bf66744d61d18c66d46f2608de0467ad3df0268Mopria    public void resolve(NsdManager nsdManager, NsdServiceInfo serviceInfo,
673bf66744d61d18c66d46f2608de0467ad3df0268Mopria            NsdManager.ResolveListener listener) {
683bf66744d61d18c66d46f2608de0467ad3df0268Mopria        if (DEBUG) {
693bf66744d61d18c66d46f2608de0467ad3df0268Mopria            Log.d(TAG, "Adding resolve of " + serviceInfo.getServiceName() + " to queue size=" +
703bf66744d61d18c66d46f2608de0467ad3df0268Mopria                    mResolveRequests.size());
713bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
723bf66744d61d18c66d46f2608de0467ad3df0268Mopria        mResolveRequests.addLast(new NsdResolveRequest(nsdManager, serviceInfo, listener));
733bf66744d61d18c66d46f2608de0467ad3df0268Mopria        if (mResolveRequests.size() == 1) {
743bf66744d61d18c66d46f2608de0467ad3df0268Mopria            resolveNextRequest();
753bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
763bf66744d61d18c66d46f2608de0467ad3df0268Mopria    }
773bf66744d61d18c66d46f2608de0467ad3df0268Mopria
783bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /** Immediately reject all unstarted requests */
793bf66744d61d18c66d46f2608de0467ad3df0268Mopria    void clear() {
803bf66744d61d18c66d46f2608de0467ad3df0268Mopria        while (mResolveRequests.size() > 1) {
813bf66744d61d18c66d46f2608de0467ad3df0268Mopria            mResolveRequests.remove(1);
823bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
833bf66744d61d18c66d46f2608de0467ad3df0268Mopria    }
843bf66744d61d18c66d46f2608de0467ad3df0268Mopria
853bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /**
863bf66744d61d18c66d46f2608de0467ad3df0268Mopria     * Resolve the next request if there is one.
873bf66744d61d18c66d46f2608de0467ad3df0268Mopria     */
883bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private void resolveNextRequest() {
893bf66744d61d18c66d46f2608de0467ad3df0268Mopria        if (!mResolveRequests.isEmpty()) {
903bf66744d61d18c66d46f2608de0467ad3df0268Mopria            mResolveRequests.getFirst().start();
913bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
923bf66744d61d18c66d46f2608de0467ad3df0268Mopria    }
933bf66744d61d18c66d46f2608de0467ad3df0268Mopria
943bf66744d61d18c66d46f2608de0467ad3df0268Mopria    /**
953bf66744d61d18c66d46f2608de0467ad3df0268Mopria     * Holds a request to resolve a {@link NsdServiceInfo}
963bf66744d61d18c66d46f2608de0467ad3df0268Mopria     */
973bf66744d61d18c66d46f2608de0467ad3df0268Mopria    private class NsdResolveRequest implements NsdManager.ResolveListener {
983bf66744d61d18c66d46f2608de0467ad3df0268Mopria        final NsdManager nsdManager;
993bf66744d61d18c66d46f2608de0467ad3df0268Mopria        final NsdServiceInfo serviceInfo;
1003bf66744d61d18c66d46f2608de0467ad3df0268Mopria        final NsdManager.ResolveListener listener;
1013bf66744d61d18c66d46f2608de0467ad3df0268Mopria        private long mStartTime;
1023bf66744d61d18c66d46f2608de0467ad3df0268Mopria
1033bf66744d61d18c66d46f2608de0467ad3df0268Mopria        private NsdResolveRequest(NsdManager nsdManager,
1043bf66744d61d18c66d46f2608de0467ad3df0268Mopria                NsdServiceInfo serviceInfo,
1053bf66744d61d18c66d46f2608de0467ad3df0268Mopria                NsdManager.ResolveListener listener) {
1063bf66744d61d18c66d46f2608de0467ad3df0268Mopria            this.nsdManager = nsdManager;
1073bf66744d61d18c66d46f2608de0467ad3df0268Mopria            this.serviceInfo = serviceInfo;
1083bf66744d61d18c66d46f2608de0467ad3df0268Mopria            this.listener = listener;
1093bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
1103bf66744d61d18c66d46f2608de0467ad3df0268Mopria
1113bf66744d61d18c66d46f2608de0467ad3df0268Mopria        public void start() {
1123bf66744d61d18c66d46f2608de0467ad3df0268Mopria            mStartTime = System.currentTimeMillis();
1133bf66744d61d18c66d46f2608de0467ad3df0268Mopria            if (DEBUG) Log.d(TAG, "resolveService " + serviceInfo.getServiceName());
1143bf66744d61d18c66d46f2608de0467ad3df0268Mopria            nsdManager.resolveService(serviceInfo, this);
1153bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
1163bf66744d61d18c66d46f2608de0467ad3df0268Mopria
1173bf66744d61d18c66d46f2608de0467ad3df0268Mopria        @Override
1183bf66744d61d18c66d46f2608de0467ad3df0268Mopria        public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
1193bf66744d61d18c66d46f2608de0467ad3df0268Mopria            if (DEBUG) {
1203bf66744d61d18c66d46f2608de0467ad3df0268Mopria                Log.d(TAG, "onResolveFailed " + serviceInfo.getServiceName() + " errorCode=" +
1213bf66744d61d18c66d46f2608de0467ad3df0268Mopria                        errorCode + " (" + (System.currentTimeMillis() - mStartTime) + " ms)");
1223bf66744d61d18c66d46f2608de0467ad3df0268Mopria            }
1233bf66744d61d18c66d46f2608de0467ad3df0268Mopria            mMainHandler.post(() -> {
1243bf66744d61d18c66d46f2608de0467ad3df0268Mopria                listener.onResolveFailed(serviceInfo, errorCode);
1253bf66744d61d18c66d46f2608de0467ad3df0268Mopria                mResolveRequests.pop();
1263bf66744d61d18c66d46f2608de0467ad3df0268Mopria                resolveNextRequest();
1273bf66744d61d18c66d46f2608de0467ad3df0268Mopria            });
1283bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
1293bf66744d61d18c66d46f2608de0467ad3df0268Mopria
1303bf66744d61d18c66d46f2608de0467ad3df0268Mopria        @Override
1313bf66744d61d18c66d46f2608de0467ad3df0268Mopria        public void onServiceResolved(NsdServiceInfo serviceInfo) {
1323bf66744d61d18c66d46f2608de0467ad3df0268Mopria            if (DEBUG) {
1333bf66744d61d18c66d46f2608de0467ad3df0268Mopria                Log.d(TAG, "onServiceResolved " + serviceInfo.getServiceName() +
1343bf66744d61d18c66d46f2608de0467ad3df0268Mopria                        " (" + (System.currentTimeMillis() - mStartTime) + " ms)");
1353bf66744d61d18c66d46f2608de0467ad3df0268Mopria            }
1363bf66744d61d18c66d46f2608de0467ad3df0268Mopria            mMainHandler.post(() -> {
1373bf66744d61d18c66d46f2608de0467ad3df0268Mopria                listener.onServiceResolved(serviceInfo);
1383bf66744d61d18c66d46f2608de0467ad3df0268Mopria                mResolveRequests.pop();
1393bf66744d61d18c66d46f2608de0467ad3df0268Mopria                resolveNextRequest();
1403bf66744d61d18c66d46f2608de0467ad3df0268Mopria            });
1413bf66744d61d18c66d46f2608de0467ad3df0268Mopria        }
1423bf66744d61d18c66d46f2608de0467ad3df0268Mopria    }
1433bf66744d61d18c66d46f2608de0467ad3df0268Mopria}