~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/geolocation/wifi_data_provider_android.cc

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2008, Google Inc.
 
2
//
 
3
// Redistribution and use in source and binary forms, with or without
 
4
// modification, are permitted provided that the following conditions are met:
 
5
//
 
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.
 
14
//
 
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.
 
25
 
 
26
// TODO(cprince): remove platform-specific #ifdef guards when OS-specific
 
27
// sources (e.g. WIN32_CPPSRCS) are implemented.
 
28
#ifdef OS_ANDROID
 
29
 
 
30
#include "gears/geolocation/wifi_data_provider_android.h"
 
31
 
 
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"
 
36
 
 
37
static const char* kWifiProviderClass =
 
38
    GEARS_JAVA_PACKAGE "/AndroidWifiDataProvider";
 
39
 
 
40
static JavaClass::Method provider_methods[] = {
 
41
  { JavaClass::kNonStatic, "<init>", "(Landroid/webkit/WebView;J)V"},
 
42
  { JavaClass::kNonStatic, "shutdown", "()V"},
 
43
};
 
44
 
 
45
enum ProviderMethodID {
 
46
  METHOD_ID_INIT = 0,
 
47
  METHOD_ID_SHUTDOWN,
 
48
};
 
49
 
 
50
 
 
51
JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {
 
52
  {"onUpdateAvailable",
 
53
   "(Ljava/util/List;J)V",
 
54
   reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
 
55
  },
 
56
};
 
57
 
 
58
 
 
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);
 
63
 
 
64
// static
 
65
template<>
 
66
WifiDataProviderImplBase *WifiDataProvider::DefaultFactoryFunction() {
 
67
  return new AndroidWifiDataProvider();
 
68
}
 
69
 
 
70
AndroidWifiDataProvider::AndroidWifiDataProvider()
 
71
    : supported_(false),
 
72
#if USING_CCTESTS
 
73
      first_callback_made_(false),
 
74
#endif
 
75
      is_first_scan_complete_(false) {
 
76
  JavaLocalFrame frame;
 
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.
 
80
 
 
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.
 
86
    return;
 
87
  }
 
88
 
 
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.
 
94
    return;
 
95
  }
 
96
 
 
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.
 
100
  jobject webview;
 
101
  if (!WebViewManager::GetWebView(&webview)) {
 
102
    LOG(("Could not find the window object.\n"));
 
103
    assert(false);
 
104
    return;
 
105
  }
 
106
  webview_.Reset(webview);
 
107
  // Construction succeeded.
 
108
  supported_ = true;
 
109
  // We're now ready to go. Start the thread.
 
110
  Start();
 
111
}
 
112
 
 
113
AndroidWifiDataProvider::~AndroidWifiDataProvider() {
 
114
  if (supported_) {
 
115
    Stop();
 
116
  }
 
117
}
 
118
 
 
119
bool AndroidWifiDataProvider::GetData(WifiData* data) {
 
120
  if (!supported_) {
 
121
    return false;
 
122
  }
 
123
  assert(data);
 
124
  MutexLock lock(&data_mutex_);
 
125
  *data = wifi_data_;
 
126
  return is_first_scan_complete_;
 
127
}
 
128
 
 
129
void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
 
130
  assert(supported_);
 
131
  assert(new_wifi_data);
 
132
  bool is_update_available = false;
 
133
  data_mutex_.Lock();
 
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();
 
138
 
 
139
  if (is_update_available) {
 
140
    is_first_scan_complete_ = true;
 
141
    NotifyListeners();
 
142
  }
 
143
 
 
144
#if USING_CCTESTS
 
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;
 
149
    NotifyListeners();
 
150
  }
 
151
#endif
 
152
}
 
153
 
 
154
void AndroidWifiDataProvider::BeforeLoop() {
 
155
  assert(supported_);
 
156
  JavaExceptionScope scope;
 
157
  JavaLocalFrame frame;
 
158
 
 
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,
 
163
                                            webview_.Get(),
 
164
                                            native_object_ptr);
 
165
 
 
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.
 
175
  } else {
 
176
    provider_java_object_.MoveLocal(provider);
 
177
    // Register the native callback.
 
178
    provider_java_class_.RegisterNativeMethods(native_methods_,
 
179
                                               NELEM(native_methods_));
 
180
  }
 
181
}
 
182
 
 
183
void AndroidWifiDataProvider::AfterLoop() {
 
184
  assert(supported_);
 
185
  // Call the shutdown method if the Java object was created
 
186
  // successfully.
 
187
  if (provider_java_object_.Get()) {
 
188
    JniGetEnv()->CallVoidMethod(provider_java_object_.Get(),
 
189
                                provider_methods[METHOD_ID_SHUTDOWN].id);
 
190
  }
 
191
}
 
192
 
 
193
//static
 
194
void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*  /* env */,
 
195
                                                jclass  /* cls */,
 
196
                                                jobject wifi_data,
 
