|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "Tickler.h" |
|
7 |
|
8 #ifdef MOZ_USE_WIFI_TICKLER |
|
9 #include "nsComponentManagerUtils.h" |
|
10 #include "nsIPrefBranch.h" |
|
11 #include "nsIPrefService.h" |
|
12 #include "nsServiceManagerUtils.h" |
|
13 #include "nsThreadUtils.h" |
|
14 #include "prnetdb.h" |
|
15 |
|
16 #include "AndroidBridge.h" |
|
17 |
|
18 namespace mozilla { |
|
19 namespace net { |
|
20 |
|
21 NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler) |
|
22 |
|
23 Tickler::Tickler() |
|
24 : mLock("Tickler::mLock") |
|
25 , mActive(false) |
|
26 , mCanceled(false) |
|
27 , mEnabled(false) |
|
28 , mDelay(16) |
|
29 , mDuration(TimeDuration::FromMilliseconds(400)) |
|
30 , mFD(nullptr) |
|
31 { |
|
32 MOZ_ASSERT(NS_IsMainThread()); |
|
33 } |
|
34 |
|
35 class TicklerThreadDestructor : public nsRunnable |
|
36 { |
|
37 public: |
|
38 explicit TicklerThreadDestructor(nsIThread *aThread) |
|
39 : mThread(aThread) { } |
|
40 |
|
41 NS_IMETHOD Run() MOZ_OVERRIDE |
|
42 { |
|
43 MOZ_ASSERT(NS_IsMainThread()); |
|
44 if (mThread) |
|
45 mThread->Shutdown(); |
|
46 return NS_OK; |
|
47 } |
|
48 |
|
49 private: |
|
50 ~TicklerThreadDestructor() { } |
|
51 nsCOMPtr<nsIThread> mThread; |
|
52 }; |
|
53 |
|
54 Tickler::~Tickler() |
|
55 { |
|
56 // non main thread uses of the tickler should hold weak |
|
57 // references to it if they must hold a reference at all |
|
58 MOZ_ASSERT(NS_IsMainThread()); |
|
59 |
|
60 // Shutting down a thread can spin the event loop - which is a surprising |
|
61 // thing to do from a dtor. Running it on its own event is safer. |
|
62 nsRefPtr<nsIRunnable> event = new TicklerThreadDestructor(mThread); |
|
63 if (NS_FAILED(NS_DispatchToCurrentThread(event))) { |
|
64 mThread->Shutdown(); |
|
65 } |
|
66 mThread = nullptr; |
|
67 |
|
68 if (mTimer) |
|
69 mTimer->Cancel(); |
|
70 if (mFD) |
|
71 PR_Close(mFD); |
|
72 } |
|
73 |
|
74 nsresult |
|
75 Tickler::Init() |
|
76 { |
|
77 MOZ_ASSERT(NS_IsMainThread()); |
|
78 MOZ_ASSERT(!mTimer); |
|
79 MOZ_ASSERT(!mActive); |
|
80 MOZ_ASSERT(!mThread); |
|
81 MOZ_ASSERT(!mFD); |
|
82 |
|
83 if (AndroidBridge::HasEnv()) { |
|
84 mozilla::widget::android::GeckoAppShell::EnableNetworkNotifications(); |
|
85 } |
|
86 |
|
87 mFD = PR_OpenUDPSocket(PR_AF_INET); |
|
88 if (!mFD) |
|
89 return NS_ERROR_FAILURE; |
|
90 |
|
91 // make sure new socket has a ttl of 1 |
|
92 // failure is not fatal. |
|
93 PRSocketOptionData opt; |
|
94 opt.option = PR_SockOpt_IpTimeToLive; |
|
95 opt.value.ip_ttl = 1; |
|
96 PR_SetSocketOption(mFD, &opt); |
|
97 |
|
98 nsresult rv = NS_NewNamedThread("wifi tickler", |
|
99 getter_AddRefs(mThread)); |
|
100 if (NS_FAILED(rv)) |
|
101 return rv; |
|
102 |
|
103 nsCOMPtr<nsITimer> tmpTimer(do_CreateInstance(NS_TIMER_CONTRACTID, &rv)); |
|
104 if (NS_FAILED(rv)) |
|
105 return rv; |
|
106 |
|
107 rv = tmpTimer->SetTarget(mThread); |
|
108 if (NS_FAILED(rv)) |
|
109 return rv; |
|
110 |
|
111 mTimer.swap(tmpTimer); |
|
112 |
|
113 mAddr.inet.family = PR_AF_INET; |
|
114 mAddr.inet.port = PR_htons (4886); |
|
115 mAddr.inet.ip = 0; |
|
116 |
|
117 return NS_OK; |
|
118 } |
|
119 |
|
120 void Tickler::Tickle() |
|
121 { |
|
122 MutexAutoLock lock(mLock); |
|
123 MOZ_ASSERT(mThread); |
|
124 mLastTickle = TimeStamp::Now(); |
|
125 if (!mActive) |
|
126 MaybeStartTickler(); |
|
127 } |
|
128 |
|
129 void Tickler::PostCheckTickler() |
|
130 { |
|
131 mLock.AssertCurrentThreadOwns(); |
|
132 mThread->Dispatch(NS_NewRunnableMethod(this, &Tickler::CheckTickler), |
|
133 NS_DISPATCH_NORMAL); |
|
134 return; |
|
135 } |
|
136 |
|
137 void Tickler::MaybeStartTicklerUnlocked() |
|
138 { |
|
139 MutexAutoLock lock(mLock); |
|
140 MaybeStartTickler(); |
|
141 } |
|
142 |
|
143 void Tickler::MaybeStartTickler() |
|
144 { |
|
145 mLock.AssertCurrentThreadOwns(); |
|
146 if (!NS_IsMainThread()) { |
|
147 NS_DispatchToMainThread( |
|
148 NS_NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked), |
|
149 NS_DISPATCH_NORMAL); |
|
150 return; |
|
151 } |
|
152 |
|
153 if (!mPrefs) |
|
154 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
155 if (mPrefs) { |
|
156 int32_t val; |
|
157 bool boolVal; |
|
158 |
|
159 if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal))) |
|
160 mEnabled = boolVal; |
|
161 |
|
162 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) { |
|
163 if (val < 1) |
|
164 val = 1; |
|
165 if (val > 100000) |
|
166 val = 100000; |
|
167 mDuration = TimeDuration::FromMilliseconds(val); |
|
168 } |
|
169 |
|
170 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) { |
|
171 if (val < 1) |
|
172 val = 1; |
|
173 if (val > 1000) |
|
174 val = 1000; |
|
175 mDelay = static_cast<uint32_t>(val); |
|
176 } |
|
177 } |
|
178 |
|
179 PostCheckTickler(); |
|
180 } |
|
181 |
|
182 void Tickler::CheckTickler() |
|
183 { |
|
184 MutexAutoLock lock(mLock); |
|
185 MOZ_ASSERT(mThread == NS_GetCurrentThread()); |
|
186 |
|
187 bool shouldRun = (!mCanceled) && |
|
188 ((TimeStamp::Now() - mLastTickle) <= mDuration); |
|
189 |
|
190 if ((shouldRun && mActive) || (!shouldRun && !mActive)) |
|
191 return; // no change in state |
|
192 |
|
193 if (mActive) |
|
194 StopTickler(); |
|
195 else |
|
196 StartTickler(); |
|
197 } |
|
198 |
|
199 void Tickler::Cancel() |
|
200 { |
|
201 MutexAutoLock lock(mLock); |
|
202 MOZ_ASSERT(NS_IsMainThread()); |
|
203 mCanceled = true; |
|
204 if (mThread) |
|
205 PostCheckTickler(); |
|
206 } |
|
207 |
|
208 void Tickler::StopTickler() |
|
209 { |
|
210 mLock.AssertCurrentThreadOwns(); |
|
211 MOZ_ASSERT(mThread == NS_GetCurrentThread()); |
|
212 MOZ_ASSERT(mTimer); |
|
213 MOZ_ASSERT(mActive); |
|
214 |
|
215 mTimer->Cancel(); |
|
216 mActive = false; |
|
217 } |
|
218 |
|
219 class TicklerTimer MOZ_FINAL : public nsITimerCallback |
|
220 { |
|
221 NS_DECL_THREADSAFE_ISUPPORTS |
|
222 NS_DECL_NSITIMERCALLBACK |
|
223 |
|
224 TicklerTimer(Tickler *aTickler) |
|
225 { |
|
226 mTickler = do_GetWeakReference(aTickler); |
|
227 } |
|
228 |
|
229 ~TicklerTimer() {}; |
|
230 |
|
231 private: |
|
232 nsWeakPtr mTickler; |
|
233 }; |
|
234 |
|
235 void Tickler::StartTickler() |
|
236 { |
|
237 mLock.AssertCurrentThreadOwns(); |
|
238 MOZ_ASSERT(mThread == NS_GetCurrentThread()); |
|
239 MOZ_ASSERT(!mActive); |
|
240 MOZ_ASSERT(mTimer); |
|
241 |
|
242 if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this), |
|
243 mEnabled ? mDelay : 1000, |
|
244 nsITimer::TYPE_REPEATING_SLACK))) |
|
245 mActive = true; |
|
246 } |
|
247 |
|
248 // argument should be in network byte order |
|
249 void Tickler::SetIPV4Address(uint32_t address) |
|
250 { |
|
251 mAddr.inet.ip = address; |
|
252 } |
|
253 |
|
254 // argument should be in network byte order |
|
255 void Tickler::SetIPV4Port(uint16_t port) |
|
256 { |
|
257 mAddr.inet.port = port; |
|
258 } |
|
259 |
|
260 NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback) |
|
261 |
|
262 NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer) |
|
263 { |
|
264 nsRefPtr<Tickler> tickler = do_QueryReferent(mTickler); |
|
265 if (!tickler) |
|
266 return NS_ERROR_FAILURE; |
|
267 MutexAutoLock lock(tickler->mLock); |
|
268 |
|
269 if (!tickler->mFD) { |
|
270 tickler->StopTickler(); |
|
271 return NS_ERROR_FAILURE; |
|
272 } |
|
273 |
|
274 if (tickler->mCanceled || |
|
275 ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) { |
|
276 tickler->StopTickler(); |
|
277 return NS_OK; |
|
278 } |
|
279 |
|
280 if (!tickler->mEnabled) |
|
281 return NS_OK; |
|
282 |
|
283 PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0); |
|
284 return NS_OK; |
|
285 } |
|
286 |
|
287 } // namespace mozilla::net |
|
288 } // namespace mozilla |
|
289 |
|
290 #else // not defined MOZ_USE_WIFI_TICKLER |
|
291 |
|
292 namespace mozilla { |
|
293 namespace net { |
|
294 NS_IMPL_ISUPPORTS0(Tickler) |
|
295 } // namespace mozilla::net |
|
296 } // namespace mozilla |
|
297 |
|
298 #endif // defined MOZ_USE_WIFI_TICKLER |
|
299 |