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