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