|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "NetworkActivityMonitor.h" |
|
8 #include "prmem.h" |
|
9 #include "nsIObserverService.h" |
|
10 #include "nsPISocketTransportService.h" |
|
11 #include "nsSocketTransportService2.h" |
|
12 #include "nsThreadUtils.h" |
|
13 #include "mozilla/Services.h" |
|
14 #include "prerror.h" |
|
15 |
|
16 using namespace mozilla::net; |
|
17 |
|
18 static PRStatus |
|
19 nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) |
|
20 { |
|
21 PRStatus ret; |
|
22 PRErrorCode code; |
|
23 ret = fd->lower->methods->connect(fd->lower, addr, timeout); |
|
24 if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR || |
|
25 code == PR_IN_PROGRESS_ERROR) |
|
26 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); |
|
27 return ret; |
|
28 } |
|
29 |
|
30 static int32_t |
|
31 nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len) |
|
32 { |
|
33 int32_t ret; |
|
34 ret = fd->lower->methods->read(fd->lower, buf, len); |
|
35 if (ret >= 0) |
|
36 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); |
|
37 return ret; |
|
38 } |
|
39 |
|
40 static int32_t |
|
41 nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len) |
|
42 { |
|
43 int32_t ret; |
|
44 ret = fd->lower->methods->write(fd->lower, buf, len); |
|
45 if (ret > 0) |
|
46 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); |
|
47 return ret; |
|
48 } |
|
49 |
|
50 static int32_t |
|
51 nsNetMon_Writev(PRFileDesc *fd, |
|
52 const PRIOVec *iov, |
|
53 int32_t size, |
|
54 PRIntervalTime timeout) |
|
55 { |
|
56 int32_t ret; |
|
57 ret = fd->lower->methods->writev(fd->lower, iov, size, timeout); |
|
58 if (ret > 0) |
|
59 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); |
|
60 return ret; |
|
61 } |
|
62 |
|
63 static int32_t |
|
64 nsNetMon_Recv(PRFileDesc *fd, |
|
65 void *buf, |
|
66 int32_t amount, |
|
67 int flags, |
|
68 PRIntervalTime timeout) |
|
69 { |
|
70 int32_t ret; |
|
71 ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout); |
|
72 if (ret >= 0) |
|
73 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); |
|
74 return ret; |
|
75 } |
|
76 |
|
77 static int32_t |
|
78 nsNetMon_Send(PRFileDesc *fd, |
|
79 const void *buf, |
|
80 int32_t amount, |
|
81 int flags, |
|
82 PRIntervalTime timeout) |
|
83 { |
|
84 int32_t ret; |
|
85 ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout); |
|
86 if (ret > 0) |
|
87 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); |
|
88 return ret; |
|
89 } |
|
90 |
|
91 static int32_t |
|
92 nsNetMon_RecvFrom(PRFileDesc *fd, |
|
93 void *buf, |
|
94 int32_t amount, |
|
95 int flags, |
|
96 PRNetAddr *addr, |
|
97 PRIntervalTime timeout) |
|
98 { |
|
99 int32_t ret; |
|
100 ret = fd->lower->methods->recvfrom(fd->lower, |
|
101 buf, |
|
102 amount, |
|
103 flags, |
|
104 addr, |
|
105 timeout); |
|
106 if (ret >= 0) |
|
107 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); |
|
108 return ret; |
|
109 } |
|
110 |
|
111 static int32_t |
|
112 nsNetMon_SendTo(PRFileDesc *fd, |
|
113 const void *buf, |
|
114 int32_t amount, |
|
115 int flags, |
|
116 const PRNetAddr *addr, |
|
117 PRIntervalTime timeout) |
|
118 { |
|
119 int32_t ret; |
|
120 ret = fd->lower->methods->sendto(fd->lower, |
|
121 buf, |
|
122 amount, |
|
123 flags, |
|
124 addr, |
|
125 timeout); |
|
126 if (ret > 0) |
|
127 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload); |
|
128 return ret; |
|
129 } |
|
130 |
|
131 static int32_t |
|
132 nsNetMon_AcceptRead(PRFileDesc *listenSock, |
|
133 PRFileDesc **acceptedSock, |
|
134 PRNetAddr **peerAddr, |
|
135 void *buf, |
|
136 int32_t amount, |
|
137 PRIntervalTime timeout) |
|
138 { |
|
139 int32_t ret; |
|
140 ret = listenSock->lower->methods->acceptread(listenSock->lower, |
|
141 acceptedSock, |
|
142 peerAddr, |
|
143 buf, |
|
144 amount, |
|
145 timeout); |
|
146 if (ret > 0) |
|
147 NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload); |
|
148 return ret; |
|
149 } |
|
150 |
|
151 |
|
152 class NotifyNetworkActivity : public nsRunnable { |
|
153 public: |
|
154 NotifyNetworkActivity(NetworkActivityMonitor::Direction aDirection) |
|
155 : mDirection(aDirection) |
|
156 {} |
|
157 NS_IMETHOD Run() |
|
158 { |
|
159 MOZ_ASSERT(NS_IsMainThread()); |
|
160 |
|
161 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
|
162 if (!obs) |
|
163 return NS_ERROR_FAILURE; |
|
164 |
|
165 obs->NotifyObservers(nullptr, |
|
166 mDirection == NetworkActivityMonitor::kUpload |
|
167 ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC |
|
168 : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, |
|
169 nullptr); |
|
170 return NS_OK; |
|
171 } |
|
172 private: |
|
173 nsCOMPtr<nsIObserverService> mObs; |
|
174 NetworkActivityMonitor::Direction mDirection; |
|
175 }; |
|
176 |
|
177 NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr; |
|
178 |
|
179 NetworkActivityMonitor::NetworkActivityMonitor() |
|
180 : mLayerIdentity(PR_INVALID_IO_LAYER) |
|
181 , mBlipInterval(PR_INTERVAL_NO_TIMEOUT) |
|
182 { |
|
183 MOZ_COUNT_CTOR(NetworkActivityMonitor); |
|
184 |
|
185 NS_ASSERTION(gInstance==nullptr, |
|
186 "multiple NetworkActivityMonitor instances!"); |
|
187 } |
|
188 |
|
189 NetworkActivityMonitor::~NetworkActivityMonitor() |
|
190 { |
|
191 MOZ_COUNT_DTOR(NetworkActivityMonitor); |
|
192 gInstance = nullptr; |
|
193 } |
|
194 |
|
195 nsresult |
|
196 NetworkActivityMonitor::Init(int32_t blipInterval) |
|
197 { |
|
198 nsresult rv; |
|
199 |
|
200 if (gInstance) |
|
201 return NS_ERROR_ALREADY_INITIALIZED; |
|
202 |
|
203 NetworkActivityMonitor * mon = new NetworkActivityMonitor(); |
|
204 rv = mon->Init_Internal(blipInterval); |
|
205 if (NS_FAILED(rv)) { |
|
206 delete mon; |
|
207 return rv; |
|
208 } |
|
209 |
|
210 gInstance = mon; |
|
211 return NS_OK; |
|
212 } |
|
213 |
|
214 nsresult |
|
215 NetworkActivityMonitor::Shutdown() |
|
216 { |
|
217 if (!gInstance) |
|
218 return NS_ERROR_NOT_INITIALIZED; |
|
219 |
|
220 delete gInstance; |
|
221 return NS_OK; |
|
222 } |
|
223 |
|
224 nsresult |
|
225 NetworkActivityMonitor::Init_Internal(int32_t blipInterval) |
|
226 { |
|
227 mLayerIdentity = PR_GetUniqueIdentity("network activity monitor layer"); |
|
228 mLayerMethods = *PR_GetDefaultIOMethods(); |
|
229 mLayerMethods.connect = nsNetMon_Connect; |
|
230 mLayerMethods.read = nsNetMon_Read; |
|
231 mLayerMethods.write = nsNetMon_Write; |
|
232 mLayerMethods.writev = nsNetMon_Writev; |
|
233 mLayerMethods.recv = nsNetMon_Recv; |
|
234 mLayerMethods.send = nsNetMon_Send; |
|
235 mLayerMethods.recvfrom = nsNetMon_RecvFrom; |
|
236 mLayerMethods.sendto = nsNetMon_SendTo; |
|
237 mLayerMethods.acceptread = nsNetMon_AcceptRead; |
|
238 |
|
239 mBlipInterval = PR_MillisecondsToInterval(blipInterval); |
|
240 // Set the last notification times to time that has just expired, so any |
|
241 // activity even right now will trigger notification. |
|
242 mLastNotificationTime[kUpload] = PR_IntervalNow() - mBlipInterval; |
|
243 mLastNotificationTime[kDownload] = mLastNotificationTime[kUpload]; |
|
244 |
|
245 return NS_OK; |
|
246 } |
|
247 |
|
248 nsresult |
|
249 NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd) |
|
250 { |
|
251 if (!gInstance) |
|
252 return NS_OK; |
|
253 |
|
254 PRFileDesc * layer; |
|
255 PRStatus status; |
|
256 |
|
257 layer = PR_CreateIOLayerStub(gInstance->mLayerIdentity, |
|
258 &gInstance->mLayerMethods); |
|
259 if (!layer) { |
|
260 return NS_ERROR_FAILURE; |
|
261 } |
|
262 |
|
263 status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer); |
|
264 |
|
265 if (status == PR_FAILURE) { |
|
266 PR_DELETE(layer); |
|
267 return NS_ERROR_FAILURE; |
|
268 } |
|
269 |
|
270 return NS_OK; |
|
271 } |
|
272 |
|
273 nsresult |
|
274 NetworkActivityMonitor::DataInOut(Direction direction) |
|
275 { |
|
276 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); |
|
277 |
|
278 if (gInstance) { |
|
279 PRIntervalTime now = PR_IntervalNow(); |
|
280 if ((now - gInstance->mLastNotificationTime[direction]) > |
|
281 gInstance->mBlipInterval) { |
|
282 gInstance->mLastNotificationTime[direction] = now; |
|
283 gInstance->PostNotification(direction); |
|
284 } |
|
285 } |
|
286 |
|
287 return NS_OK; |
|
288 } |
|
289 |
|
290 void |
|
291 NetworkActivityMonitor::PostNotification(Direction direction) |
|
292 { |
|
293 nsRefPtr<nsIRunnable> ev = new NotifyNetworkActivity(direction); |
|
294 NS_DispatchToMainThread(ev); |
|
295 } |