diff options
-rw-r--r-- | base/android/java/org/chromium/base/ThreadUtils.java | 134 | ||||
-rw-r--r-- | content/browser/DEPS | 1 | ||||
-rw-r--r-- | content/browser/geolocation/arbitrator_dependency_factory.cc | 5 | ||||
-rw-r--r-- | content/browser/geolocation/location_api_adapter_android.cc | 160 | ||||
-rw-r--r-- | content/browser/geolocation/location_api_adapter_android.h | 83 | ||||
-rw-r--r-- | content/browser/geolocation/location_provider.cc | 4 | ||||
-rw-r--r-- | content/browser/geolocation/location_provider_android.cc | 48 | ||||
-rw-r--r-- | content/browser/geolocation/location_provider_android.h | 35 | ||||
-rw-r--r-- | content/content.gyp | 16 | ||||
-rw-r--r-- | content/content_browser.gypi | 15 | ||||
-rw-r--r-- | content/content_tests.gypi | 9 | ||||
-rw-r--r-- | content/public/android/OWNERS | 4 | ||||
-rw-r--r-- | content/public/android/java/content.xml | 1 | ||||
-rw-r--r-- | content/public/android/java/org/chromium/content/browser/LocationProvider.java | 232 |
14 files changed, 745 insertions, 2 deletions
diff --git a/base/android/java/org/chromium/base/ThreadUtils.java b/base/android/java/org/chromium/base/ThreadUtils.java new file mode 100644 index 0000000..ca31c3d --- /dev/null +++ b/base/android/java/org/chromium/base/ThreadUtils.java @@ -0,0 +1,134 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base; + +import android.os.Handler; +import android.os.Looper; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +/** + * Helper methods to deal with threading related tasks. + */ +public class ThreadUtils { + + /** + * Run the supplied Runnable on the main thread. The method will block until + * the Runnable completes. + * + * @param r The Runnable to run. + */ + public static void runOnUiThreadBlocking(final Runnable r) { + if (runningOnUiThread()) { + r.run(); + } else { + FutureTask<Void> task = new FutureTask<Void>(r, null); + postOnUiThread(task); + try { + task.get(); + } catch (Exception e) { + throw new RuntimeException("Exception occured while waiting for runnable", e); + } + } + } + + /** + * Run the supplied Callable on the main thread, wrapping any exceptions in + * a RuntimeException. The method will block until the Callable completes. + * + * @param c The Callable to run + * @return The result of the callable + */ + public static <T> T runOnUiThreadBlockingNoException(Callable<T> c) { + try { + return runOnUiThreadBlocking(c); + } catch (ExecutionException e) { + throw new RuntimeException("Error occured waiting for callable", e); + } + } + + /** + * Run the supplied Callable on the main thread, The method will block until + * the Callable completes. + * + * @param c The Callable to run + * @return The result of the callable + * @throws ExecutionException c's exception + */ + public static <T> T runOnUiThreadBlocking(Callable<T> c) throws ExecutionException { + FutureTask<T> task = new FutureTask<T>(c); + runOnUiThread(task); + try { + return task.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for callable", e); + } + } + + /** + * Run the supplied FutureTask on the main thread. The method will block + * only if the current thread is the main thread. + * + * @param task The FutureTask to run + * @return The queried task (to aid inline construction) + */ + public static <T> FutureTask<T> runOnUiThread(FutureTask<T> task) { + if (runningOnUiThread()) { + task.run(); + } else { + postOnUiThread(task); + } + return task; + } + + /** + * Run the supplied Callable on the main thread. The method will block + * only if the current thread is the main thread. + * + * @param c The Callable to run + * @return A FutureTask wrapping the callable to retrieve results + */ + public static <T> FutureTask<T> runOnUiThread(Callable<T> c) { + return runOnUiThread(new FutureTask<T>(c)); + } + + /** + * Run the supplied Runnable on the main thread. The method will block + * only if the current thread is the main thread. + * + * @param r The Runnable to run + */ + public static void runOnUiThread(Runnable r) { + runOnUiThread(new FutureTask<Void>(r, null)); + } + + /** + * Post the supplied FutureTask to run on the main thread. The method will + * not block, even if called on the UI thread. + * + * @param task The FutureTask to run + * @return The queried task (to aid inline construction) + */ + public static <T> FutureTask<T> postOnUiThread(FutureTask<T> task) { + new Handler(Looper.getMainLooper()).post(task); + return task; + } + + /** + * Asserts that the current thread is running on the main thread. + */ + public static void assertOnUiThread() { + assert runningOnUiThread(); + } + + /** + * @return true iff the current thread is the main (UI) thread. + */ + public static boolean runningOnUiThread() { + return Looper.getMainLooper() == Looper.myLooper(); + } +} diff --git a/content/browser/DEPS b/content/browser/DEPS index d937ab9..9d94e0d 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS @@ -2,6 +2,7 @@ include_rules = [ "+content/gpu", # For gpu_info_collector.h and in-process GPU "+content/port/browser", "+content/public/browser", + "+jni", # For generated JNI includes "+media/audio", # For audio input for speech input feature. "+media/base/android", # For Android JNI registration. diff --git a/content/browser/geolocation/arbitrator_dependency_factory.cc b/content/browser/geolocation/arbitrator_dependency_factory.cc index 60225b6..170f6d3 100644 --- a/content/browser/geolocation/arbitrator_dependency_factory.cc +++ b/content/browser/geolocation/arbitrator_dependency_factory.cc @@ -33,8 +33,13 @@ DefaultGeolocationArbitratorDependencyFactory::NewNetworkLocationProvider( net::URLRequestContextGetter* context, const GURL& url, const string16& access_token) { +#if defined(OS_ANDROID) + // Android uses its own SystemLocationProvider. + return NULL; +#else return ::NewNetworkLocationProvider(access_token_store, context, url, access_token); +#endif } LocationProviderBase* diff --git a/content/browser/geolocation/location_api_adapter_android.cc b/content/browser/geolocation/location_api_adapter_android.cc new file mode 100644 index 0000000..f1c8948 --- /dev/null +++ b/content/browser/geolocation/location_api_adapter_android.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/geolocation/location_api_adapter_android.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/bind.h" +#include "base/location.h" +#include "content/browser/geolocation/location_provider_android.h" +#include "jni/location_provider_jni.h" + +using base::android::AttachCurrentThread; +using base::android::CheckException; +using base::android::ClearException; +using base::android::GetMethodID; + +static void NewLocationAvailable(JNIEnv* env, jclass, + jdouble latitude, + jdouble longitude, + jlong time_stamp, + jboolean has_altitude, jdouble altitude, + jboolean has_accuracy, jdouble accuracy, + jboolean has_heading, jdouble heading, + jboolean has_speed, jdouble speed) { + AndroidLocationApiAdapter::OnNewLocationAvailable(latitude, longitude, + time_stamp, has_altitude, altitude, has_accuracy, accuracy, + has_heading, heading, has_speed, speed); +} + +static void NewErrorAvailable(JNIEnv* env, jclass, jstring message) { + AndroidLocationApiAdapter::OnNewErrorAvailable(env, message); +} + +AndroidLocationApiAdapter::AndroidLocationApiAdapter() + : location_provider_(NULL) { +} + +AndroidLocationApiAdapter::~AndroidLocationApiAdapter() { + CHECK(!location_provider_); + CHECK(!message_loop_); + CHECK(java_location_provider_android_object_.is_null()); +} + +bool AndroidLocationApiAdapter::Start( + LocationProviderAndroid* location_provider, bool high_accuracy) { + JNIEnv* env = AttachCurrentThread(); + if (!location_provider_) { + location_provider_ = location_provider; + CHECK(java_location_provider_android_object_.is_null()); + CreateJavaObject(env); + { + base::AutoLock lock(lock_); + CHECK(!message_loop_.get()); + message_loop_ = base::MessageLoopProxy::current(); + } + } + // At this point we should have all our pre-conditions ready, and they'd only + // change in Stop() which must be called on the same thread as here. + CHECK(location_provider_); + CHECK(message_loop_.get()); + CHECK(!java_location_provider_android_object_.is_null()); + // We'll start receiving notifications from java in the main thread looper + // until Stop() is called. + return Java_LocationProvider_start(env, + java_location_provider_android_object_.obj(), high_accuracy); +} + +void AndroidLocationApiAdapter::Stop() { + if (!location_provider_) { + CHECK(!message_loop_.get()); + CHECK(java_location_provider_android_object_.is_null()); + return; + } + + { + base::AutoLock lock(lock_); + message_loop_ = NULL; + } + + location_provider_ = NULL; + + JNIEnv* env = AttachCurrentThread(); + Java_LocationProvider_stop(env, java_location_provider_android_object_.obj()); + java_location_provider_android_object_.Reset(); +} + +// static +void AndroidLocationApiAdapter::NotifyProviderNewGeoposition( + const content::Geoposition& geoposition) { + // Called on the geolocation thread, safe to access location_provider_ here. + if (GetInstance()->location_provider_) { + CHECK(GetInstance()->message_loop_->BelongsToCurrentThread()); + GetInstance()->location_provider_->NotifyNewGeoposition(geoposition); + } +} + +// static +void AndroidLocationApiAdapter::OnNewLocationAvailable( + double latitude, double longitude, long time_stamp, + bool has_altitude, double altitude, + bool has_accuracy, double accuracy, + bool has_heading, double heading, + bool has_speed, double speed) { + content::Geoposition position; + position.latitude = latitude; + position.longitude = longitude; + position.timestamp = base::Time::FromDoubleT(time_stamp / 1000.0); + if (has_altitude) + position.altitude = altitude; + if (has_accuracy) + position.accuracy = accuracy; + if (has_heading) + position.heading = heading; + if (has_speed) + position.speed = speed; + GetInstance()->OnNewGeopositionInternal(position); +} + +// static +void AndroidLocationApiAdapter::OnNewErrorAvailable(JNIEnv* env, + jstring message) { + content::Geoposition position_error; + position_error.error_code = + content::Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + position_error.error_message = + base::android::ConvertJavaStringToUTF8(env, message); + GetInstance()->OnNewGeopositionInternal(position_error); +} + +// static +AndroidLocationApiAdapter* AndroidLocationApiAdapter::GetInstance() { + return Singleton<AndroidLocationApiAdapter>::get(); +} + +// static +bool AndroidLocationApiAdapter::RegisterGeolocationService(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void AndroidLocationApiAdapter::CreateJavaObject(JNIEnv* env) { + // Create the Java AndroidLocationProvider object. + java_location_provider_android_object_.Reset( + Java_LocationProvider_create(env, + base::android::GetApplicationContext())); + CHECK(!java_location_provider_android_object_.is_null()); +} + +void AndroidLocationApiAdapter::OnNewGeopositionInternal( + const content::Geoposition& geoposition) { + base::AutoLock lock(lock_); + if (!message_loop_) + return; + message_loop_->PostTask( + FROM_HERE, + base::Bind( + &AndroidLocationApiAdapter::NotifyProviderNewGeoposition, + geoposition)); +} diff --git a/content/browser/geolocation/location_api_adapter_android.h b/content/browser/geolocation/location_api_adapter_android.h new file mode 100644 index 0000000..c2b4f85 --- /dev/null +++ b/content/browser/geolocation/location_api_adapter_android.h @@ -0,0 +1,83 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_GEOLOCATION_ANDROID_LOCATION_API_ADAPTER_H_ +#define CONTENT_BROWSER_GEOLOCATION_ANDROID_LOCATION_API_ADAPTER_H_ +#pragma once + +#include "base/android/scoped_java_ref.h" +#include "base/synchronization/lock.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/message_loop_proxy.h" +#include "base/android/jni_helper.h" + +namespace content { +struct Geoposition; +} + +class LocationProviderAndroid; + +// Interacts with JNI and reports back to AndroidLocationProvider. +// This class creates a LocationProvider java object and listens for +// updates. +// The simplified flow is: +// GeolocationProvider runs in a Geolocation Thread and fetches geolocation data +// from a LocationProvider. +// AndroidLocationProvider access a singleton AndroidLocationApiAdapter +// AndroidLocationApiAdapter calls via JNI and uses the main thread Looper +// in the java side to listen for location updates. We then bounce these updates +// to the Geolocation thread. +// Note that AndroidLocationApiAdapter is a singleton and there's at most only +// one AndroidLocationProvider that has called Start(). +class AndroidLocationApiAdapter { + public: + // Starts the underlying location provider, returns true if successful. + // Called on the Geolocation thread. + bool Start(LocationProviderAndroid* location_provider, bool high_accuracy); + // Stops the underlying location provider. + // Called on the Geolocation thread. + void Stop(); + + // Returns our singleton. + static AndroidLocationApiAdapter* GetInstance(); + + // Called when initializing chrome_view to obtain a pointer to the java class. + static bool RegisterGeolocationService(JNIEnv* env); + + // Called by JNI on main thread looper. + static void OnNewLocationAvailable(double latitude, + double longitude, + long time_stamp, + bool has_altitude, double altitude, + bool has_accuracy, double accuracy, + bool has_heading, double heading, + bool has_speed, double speed); + static void OnNewErrorAvailable(JNIEnv* env, jstring message); + + private: + friend struct DefaultSingletonTraits<AndroidLocationApiAdapter>; + AndroidLocationApiAdapter(); + ~AndroidLocationApiAdapter(); + + void CreateJavaObject(JNIEnv* env); + + // Called on the JNI main thread looper. + void OnNewGeopositionInternal(const content::Geoposition& geoposition); + + /// Called on the Geolocation thread. + static void NotifyProviderNewGeoposition( + const content::Geoposition& geoposition); + + base::android::ScopedJavaGlobalRef<jobject> + java_location_provider_android_object_; + LocationProviderAndroid* location_provider_; + + // Guards against the following member which is accessed on Geolocation + // thread and the JNI main thread looper. + base::Lock lock_; + scoped_refptr<base::MessageLoopProxy> message_loop_; +}; + +#endif // CONTENT_BROWSER_GEOLOCATION_ANDROID_LOCATION_API_ADAPTER_H_ diff --git a/content/browser/geolocation/location_provider.cc b/content/browser/geolocation/location_provider.cc index c58bf6b..d587b55 100644 --- a/content/browser/geolocation/location_provider.cc +++ b/content/browser/geolocation/location_provider.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -56,7 +56,7 @@ void LocationProviderBase::UpdateListeners() { } } -#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_WIN) +#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_WIN) && !defined OS_ANDROID LocationProviderBase* NewSystemLocationProvider() { return NULL; } diff --git a/content/browser/geolocation/location_provider_android.cc b/content/browser/geolocation/location_provider_android.cc new file mode 100644 index 0000000..8628679 --- /dev/null +++ b/content/browser/geolocation/location_provider_android.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/geolocation/location_provider_android.h" + +#include "base/time.h" +#include "content/browser/geolocation/location_api_adapter_android.h" +#include "content/public/common/geoposition.h" + +// LocationProviderAndroid +LocationProviderAndroid::LocationProviderAndroid() { +} + +LocationProviderAndroid::~LocationProviderAndroid() { + StopProvider(); +} + +void LocationProviderAndroid::NotifyNewGeoposition( + const content::Geoposition& position) { + last_position_ = position; + UpdateListeners(); +} + +bool LocationProviderAndroid::StartProvider(bool high_accuracy) { + return AndroidLocationApiAdapter::GetInstance()->Start(this, high_accuracy); +} + +void LocationProviderAndroid::StopProvider() { + AndroidLocationApiAdapter::GetInstance()->Stop(); +} + +void LocationProviderAndroid::GetPosition(content::Geoposition* position) { + *position = last_position_; +} + +void LocationProviderAndroid::UpdatePosition() { + // Nothing to do here, android framework will call us back on new position. +} + +void LocationProviderAndroid::OnPermissionGranted( + const GURL& requesting_frame) { + // Nothing to do here. +} + +LocationProviderBase* NewSystemLocationProvider() { + return new LocationProviderAndroid; +} diff --git a/content/browser/geolocation/location_provider_android.h b/content/browser/geolocation/location_provider_android.h new file mode 100644 index 0000000..748bca1 --- /dev/null +++ b/content/browser/geolocation/location_provider_android.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_GEOLOCATION_LOCATION_PROVIDER_ANDROID_H_ +#define CONTENT_BROWSER_GEOLOCATION_LOCATION_PROVIDER_ANDROID_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/geolocation/location_provider.h" +#include "content/public/common/geoposition.h" + +class AndroidLocationApiAdapter; +struct Geoposition; + +// Location provider for Android using the platform provider over JNI. +class LocationProviderAndroid : public LocationProviderBase { + public: + LocationProviderAndroid(); + virtual ~LocationProviderAndroid(); + + // Called by the AndroidLocationApiAdapter. + void NotifyNewGeoposition(const content::Geoposition& position); + + // LocationProviderBase. + virtual bool StartProvider(bool high_accuracy) OVERRIDE; + virtual void StopProvider() OVERRIDE; + virtual void GetPosition(content::Geoposition* position) OVERRIDE; + virtual void UpdatePosition() OVERRIDE; + virtual void OnPermissionGranted(const GURL& requesting_frame) OVERRIDE; + + private: + content::Geoposition last_position_; +}; + +#endif // CONTENT_BROWSER_GEOLOCATION_LOCATION_PROVIDER_ANDROID_H_ diff --git a/content/content.gyp b/content/content.gyp index 6632f6d..b05a6e7 100644 --- a/content/content.gyp +++ b/content/content.gyp @@ -200,12 +200,28 @@ ['OS == "android"', { 'targets': [ { + 'target_name': 'content_jni_headers', + 'type': 'none', + 'variables': { + 'java_sources': [ + '../content/public/android/java/org/chromium/content/browser/LocationProvider.java', + ], + 'jni_headers': [ + '<(SHARED_INTERMEDIATE_DIR)/content/jni/location_provider_jni.h', + ], + }, + 'includes': [ '../build/jni_generator.gypi' ], + }, + { 'target_name': 'content_java', 'type': 'none', 'variables': { 'package_name': 'content', 'java_in_dir': '../content/public/android/java', }, + 'dependencies': [ + '../base/base.gyp:base_java', + ], 'includes': [ '../build/java.gypi' ], }, ], diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 558fa5e..bc44c69 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -337,10 +337,14 @@ 'browser/geolocation/gps_location_provider_linux.h', 'browser/geolocation/libgps_wrapper_linux.cc', 'browser/geolocation/libgps_wrapper_linux.h', + 'browser/geolocation/location_api_adapter_android.cc', + 'browser/geolocation/location_api_adapter_android.h', 'browser/geolocation/location_arbitrator.cc', 'browser/geolocation/location_arbitrator.h', 'browser/geolocation/location_provider.cc', 'browser/geolocation/location_provider.h', + 'browser/geolocation/location_provider_android.cc', + 'browser/geolocation/location_provider_android.h', 'browser/geolocation/network_location_provider.cc', 'browser/geolocation/network_location_provider.h', 'browser/geolocation/network_location_request.cc', @@ -814,6 +818,17 @@ ], 'dependencies': [ '../media/media.gyp:media', + 'content.gyp:content_jni_headers', + 'content.gyp:content_java', + ], + 'sources!': [ + 'browser/geolocation/network_location_provider.cc', + 'browser/geolocation/network_location_provider.h', + 'browser/geolocation/network_location_request.cc', + 'browser/geolocation/network_location_request.h', + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)/content', ], }], ['OS=="mac"', { diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 5b828fd..aea6d7d 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -380,6 +380,15 @@ 'browser/accessibility/browser_accessibility_win_unittest.cc', ], }], + ['OS == "android"', { + 'sources!': [ + 'browser/geolocation/device_data_provider_unittest.cc', + 'browser/geolocation/gps_location_provider_unittest_linux.cc', + 'browser/geolocation/network_location_provider_unittest.cc', + 'browser/geolocation/wifi_data_provider_common_unittest.cc', + 'browser/geolocation/wifi_data_provider_linux_unittest.cc', + ], + }], ], }, { diff --git a/content/public/android/OWNERS b/content/public/android/OWNERS new file mode 100644 index 0000000..a35ceee --- /dev/null +++ b/content/public/android/OWNERS @@ -0,0 +1,4 @@ +satish@chromium.org +bulach@chromium.org +jrg@chromium.org +yfriedman@chromium.org diff --git a/content/public/android/java/content.xml b/content/public/android/java/content.xml index a00a4e5..435cbab 100644 --- a/content/public/android/java/content.xml +++ b/content/public/android/java/content.xml @@ -40,6 +40,7 @@ <javac srcdir="${src}" destdir="${classes.dir}"> <classpath> <path location="${location.base}/android.jar"/> + <path location="${PRODUCT_DIR}/chromium_base.jar"/> </classpath> </javac> </target> diff --git a/content/public/android/java/org/chromium/content/browser/LocationProvider.java b/content/public/android/java/org/chromium/content/browser/LocationProvider.java new file mode 100644 index 0000000..6a89eef --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/LocationProvider.java @@ -0,0 +1,232 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.content.browser; + +import android.content.Context; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.os.Looper; +import android.util.Log; + +import org.chromium.base.ActivityStatus; +import org.chromium.base.CalledByNative; +import org.chromium.base.ThreadUtils; + +import java.util.concurrent.FutureTask; + +/** + * Implements the Java side of LocationProviderAndroid. + * Delegates all real functionality to the inner class. + * See detailed documentation on + * content/browser/geolocation/android_location_api_adapter.h. + * Based on android.webkit.GeolocationService.java + */ +class LocationProvider { + + // Log tag + private static final String TAG = "LocationProvider"; + + /** + * This is the core of android location provider. It is a separate class for clarity + * so that it can manage all processing completely in the UI thread. The container class + * ensures that the start/stop calls into this class are done in the UI thread. + */ + private static class LocationProviderImpl + implements LocationListener, ActivityStatus.Listener { + + private Context mContext; + private LocationManager mLocationManager; + private boolean mIsRunning; + private boolean mShouldRunAfterActivityResume; + private boolean mIsGpsEnabled; + + LocationProviderImpl(Context context) { + mContext = context; + } + + public void onActivityStatusChanged(boolean isPaused) { + if (isPaused) { + mShouldRunAfterActivityResume = mIsRunning; + unregisterFromLocationUpdates(); + } else { + assert !mIsRunning; + if (mShouldRunAfterActivityResume) { + registerForLocationUpdates(); + } + } + } + + /** + * Start listening for location updates. + * @param gpsEnabled Whether or not we're interested in high accuracy GPS. + */ + private void start(boolean gpsEnabled) { + if (!mIsRunning && !mShouldRunAfterActivityResume) { + // Currently idle so start listening to activity status changes. + ActivityStatus.getInstance().registerListener(this); + } + mIsGpsEnabled = gpsEnabled; + if (ActivityStatus.getInstance().isPaused()) { + mShouldRunAfterActivityResume = true; + } else { + unregisterFromLocationUpdates(); + registerForLocationUpdates(); + } + } + + /** + * Stop listening for location updates. + */ + private void stop() { + unregisterFromLocationUpdates(); + ActivityStatus.getInstance().unregisterListener(this); + mShouldRunAfterActivityResume = false; + } + + /** + * Returns true if we are currently listening for location updates, false if not. + */ + private boolean isRunning() { + return mIsRunning; + } + + @Override + public void onLocationChanged(Location location) { + // Callbacks from the system location sevice are queued to this thread, so it's + // possible that we receive callbacks after unregistering. At this point, the + // native object will no longer exist. + if (mIsRunning) { + nativeNewLocationAvailable(location.getLatitude(), location.getLongitude(), + location.getTime(), location.hasAltitude(), location.getAltitude(), + location.hasAccuracy(), location.getAccuracy(), + location.hasBearing(), location.getBearing(), + location.hasSpeed(), location.getSpeed()); + } + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + } + + @Override + public void onProviderEnabled(String provider) { + } + + @Override + public void onProviderDisabled(String provider) { + } + + private void ensureLocationManagerCreated() { + if (mLocationManager != null) return; + mLocationManager = (LocationManager) mContext.getSystemService( + Context.LOCATION_SERVICE); + if (mLocationManager == null) { + Log.e(TAG, "Could not get location manager."); + } + } + + /** + * Registers this object with the location service. + */ + private void registerForLocationUpdates() { + ensureLocationManagerCreated(); + + assert !mIsRunning; + mIsRunning = true; + + // We're running on the main thread. The C++ side is responsible to + // bounce notifications to the Geolocation thread as they arrive in the mainLooper. + try { + Criteria criteria = new Criteria(); + mLocationManager.requestLocationUpdates(0, 0, criteria, this, + Looper.getMainLooper()); + if (mIsGpsEnabled) { + criteria.setAccuracy(Criteria.ACCURACY_FINE); + mLocationManager.requestLocationUpdates(0, 0, criteria, this, + Looper.getMainLooper()); + } + } catch(SecurityException e) { + Log.e(TAG, "Caught security exception registering for location updates from " + + "system. This should only happen in DumpRenderTree."); + } catch(IllegalArgumentException e) { + Log.e(TAG, "Caught IllegalArgumentException registering for location updates."); + } + } + + /** + * Unregisters this object from the location service. + */ + private void unregisterFromLocationUpdates() { + if (mIsRunning) { + mIsRunning = false; + mLocationManager.removeUpdates(this); + } + } + } + + // Delegate handling the real work in the main thread. + private LocationProviderImpl mImpl; + + private LocationProvider(Context context) { + mImpl = new LocationProviderImpl(context); + } + + @CalledByNative + static LocationProvider create(Context context) { + return new LocationProvider(context); + } + + /** + * Start listening for location updates until we're told to quit. May be + * called in any thread. + * @param gpsEnabled Whether or not we're interested in high accuracy GPS. + */ + @CalledByNative + public boolean start(final boolean gpsEnabled) { + FutureTask<Void> task = new FutureTask<Void>(new Runnable() { + @Override + public void run() { + mImpl.start(gpsEnabled); + } + }, null); + ThreadUtils.runOnUiThread(task); + return true; + } + + /** + * Stop listening for location updates. May be called in any thread. + */ + @CalledByNative + public void stop() { + FutureTask<Void> task = new FutureTask<Void>(new Runnable() { + @Override + public void run() { + mImpl.stop(); + } + }, null); + ThreadUtils.runOnUiThread(task); + } + + /** + * Returns true if we are currently listening for location updates, false if not. + * Must be called only in the UI thread. + */ + public boolean isRunning() { + assert Looper.myLooper() == Looper.getMainLooper(); + return mImpl.isRunning(); + } + + // Native functions + public static native void nativeNewLocationAvailable( + double latitude, double longitude, long timeStamp, + boolean hasAltitude, double altitude, + boolean hasAccuracy, double accuracy, + boolean hasHeading, double heading, + boolean hasSpeed, double speed); + public static native void nativeNewErrorAvailable(String message); +} |