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.printservice.recommendation.util;
18 
19 import android.net.nsd.NsdManager;
20 import android.net.nsd.NsdServiceInfo;
21 
22 import androidx.annotation.GuardedBy;
23 import androidx.annotation.NonNull;
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 public class NsdResolveQueue {
32     /** Lock for {@link #sInstance} */
33     private static final Object sLock = new Object();
34 
35     /** Instance of this singleton */
36     @GuardedBy("sLock")
37     private static NsdResolveQueue sInstance;
38 
39     /** Lock for {@link #mResolveRequests} */
40     private final Object mLock = new Object();
41 
42     /** Current set of registered service info resolve attempts */
43     @GuardedBy("mLock")
44     private final LinkedList<NsdResolveRequest> mResolveRequests = new LinkedList<>();
45 
getInstance()46     public static NsdResolveQueue getInstance() {
47         synchronized (sLock) {
48             if (sInstance == null) {
49                 sInstance = new NsdResolveQueue();
50             }
51 
52             return sInstance;
53         }
54     }
55 
56     /**
57      * Container for a request to resolve a serviceInfo.
58      */
59     private static class NsdResolveRequest {
60         final @NonNull NsdManager nsdManager;
61         final @NonNull NsdServiceInfo serviceInfo;
62         final @NonNull NsdManager.ResolveListener listener;
63 
NsdResolveRequest(@onNull NsdManager nsdManager, @NonNull NsdServiceInfo serviceInfo, @NonNull NsdManager.ResolveListener listener)64         private NsdResolveRequest(@NonNull NsdManager nsdManager,
65                 @NonNull NsdServiceInfo serviceInfo, @NonNull NsdManager.ResolveListener listener) {
66             this.nsdManager = nsdManager;
67             this.serviceInfo = serviceInfo;
68             this.listener = listener;
69         }
70     }
71 
72     /**
73      * Resolve a serviceInfo or queue the request if there is a request currently in flight.
74      *
75      * @param nsdManager  The nsd manager to use
76      * @param serviceInfo The service info to resolve
77      * @param listener    The listener to call back once the info is resolved.
78      */
resolve(@onNull NsdManager nsdManager, @NonNull NsdServiceInfo serviceInfo, @NonNull NsdManager.ResolveListener listener)79     public void resolve(@NonNull NsdManager nsdManager, @NonNull NsdServiceInfo serviceInfo,
80             @NonNull NsdManager.ResolveListener listener) {
81         synchronized (mLock) {
82             mResolveRequests.addLast(new NsdResolveRequest(nsdManager, serviceInfo,
83                     new ListenerWrapper(listener)));
84 
85             if (mResolveRequests.size() == 1) {
86                 resolveNextRequest();
87             }
88         }
89     }
90 
91     /**
92      * Wrapper for a {@link NsdManager.ResolveListener}. Calls the listener and then
93      * {@link #resolveNextRequest()}.
94      */
95     private class ListenerWrapper implements NsdManager.ResolveListener {
96         private final @NonNull NsdManager.ResolveListener mListener;
97 
ListenerWrapper(@onNull NsdManager.ResolveListener listener)98         private ListenerWrapper(@NonNull NsdManager.ResolveListener listener) {
99             mListener = listener;
100         }
101 
102         @Override
onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)103         public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
104             mListener.onResolveFailed(serviceInfo, errorCode);
105 
106             synchronized (mLock) {
107                 mResolveRequests.pop();
108                 resolveNextRequest();
109             }
110         }
111 
112         @Override
onServiceResolved(NsdServiceInfo serviceInfo)113         public void onServiceResolved(NsdServiceInfo serviceInfo) {
114             mListener.onServiceResolved(serviceInfo);
115 
116             synchronized (mLock) {
117                 mResolveRequests.pop();
118                 resolveNextRequest();
119             }
120         }
121     }
122 
123     /**
124      * Resolve the next request if there is one.
125      */
resolveNextRequest()126     private void resolveNextRequest() {
127         if (!mResolveRequests.isEmpty()) {
128             NsdResolveRequest request = mResolveRequests.getFirst();
129 
130             request.nsdManager.resolveService(request.serviceInfo, request.listener);
131         }
132     }
133 
134 }
135