197
                                                jlong self) {
 
198
  assert(self);
 
199
  AndroidWifiDataProvider *self_ptr =
 
200
      reinterpret_cast<AndroidWifiDataProvider*>(self);
 
201
  WifiData new_wifi_data;
 
202
  if (wifi_data) {
 
203
    InitFromJava(wifi_data, &new_wifi_data);
 
204
  }
 
205
  // We notify regardless of whether new_wifi_data is empty
 
206
  // or not. The arbitrator will decide what to do with an empty
 
207
  // WifiData object.
 
208
  self_ptr->NewWifiDataAvailable(&new_wifi_data);
 
209
}
 
210
 
 
211
// Internal
 
212
 
 
213
// Despicable JNI goo.
 
214
static bool InitFromJava(jobject wifi_data, WifiData* new_wifi_data_out) {
 
215
  assert(new_wifi_data_out);
 
216
  assert(wifi_data);
 
217
 
 
218
  JavaClass::Method list_methods[] = {
 
219
    { JavaClass::kNonStatic, "iterator", "()Ljava/util/Iterator;"},
 
220
  };
 
221
 
 
222
  enum ListMethodID {
 
223
    METHOD_ID_ITERATOR = 0,
 
224
  };
 
225
 
 
226
  JavaClass::Method iterator_methods[] = {
 
227
    { JavaClass::kNonStatic, "hasNext", "()Z"},
 
228
    { JavaClass::kNonStatic, "next", "()Ljava/lang/Object;"},
 
229
  };
 
230
 
 
231
  enum IteratorMethodID {
 
232
    METHOD_ID_HASNEXT = 0,
 
233
    METHOD_ID_NEXT,
 
234
  };
 
235
 
 
236
  JavaLocalFrame frame;
 
237
  JavaClass list_class("List", JniGetEnv()->GetObjectClass(wifi_data));
 
238
 
 
239
  if (!list_class.GetMultipleMethodIDs(list_methods, NELEM(list_methods))) {
 
240
    LOG(("Could not find the List<ScanResults> methods.\n"));
 
241
    assert(false);
 
242
    return false;
 
243
  }
 
244
 
 
245
  jobject iterator = JniGetEnv()->CallObjectMethod(
 
246
      wifi_data,
 
247
      list_methods[METHOD_ID_ITERATOR].id);
 
248
 
 
249
  if (!iterator) {
 
250
    LOG(("Could not get the List<ScanResults> iterator.\n"));
 
251
    assert(false);
 
252
    return false;
 
253
  }
 
254
 
 
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"));
 
259
    assert(false);
 
260
    return false;
 
261
  }
 
262
  // Iterate the list of ScanResults.
 
263
  while (JniGetEnv()->CallBooleanMethod(
 
264
             iterator,
 
265
             iterator_methods[METHOD_ID_HASNEXT].id)) {
 
266
    // Get the next ScanResult.
 
267
    jobject scan_result = JniGetEnv()->CallObjectMethod(
 
268
        iterator,
 
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);
 
274
    }
 
275
    JniGetEnv()->DeleteLocalRef(scan_result);
 
276
  }
 
277
  return true;
 
278
}
 
279
 
 
280
static bool InitFromJavaScanResult(jobject scan_result,
 
281
                                   AccessPointData* data_out) {
 
282
  assert(scan_result);
 
283
  assert(data_out);
 
284
 
 
285
  JavaLocalFrame frame;
 
286
  JavaClass clazz("ScanResult", JniGetEnv()->GetObjectClass(scan_result));
 
287
 
 
288
  // Get the mac_address from the BSSID field.
 
289
  jfieldID fid = JniGetEnv()->GetFieldID(clazz.Get(),
 
290
                                         "BSSID",
 
291
                                         "Ljava/lang/String;");
 
292
  if (fid == NULL) {
 
293
    LOG(("Could not extract BSSID field ID.\n"));
 
294
    assert(false);
 
295
    return false;
 
296
  }
 
297
 
 
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()));
 
303
  }
 
304
 
 
305
  // Get the SSID from the SSID field.
 
306
  fid = JniGetEnv()->GetFieldID(clazz.Get(), "SSID", "Ljava/lang/String;");
 
307
  if (fid == NULL) {
 
308
    LOG(("Could not extract SSID field ID.\n"));
 
309
    assert(false);
 
310
    return false;
 
311
  }
 
312
 
 
313
  JavaString ssid(
 
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()));
 
318
  }
 
319
 
 
320
  // Get channel from the frequency field.
 
321
  fid = JniGetEnv()->GetFieldID(clazz.Get(), "frequency", "I");
 
322
  if (fid == NULL) {
 
323
    LOG(("Could not extract frequency field ID.\n"));
 
324
    assert(false);
 
325
    return false;
 
326
  }
 
327
 
 
328
  data_out->channel = JniGetEnv()->GetIntField(scan_result, fid);
 
329
 
 
330
  // Get the radio_signal_strength from the level field.
 
331
  fid = JniGetEnv()->GetFieldID(clazz.Get(), "level", "I");
 
332
  if (fid == NULL) {
 
333
    LOG(("Could not extract level field ID.\n"));
 
334
    assert(false);
 
335
    return false;
 
336
  }
 
337
 
 
338
  data_out->radio_signal_strength = JniGetEnv()->GetIntField(scan_result, fid);
 
339
 
 
340
  return true;
 
341
}
 
342
 
 
343
#endif  // OS_ANDROID