1
// Copyright 2008, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
// TODO(cprince): remove platform-specific #ifdef guards when OS-specific
27
// sources (e.g. WIN32_CPPSRCS) are implemented.
30
#include "gears/geolocation/wifi_data_provider_android.h"
32
#include "gears/base/android/java_exception_scope.h"
33
#include "gears/base/android/java_local_frame.h"
34
#include "gears/base/android/webview_manager.h"
35
#include "third_party/scoped_ptr/scoped_ptr.h"
37
static const char* kWifiProviderClass =
38
GEARS_JAVA_PACKAGE "/AndroidWifiDataProvider";
40
static JavaClass::Method provider_methods[] = {
41
{ JavaClass::kNonStatic, "<init>", "(Landroid/webkit/WebView;J)V"},
42
{ JavaClass::kNonStatic, "shutdown", "()V"},
45
enum ProviderMethodID {
51
JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {
53
"(Ljava/util/List;J)V",
54
reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
59
// Utility functions that convert from Java to C++ objects.
60
static bool InitFromJava(jobject wifi_data, WifiData* new_wifi_data_out);
61
static bool InitFromJavaScanResult(jobject scan_result,
62
AccessPointData* data_out);
66
WifiDataProviderImplBase *WifiDataProvider::DefaultFactoryFunction() {
67
return new AndroidWifiDataProvider();
70
AndroidWifiDataProvider::AndroidWifiDataProvider()
73
first_callback_made_(false),
75
is_first_scan_complete_(false) {
77
// This provider must run on a Looper thread in order to receive events
78
// from the WifiManager. We cannot use the AndroidMessageLoop
79
// as that cannot receive events from this service.
81
// Set up the jni objects for the Java wifi data provider instance
82
// that will be created later, once the thread is started.
83
if (!provider_java_class_.FindClass(kWifiProviderClass)) {
84
LOG(("Could not find the AndroidWifiDataProvider class.\n"));
85
// Fail gracefully. API not available.
89
// Get the provider method IDs.
90
if (!provider_java_class_.GetMultipleMethodIDs(provider_methods,
91
NELEM(provider_methods))) {
92
LOG(("Could not find the AndroidWifiDataProvider methods.\n"));
93
// Fail gracefully. API not compatible.
97
// Get the WebView pointer. Note that this MUST execute on either
98
// the browser main thread or a worker thread. See WebView::RegisterWebView()
99
// method comments for more details aboud this constraint.
101
if (!WebViewManager::GetWebView(&webview)) {
102
LOG(("Could not find the window object.\n"));
106
webview_.Reset(webview);
107
// Construction succeeded.
109
// We're now ready to go. Start the thread.
113
AndroidWifiDataProvider::~AndroidWifiDataProvider() {
119
bool AndroidWifiDataProvider::GetData(WifiData* data) {
124
MutexLock lock(&data_mutex_);
126
return is_first_scan_complete_;
129
void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
131
assert(new_wifi_data);
132
bool is_update_available = false;
134
is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);
135
wifi_data_ = *new_wifi_data;
136
// Avoid holding the mutex locked while notifying observers.
137
data_mutex_.Unlock();
139
if (is_update_available) {
140
is_first_scan_complete_ = true;
145
// This is needed for running the WiFi test on the emulator.
146
// See wifi_data_provider_android.h for details.
147
if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {
148
first_callback_made_ = true;
154
void AndroidWifiDataProvider::BeforeLoop() {
156
JavaExceptionScope scope;
157
JavaLocalFrame frame;
159
// Construct the Java Wifi provider object.
160
jlong native_object_ptr = reinterpret_cast<jlong>(this);
161
jobject provider = JniGetEnv()->NewObject(provider_java_class_.Get(),
162
provider_methods[METHOD_ID_INIT].id,
166
if (scope.Occurred()) {
167
// Something went wrong when instantiating the Java object.
168
LOG(("Could not instantiate the Java object.\n"));
169
// We're not asserting as this can happen due to other reasons
170
// than Gears programmer error. We also don't exit now but let
171
// the normal operation of the Geolocation stack run its course.
172
// We'll get stopped once the location fix is terminated.
173
// No need to call 'scope.Clear();', as the exception
174
// will be cleared by the destructor of scope.
176
provider_java_object_.MoveLocal(provider);
177
// Register the native callback.
178
provider_java_class_.RegisterNativeMethods(native_methods_,
179
NELEM(native_methods_));
183
void AndroidWifiDataProvider::AfterLoop() {
185
// Call the shutdown method if the Java object was created
187
if (provider_java_object_.Get()) {
188
JniGetEnv()->CallVoidMethod(provider_java_object_.Get(),
189
provider_methods[METHOD_ID_SHUTDOWN].id);
194
void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /* env */,
199
AndroidWifiDataProvider *self_ptr =
200
reinterpret_cast<AndroidWifiDataProvider*>(self);
201
WifiData new_wifi_data;
203
InitFromJava(wifi_data, &new_wifi_data);
205
// We notify regardless of whether new_wifi_data is empty
206
// or not. The arbitrator will decide what to do with an empty
208
self_ptr->NewWifiDataAvailable(&new_wifi_data);
213
// Despicable JNI goo.
214
static bool InitFromJava(jobject wifi_data, WifiData* new_wifi_data_out) {
215
assert(new_wifi_data_out);
218
JavaClass::Method list_methods[] = {
219
{ JavaClass::kNonStatic, "iterator", "()Ljava/util/Iterator;"},
223
METHOD_ID_ITERATOR = 0,
226
JavaClass::Method iterator_methods[] = {
227
{ JavaClass::kNonStatic, "hasNext", "()Z"},
228
{ JavaClass::kNonStatic, "next", "()Ljava/lang/Object;"},
231
enum IteratorMethodID {
232
METHOD_ID_HASNEXT = 0,
236
JavaLocalFrame frame;
237
JavaClass list_class("List", JniGetEnv()->GetObjectClass(wifi_data));
239
if (!list_class.GetMultipleMethodIDs(list_methods, NELEM(list_methods))) {
240
LOG(("Could not find the List<ScanResults> methods.\n"));
245
jobject iterator = JniGetEnv()->CallObjectMethod(
247
list_methods[METHOD_ID_ITERATOR].id);
250
LOG(("Could not get the List<ScanResults> iterator.\n"));
255
JavaClass iter_class("Iterator", JniGetEnv()->GetObjectClass(iterator));
256
if (!iter_class.GetMultipleMethodIDs(iterator_methods,
257
NELEM(iterator_methods))) {
258
LOG(("Could not find the Iterator methods.\n"));
262
// Iterate the list of ScanResults.
263
while (JniGetEnv()->CallBooleanMethod(
265
iterator_methods[METHOD_ID_HASNEXT].id)) {
266
// Get the next ScanResult.
267
jobject scan_result = JniGetEnv()->CallObjectMethod(
269
iterator_methods[METHOD_ID_NEXT].id);
270
// Convert it to an AccessPointData.
271
AccessPointData data;
272
if (InitFromJavaScanResult(scan_result, &data)) {
273
new_wifi_data_out->access_point_data.insert(data);
275
JniGetEnv()->DeleteLocalRef(scan_result);
280
static bool InitFromJavaScanResult(jobject scan_result,
281
AccessPointData* data_out) {
285
JavaLocalFrame frame;
286
JavaClass clazz("ScanResult", JniGetEnv()->GetObjectClass(scan_result));
288
// Get the mac_address from the BSSID field.
289
jfieldID fid = JniGetEnv()->GetFieldID(clazz.Get(),
291
"Ljava/lang/String;");
293
LOG(("Could not extract BSSID field ID.\n"));
298
JavaString mac_address(
299
static_cast<jstring>(JniGetEnv()->GetObjectField(scan_result, fid)));
300
if (mac_address.Get() != NULL) {
301
mac_address.ToString16(&(data_out->mac_address));
302
LOG(("Got mac_address: %s \n", mac_address.ToString8().c_str()));
305
// Get the SSID from the SSID field.
306
fid = JniGetEnv()->GetFieldID(clazz.Get(), "SSID", "Ljava/lang/String;");
308
LOG(("Could not extract SSID field ID.\n"));
314
static_cast<jstring>(JniGetEnv()->GetObjectField(scan_result, fid)));
315
if (ssid.Get() != NULL) {
316
ssid.ToString16(&(data_out->ssid));
317
LOG(("Got ssid: %s \n", ssid.ToString8().c_str()));
320
// Get channel from the frequency field.
321
fid = JniGetEnv()->GetFieldID(clazz.Get(), "frequency", "I");
323
LOG(("Could not extract frequency field ID.\n"));
328
data_out->channel = JniGetEnv()->GetIntField(scan_result, fid);
330
// Get the radio_signal_strength from the level field.
331
fid = JniGetEnv()->GetFieldID(clazz.Get(), "level", "I");
333
LOG(("Could not extract level field ID.\n"));
338
data_out->radio_signal_strength = JniGetEnv()->GetIntField(scan_result, fid);