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/IntegerPrintfMacros.h"
20 #include "mozilla/Services.h"
21 
22 using namespace mozilla;
23 
24 LazyLogModule gWifiMonitorLog("WifiMonitor");
25 
NS_IMPL_ISUPPORTS(nsWifiMonitor,nsIRunnable,nsIObserver,nsIWifiMonitor)26 NS_IMPL_ISUPPORTS(nsWifiMonitor, nsIRunnable, nsIObserver, nsIWifiMonitor)
27 
28 nsWifiMonitor::nsWifiMonitor()
29     : mKeepGoing(true),
30       mThreadComplete(false),
31       mReentrantMonitor("nsWifiMonitor.mReentrantMonitor") {
32   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
33   if (obsSvc) obsSvc->AddObserver(this, "xpcom-shutdown", false);
34 
35   LOG(("@@@@@ wifimonitor created\n"));
36 }
37 
38 NS_IMETHODIMP
Observe(nsISupports * subject,const char * topic,const char16_t * data)39 nsWifiMonitor::Observe(nsISupports* subject, const char* topic,
40                        const char16_t* data) {
41   if (!strcmp(topic, "xpcom-shutdown")) {
42     LOG(("Shutting down\n"));
43 
44     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
45     mKeepGoing = false;
46     mon.Notify();
47     mThread = nullptr;
48   }
49   return NS_OK;
50 }
51 
StartWatching(nsIWifiListener * aListener)52 NS_IMETHODIMP nsWifiMonitor::StartWatching(nsIWifiListener* aListener) {
53   LOG(("nsWifiMonitor::StartWatching %p thread %p listener %p\n", this,
54        mThread.get(), aListener));
55   MOZ_ASSERT(NS_IsMainThread());
56 
57   if (!aListener) return NS_ERROR_NULL_POINTER;
58   if (!mKeepGoing) {
59     return NS_ERROR_NOT_AVAILABLE;
60   }
61 
62   nsresult rv = NS_OK;
63 
64   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
65   if (mThreadComplete) {
66     // generally there is just one thread for the lifetime of the service,
67     // but if DoScan returns with an error before shutdown (i.e. !mKeepGoing)
68     // then we will respawn the thread.
69     LOG(("nsWifiMonitor::StartWatching %p restarting thread\n", this));
70     mThreadComplete = false;
71     mThread = nullptr;
72   }
73 
74   if (!mThread) {
75     rv = NS_NewNamedThread("Wifi Monitor", getter_AddRefs(mThread), this);
76     if (NS_FAILED(rv)) return rv;
77   }
78 
79   mListeners.AppendElement(
80       nsWifiListener(new nsMainThreadPtrHolder<nsIWifiListener>(
81           "nsIWifiListener", aListener)));
82 
83   // tell ourselves that we have a new watcher.
84   mon.Notify();
85   return NS_OK;
86 }
87 
StopWatching(nsIWifiListener * aListener)88 NS_IMETHODIMP nsWifiMonitor::StopWatching(nsIWifiListener* aListener) {
89   LOG(("nsWifiMonitor::StopWatching %p thread %p listener %p\n", this,
90        mThread.get(), aListener));
91   MOZ_ASSERT(NS_IsMainThread());
92 
93   if (!aListener) return NS_ERROR_NULL_POINTER;
94 
95   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
96 
97   for (uint32_t i = 0; i < mListeners.Length(); i++) {
98     if (mListeners[i].mListener == aListener) {
99       mListeners.RemoveElementAt(i);
100       break;
101     }
102   }
103 
104   return NS_OK;
105 }
106 
107 using WifiListenerArray = nsTArray<nsMainThreadPtrHandle<nsIWifiListener>>;
108 
109 class nsPassErrorToWifiListeners final : public nsIRunnable {
110  public:
111   NS_DECL_THREADSAFE_ISUPPORTS
112   NS_DECL_NSIRUNNABLE
113 
nsPassErrorToWifiListeners(UniquePtr<WifiListenerArray> && aListeners,nsresult aResult)114   nsPassErrorToWifiListeners(UniquePtr<WifiListenerArray>&& aListeners,
115                              nsresult aResult)
116       : mListeners(std::move(aListeners)), mResult(aResult) {}
117 
118  private:
119   ~nsPassErrorToWifiListeners() = default;
120   UniquePtr<WifiListenerArray> mListeners;
121   nsresult mResult;
122 };
123 
NS_IMPL_ISUPPORTS(nsPassErrorToWifiListeners,nsIRunnable)124 NS_IMPL_ISUPPORTS(nsPassErrorToWifiListeners, nsIRunnable)
125 
126 NS_IMETHODIMP nsPassErrorToWifiListeners::Run() {
127   LOG(("About to send error to the wifi listeners\n"));
128   for (size_t i = 0; i < mListeners->Length(); i++) {
129     (*mListeners)[i]->OnError(mResult);
130   }
131   return NS_OK;
132 }
133 
Run()134 NS_IMETHODIMP nsWifiMonitor::Run() {
135   LOG(("@@@@@ wifi monitor run called\n"));
136 
137   nsresult rv = DoScan();
138   LOG(("@@@@@ wifi monitor run::doscan complete %" PRIx32 "\n",
139        static_cast<uint32_t>(rv)));
140 
141   UniquePtr<WifiListenerArray> currentListeners;
142   bool doError = false;
143 
144   {
145     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
146     if (mKeepGoing && NS_FAILED(rv)) {
147       doError = true;
148       currentListeners = MakeUnique<WifiListenerArray>(mListeners.Length());
149       for (uint32_t i = 0; i < mListeners.Length(); i++) {
150         currentListeners->AppendElement(mListeners[i].mListener);
151       }
152     }
153     mThreadComplete = true;
154   }
155 
156   if (doError) {
157     nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
158     if (!target) return NS_ERROR_UNEXPECTED;
159 
160     nsCOMPtr<nsIRunnable> runnable(
161         new nsPassErrorToWifiListeners(std::move(currentListeners), rv));
162     if (!runnable) return NS_ERROR_OUT_OF_MEMORY;
163 
164     target->Dispatch(runnable, NS_DISPATCH_SYNC);
165   }
166 
167   LOG(("@@@@@ wifi monitor run complete\n"));
168   return NS_OK;
169 }
170 
171 class nsCallWifiListeners final : public nsIRunnable {
172  public:
173   NS_DECL_THREADSAFE_ISUPPORTS
174   NS_DECL_NSIRUNNABLE
175 
nsCallWifiListeners(WifiListenerArray && aListeners,nsTArray<RefPtr<nsIWifiAccessPoint>> && aAccessPoints)176   nsCallWifiListeners(WifiListenerArray&& aListeners,
177                       nsTArray<RefPtr<nsIWifiAccessPoint>>&& aAccessPoints)
178       : mListeners(std::move(aListeners)),
179         mAccessPoints(std::move(aAccessPoints)) {}
180 
181  private:
182   ~nsCallWifiListeners() = default;
183   const WifiListenerArray mListeners;
184   const nsTArray<RefPtr<nsIWifiAccessPoint>> mAccessPoints;
185 };
186 
NS_IMPL_ISUPPORTS(nsCallWifiListeners,nsIRunnable)187 NS_IMPL_ISUPPORTS(nsCallWifiListeners, nsIRunnable)
188 
189 NS_IMETHODIMP nsCallWifiListeners::Run() {
190   LOG(("About to send data to the wifi listeners\n"));
191   for (const auto& listener : mListeners) {
192     listener->OnChange(mAccessPoints);
193   }
194   return NS_OK;
195 }
196 
CallWifiListeners(const nsCOMArray<nsWifiAccessPoint> & aAccessPoints,bool aAccessPointsChanged)197 nsresult nsWifiMonitor::CallWifiListeners(
198     const nsCOMArray<nsWifiAccessPoint>& aAccessPoints,
199     bool aAccessPointsChanged) {
200   WifiListenerArray currentListeners;
201   {
202     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
203 
204     currentListeners.SetCapacity(mListeners.Length());
205 
206     for (auto& listener : mListeners) {
207       if (!listener.mHasSentData || aAccessPointsChanged) {
208         listener.mHasSentData = true;
209         currentListeners.AppendElement(listener.mListener);
210       }
211     }
212   }
213 
214   if (!currentListeners.IsEmpty()) {
215     uint32_t resultCount = aAccessPoints.Count();
216     nsTArray<RefPtr<nsIWifiAccessPoint>> accessPoints(resultCount);
217 
218     for (uint32_t i = 0; i < resultCount; i++) {
219       accessPoints.AppendElement(aAccessPoints[i]);
220     }
221 
222     nsCOMPtr<nsIThread> thread = do_GetMainThread();
223     if (!thread) return NS_ERROR_UNEXPECTED;
224 
225     nsCOMPtr<nsIRunnable> runnable(new nsCallWifiListeners(
226         std::move(currentListeners), std::move(accessPoints)));
227     if (!runnable) return NS_ERROR_OUT_OF_MEMORY;
228 
229     thread->Dispatch(runnable, NS_DISPATCH_SYNC);
230   }
231 
232   return NS_OK;
233 }
234