1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "nsCOMPtr.h"
6 #include "nsProxyRelease.h"
7 #include "nsComponentManagerUtils.h"
8 #include "nsServiceManagerUtils.h"
9 #include "nsThreadUtils.h"
10 #include "nsXPCOM.h"
11 #include "nsXPCOMCID.h"
12 #include "nsIObserver.h"
13 #include "nsIObserverService.h"
14 #include "nsWifiMonitor.h"
15 #include "nsWifiAccessPoint.h"
16
17 #include "nsServiceManagerUtils.h"
18 #include "nsComponentManagerUtils.h"
19 #include "mozilla/Services.h"
20
21 using namespace mozilla;
22
23 LazyLogModule gWifiMonitorLog("WifiMonitor");
24
NS_IMPL_ISUPPORTS(nsWifiMonitor,nsIRunnable,nsIObserver,nsIWifiMonitor)25 NS_IMPL_ISUPPORTS(nsWifiMonitor,
26 nsIRunnable,
27 nsIObserver,
28 nsIWifiMonitor)
29
30 nsWifiMonitor::nsWifiMonitor()
31 : mKeepGoing(true)
32 , mThreadComplete(false)
33 , mReentrantMonitor("nsWifiMonitor.mReentrantMonitor")
34 {
35 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
36 if (obsSvc)
37 obsSvc->AddObserver(this, "xpcom-shutdown", false);
38
39 LOG(("@@@@@ wifimonitor created\n"));
40 }
41
~nsWifiMonitor()42 nsWifiMonitor::~nsWifiMonitor()
43 {
44 }
45
46 NS_IMETHODIMP
Observe(nsISupports * subject,const char * topic,const char16_t * data)47 nsWifiMonitor::Observe(nsISupports *subject, const char *topic,
48 const char16_t *data)
49 {
50 if (!strcmp(topic, "xpcom-shutdown")) {
51 LOG(("Shutting down\n"));
52
53 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
54 mKeepGoing = false;
55 mon.Notify();
56 mThread = nullptr;
57 }
58 return NS_OK;
59 }
60
61
StartWatching(nsIWifiListener * aListener)62 NS_IMETHODIMP nsWifiMonitor::StartWatching(nsIWifiListener *aListener)
63 {
64 LOG(("nsWifiMonitor::StartWatching %p thread %p listener %p\n",
65 this, mThread.get(), aListener));
66 MOZ_ASSERT(NS_IsMainThread());
67
68 if (!aListener)
69 return NS_ERROR_NULL_POINTER;
70 if (!mKeepGoing) {
71 return NS_ERROR_NOT_AVAILABLE;
72 }
73
74 nsresult rv = NS_OK;
75
76 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
77 if (mThreadComplete) {
78 // generally there is just one thread for the lifetime of the service,
79 // but if DoScan returns with an error before shutdown (i.e. !mKeepGoing)
80 // then we will respawn the thread.
81 LOG(("nsWifiMonitor::StartWatching %p restarting thread\n", this));
82 mThreadComplete = false;
83 mThread = nullptr;
84 }
85
86 if (!mThread) {
87 rv = NS_NewThread(getter_AddRefs(mThread), this);
88 if (NS_FAILED(rv))
89 return rv;
90 }
91
92
93 mListeners.AppendElement(nsWifiListener(new nsMainThreadPtrHolder<nsIWifiListener>(aListener)));
94
95 // tell ourselves that we have a new watcher.
96 mon.Notify();
97 return NS_OK;
98 }
99
StopWatching(nsIWifiListener * aListener)100 NS_IMETHODIMP nsWifiMonitor::StopWatching(nsIWifiListener *aListener)
101 {
102 LOG(("nsWifiMonitor::StopWatching %p thread %p listener %p\n",
103 this, mThread.get(), aListener));
104 MOZ_ASSERT(NS_IsMainThread());
105
106 if (!aListener)
107 return NS_ERROR_NULL_POINTER;
108
109 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
110
111 for (uint32_t i = 0; i < mListeners.Length(); i++) {
112
113 if (mListeners[i].mListener == aListener) {
114 mListeners.RemoveElementAt(i);
115 break;
116 }
117 }
118
119 return NS_OK;
120 }
121
122 typedef nsTArray<nsMainThreadPtrHandle<nsIWifiListener> > WifiListenerArray;
123
124 class nsPassErrorToWifiListeners final : public nsIRunnable
125 {
126 public:
127 NS_DECL_THREADSAFE_ISUPPORTS
128 NS_DECL_NSIRUNNABLE
129
nsPassErrorToWifiListeners(nsAutoPtr<WifiListenerArray> aListeners,nsresult aResult)130 nsPassErrorToWifiListeners(nsAutoPtr<WifiListenerArray> aListeners,
131 nsresult aResult)
132 : mListeners(aListeners),
133 mResult(aResult)
134 {}
135
136 private:
~nsPassErrorToWifiListeners()137 ~nsPassErrorToWifiListeners() {}
138 nsAutoPtr<WifiListenerArray> mListeners;
139 nsresult mResult;
140 };
141
NS_IMPL_ISUPPORTS(nsPassErrorToWifiListeners,nsIRunnable)142 NS_IMPL_ISUPPORTS(nsPassErrorToWifiListeners,
143 nsIRunnable)
144
145 NS_IMETHODIMP nsPassErrorToWifiListeners::Run()
146 {
147 LOG(("About to send error to the wifi listeners\n"));
148 for (size_t i = 0; i < mListeners->Length(); i++) {
149 (*mListeners)[i]->OnError(mResult);
150 }
151 return NS_OK;
152 }
153
Run()154 NS_IMETHODIMP nsWifiMonitor::Run()
155 {
156 LOG(("@@@@@ wifi monitor run called\n"));
157
158 PR_SetCurrentThreadName("Wifi Monitor");
159
160 nsresult rv = DoScan();
161 LOG(("@@@@@ wifi monitor run::doscan complete %x\n", rv));
162
163 nsAutoPtr<WifiListenerArray> currentListeners;
164 bool doError = false;
165
166 {
167 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
168 if (mKeepGoing && NS_FAILED(rv)) {
169 doError = true;
170 currentListeners = new WifiListenerArray(mListeners.Length());
171 for (uint32_t i = 0; i < mListeners.Length(); i++)
172 currentListeners->AppendElement(mListeners[i].mListener);
173 }
174 mThreadComplete = true;
175 }
176
177 if (doError) {
178 nsCOMPtr<nsIThread> thread = do_GetMainThread();
179 if (!thread)
180 return NS_ERROR_UNEXPECTED;
181
182 nsCOMPtr<nsIRunnable> runnable(new nsPassErrorToWifiListeners(currentListeners, rv));
183 if (!runnable)
184 return NS_ERROR_OUT_OF_MEMORY;
185
186 thread->Dispatch(runnable, NS_DISPATCH_SYNC);
187 }
188
189 LOG(("@@@@@ wifi monitor run complete\n"));
190 return NS_OK;
191 }
192
193 class nsCallWifiListeners final : public nsIRunnable
194 {
195 public:
196 NS_DECL_THREADSAFE_ISUPPORTS
197 NS_DECL_NSIRUNNABLE
198
nsCallWifiListeners(nsAutoPtr<WifiListenerArray> aListeners,nsAutoPtr<nsTArray<nsIWifiAccessPoint * >> aAccessPoints)199 nsCallWifiListeners(nsAutoPtr<WifiListenerArray> aListeners,
200 nsAutoPtr<nsTArray<nsIWifiAccessPoint*> > aAccessPoints)
201 : mListeners(aListeners),
202 mAccessPoints(aAccessPoints)
203 {}
204
205 private:
~nsCallWifiListeners()206 ~nsCallWifiListeners() {}
207 nsAutoPtr<WifiListenerArray> mListeners;
208 nsAutoPtr<nsTArray<nsIWifiAccessPoint*> > mAccessPoints;
209 };
210
NS_IMPL_ISUPPORTS(nsCallWifiListeners,nsIRunnable)211 NS_IMPL_ISUPPORTS(nsCallWifiListeners,
212 nsIRunnable)
213
214 NS_IMETHODIMP nsCallWifiListeners::Run()
215 {
216 LOG(("About to send data to the wifi listeners\n"));
217 for (size_t i = 0; i < mListeners->Length(); i++) {
218 (*mListeners)[i]->OnChange(mAccessPoints->Elements(), mAccessPoints->Length());
219 }
220 return NS_OK;
221 }
222
223 nsresult
CallWifiListeners(const nsCOMArray<nsWifiAccessPoint> & aAccessPoints,bool aAccessPointsChanged)224 nsWifiMonitor::CallWifiListeners(const nsCOMArray<nsWifiAccessPoint> &aAccessPoints,
225 bool aAccessPointsChanged)
226 {
227 nsAutoPtr<WifiListenerArray> currentListeners;
228 {
229 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
230
231 currentListeners = new WifiListenerArray(mListeners.Length());
232
233 for (uint32_t i = 0; i < mListeners.Length(); i++) {
234 if (!mListeners[i].mHasSentData || aAccessPointsChanged) {
235 mListeners[i].mHasSentData = true;
236 currentListeners->AppendElement(mListeners[i].mListener);
237 }
238 }
239 }
240
241 if (currentListeners->Length() > 0)
242 {
243 uint32_t resultCount = aAccessPoints.Count();
244 nsAutoPtr<nsTArray<nsIWifiAccessPoint*> > accessPoints(
245 new nsTArray<nsIWifiAccessPoint *>(resultCount));
246 if (!accessPoints)
247 return NS_ERROR_OUT_OF_MEMORY;
248
249 for (uint32_t i = 0; i < resultCount; i++)
250 accessPoints->AppendElement(aAccessPoints[i]);
251
252 nsCOMPtr<nsIThread> thread = do_GetMainThread();
253 if (!thread)
254 return NS_ERROR_UNEXPECTED;
255
256 nsCOMPtr<nsIRunnable> runnable(
257 new nsCallWifiListeners(currentListeners, accessPoints));
258 if (!runnable)
259 return NS_ERROR_OUT_OF_MEMORY;
260
261 thread->Dispatch(runnable, NS_DISPATCH_SYNC);
262 }
263
264 return NS_OK;
265 }
266