Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: net/android/network_change_notifier_android.cc

Issue 10979048: Fix potential threading issues in NetworkChangeNotifierAndroid. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/android/network_change_notifier_android.h" 5 #include "net/android/network_change_notifier_android.h"
6 6
7 #include "base/android/jni_android.h" 7 #include "base/android/jni_android.h"
8 #include "base/basictypes.h"
8 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/synchronization/lock.h"
11 #include "base/task_runner.h"
9 #include "jni/NetworkChangeNotifier_jni.h" 12 #include "jni/NetworkChangeNotifier_jni.h"
10 13
11 namespace net { 14 namespace net {
12 15
13 namespace { 16 namespace {
14 17
15 // Returns whether the provided connection type is known. 18 // Returns whether the provided connection type is known.
16 bool CheckConnectionType(int connection_type) { 19 bool CheckConnectionType(int connection_type) {
17 switch (connection_type) { 20 switch (connection_type) {
18 case NetworkChangeNotifier::CONNECTION_UNKNOWN: 21 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
19 case NetworkChangeNotifier::CONNECTION_ETHERNET: 22 case NetworkChangeNotifier::CONNECTION_ETHERNET:
20 case NetworkChangeNotifier::CONNECTION_WIFI: 23 case NetworkChangeNotifier::CONNECTION_WIFI:
21 case NetworkChangeNotifier::CONNECTION_2G: 24 case NetworkChangeNotifier::CONNECTION_2G:
22 case NetworkChangeNotifier::CONNECTION_3G: 25 case NetworkChangeNotifier::CONNECTION_3G:
23 case NetworkChangeNotifier::CONNECTION_4G: 26 case NetworkChangeNotifier::CONNECTION_4G:
24 case NetworkChangeNotifier::CONNECTION_NONE: 27 case NetworkChangeNotifier::CONNECTION_NONE:
25 return true; 28 return true;
26 default: 29 default:
27 NOTREACHED() << "Unknown connection type received: " << connection_type; 30 NOTREACHED() << "Unknown connection type received: " << connection_type;
28 return false; 31 return false;
29 } 32 }
30 } 33 }
31 34
32 } // namespace 35 } // namespace
33 36
37 class NetworkChangeNotifierAndroid::DelegateImpl
38 : public NetworkChangeNotifierAndroid::Delegate {
39 public:
40 DelegateImpl(base::TaskRunner* ui_task_runner)
41 : ui_task_runner_(ui_task_runner) {
42 DCHECK(ui_task_runner);
43 SetDeletionFlag(false);
44 SetConnectionType(NetworkChangeNotifier::CONNECTION_UNKNOWN);
45 }
46
47 void Init() {
48 if (IsRunningOnUIThread()) {
49 CreateJavaInstanceOnUIThread();
50 } else {
51 ui_task_runner_->PostTask(
52 FROM_HERE,
53 base::Bind(&DelegateImpl::CreateJavaInstanceOnUIThread,
54 base::Unretained(this)));
55 }
56 }
57
58 void SetConnectionType(ConnectionType connection_type) {
59 base::AutoLock auto_lock(lock_);
60 connection_type_ = connection_type;
61 }
62
63 void Destroy() {
64 if (IsRunningOnUIThread()) {
65 DestroyOnUIThread();
66 } else {
67 // Note that the deletion flag is only set on a non-UI thread.
68 SetDeletionFlag(true);
69 ui_task_runner_->PostTask(
70 FROM_HERE,
71 base::Bind(&DelegateImpl::DestroyOnUIThread, base::Unretained(this)));
72 }
73 }
74
75 virtual ConnectionType GetConnectionType() const {
76 base::AutoLock auto_lock(lock_);
77 return connection_type_;
78 }
79
80 // Delegate:
81 virtual void NotifyObserversOfConnectionTypeChange(
82 JNIEnv* env, jobject obj, jint new_connection_type) OVERRIDE {
83 DCHECK(IsRunningOnUIThread());
84 ConnectionType connection_type = CheckConnectionType(new_connection_type) ?
85 static_cast<ConnectionType>(new_connection_type) : CONNECTION_UNKNOWN;
86 SetConnectionType(connection_type);
87 base::AutoLock auto_lock(delete_lock_);
88 if (GetDeletionFlag())
89 // NetworkChangeNotifier's destruction has started on a non-UI thread.
90 // Omitting the return statement below could lead to a use after free in
91 // NotifyObservers() below.
92 return;
93 NetworkChangeNotifierAndroid::NotifyObservers();
94 }
95
96 virtual jint GetConnectionType(JNIEnv*, jobject) const OVERRIDE {
97 return GetConnectionType();
98 }
99
100 private:
101 virtual ~DelegateImpl() {}
102
103 void CreateJavaInstanceOnUIThread() {
104 DCHECK(IsRunningOnUIThread());
105 JNIEnv* env = base::android::AttachCurrentThread();
106 java_network_change_notifier_.Reset(
107 Java_NetworkChangeNotifier_createInstance(
108 env,
109 base::android::GetApplicationContext(),
110 reinterpret_cast<jint>(this)));
111 }
112
113 void DestroyOnUIThread() {
114 DCHECK(IsRunningOnUIThread());
115 JNIEnv* env = base::android::AttachCurrentThread();
116 Java_NetworkChangeNotifier_destroyInstance(env);
117 delete this;
118 }
119
120 bool IsRunningOnUIThread() const {
121 return ui_task_runner_->RunsTasksOnCurrentThread();
122 }
123
124 void SetDeletionFlag(bool value) {
125 base::AutoLock auto_lock(delete_lock_);
126 delete_started_on_non_ui_thread_ = value;
127 }
128
129 bool GetDeletionFlag() {
130 base::AutoLock auto_lock(delete_lock_);
131 return delete_started_on_non_ui_thread_;
132 }
133
134 base::TaskRunner* const ui_task_runner_;
135 base::android::ScopedJavaGlobalRef<jobject> java_network_change_notifier_;
136
137 mutable base::Lock lock_; // Protects the state below.
138 // Written from the UI thread, read from any thread.
139 ConnectionType connection_type_;
140
141 base::Lock delete_lock_; // Protects the state below.
142 // Tells whether NetworkChangeNotifierAndroid's deletion has started on a
143 // thread other than the UI thread. This is used to handle the case where we
144 // receive a notification on the UI thread from the Java side after
145 // NetworkChangeNotifierAndroid was deleted on a non-UI thread (e.g. network
146 // thread).
147 // Written from a non-UI thread, read from the UI thread.
148 bool delete_started_on_non_ui_thread_;
149
150 DISALLOW_COPY_AND_ASSIGN(DelegateImpl);
151 };
152
34 NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() { 153 NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() {
35 JNIEnv* env = base::android::AttachCurrentThread(); 154 delegate_->Destroy();
36 Java_NetworkChangeNotifier_destroyInstance(env);
37 }
38
39 void NetworkChangeNotifierAndroid::NotifyObserversOfConnectionTypeChange(
40 JNIEnv* env,
41 jobject obj,
42 jint new_connection_type) {
43 int connection_type = CheckConnectionType(new_connection_type) ?
44 new_connection_type : CONNECTION_UNKNOWN;
45 SetConnectionType(connection_type);
46 NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
47 }
48
49 jint NetworkChangeNotifierAndroid::GetConnectionType(JNIEnv* env, jobject obj) {
50 return GetCurrentConnectionType();
51 } 155 }
52 156
53 // static 157 // static
54 bool NetworkChangeNotifierAndroid::Register(JNIEnv* env) { 158 bool NetworkChangeNotifierAndroid::Register(JNIEnv* env) {
55 return RegisterNativesImpl(env); 159 return RegisterNativesImpl(env);
56 } 160 }
57 161
58 NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid() { 162 NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid(
59 SetConnectionType(CONNECTION_UNKNOWN); 163 base::TaskRunner* ui_task_runner)
60 JNIEnv* env = base::android::AttachCurrentThread(); 164 : delegate_(new DelegateImpl(ui_task_runner)) {
61 java_network_change_notifier_.Reset( 165 delegate_->Init();
62 Java_NetworkChangeNotifier_createInstance(
63 env,
64 base::android::GetApplicationContext(),
65 reinterpret_cast<jint>(this)));
66 } 166 }
67 167
68 void NetworkChangeNotifierAndroid::SetConnectionType(int connection_type) { 168 // static
69 base::AutoLock auto_lock(lock_); 169 void NetworkChangeNotifierAndroid::NotifyObservers() {
70 connection_type_ = connection_type; 170 NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
71 } 171 }
72 172
73 void NetworkChangeNotifierAndroid::ForceConnectivityState(bool state) { 173 void NetworkChangeNotifierAndroid::ForceConnectivityState(bool state) {
74 JNIEnv* env = base::android::AttachCurrentThread(); 174 JNIEnv* env = base::android::AttachCurrentThread();
75 Java_NetworkChangeNotifier_forceConnectivityState(env, state); 175 Java_NetworkChangeNotifier_forceConnectivityState(env, state);
76 } 176 }
77 177
78 NetworkChangeNotifier::ConnectionType 178 NetworkChangeNotifier::ConnectionType
79 NetworkChangeNotifierAndroid::GetCurrentConnectionType() const { 179 NetworkChangeNotifierAndroid::GetCurrentConnectionType() const {
80 base::AutoLock auto_lock(lock_); 180 return delegate_->GetConnectionType();
81 return static_cast<NetworkChangeNotifier::ConnectionType>(connection_type_);
82 } 181 }
83 182
84 } // namespace net 183 } // namespace net
OLDNEW
« no previous file with comments | « net/android/network_change_notifier_android.h ('k') | net/android/network_change_notifier_android_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698