summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/android/java/org/chromium/base/WeakContext.java45
-rwxr-xr-xbase/android/jni_generator/jni_generator_tests.py10
-rw-r--r--content/browser/device_orientation/data_fetcher.h6
-rw-r--r--content/browser/device_orientation/data_fetcher_impl_android.cc90
-rw-r--r--content/browser/device_orientation/data_fetcher_impl_android.h63
-rw-r--r--content/browser/device_orientation/provider.cc6
-rw-r--r--content/browser/device_orientation/provider_impl.cc6
-rw-r--r--content/browser/device_orientation/provider_unittest.cc13
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_jni.gypi2
-rw-r--r--content/public/android/java/org/chromium/content/browser/DeviceOrientation.java241
11 files changed, 473 insertions, 11 deletions
diff --git a/base/android/java/org/chromium/base/WeakContext.java b/base/android/java/org/chromium/base/WeakContext.java
new file mode 100644
index 0000000..d660cc9
--- /dev/null
+++ b/base/android/java/org/chromium/base/WeakContext.java
@@ -0,0 +1,45 @@
+// 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.content.Context;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Callable;
+
+// Holds a WeakReference to Context to allow it to be GC'd.
+// Also provides utility functions to getSystemService from the UI or any
+// other thread (may return null, if the Context has been nullified).
+public class WeakContext {
+ private static WeakReference<Context> sWeakContext;
+
+ public static void initializeWeakContext(final Context context) {
+ sWeakContext = new WeakReference<Context>(context);
+ }
+
+ public static Context getContext() {
+ return sWeakContext.get();
+ }
+
+ // Returns a system service. May be called from any thread.
+ // If necessary, it will send a message to the main thread to acquire the
+ // service, and block waiting for it to complete.
+ // May return null if context is no longer available.
+ public static Object getSystemService(final String name) {
+ final Context context = sWeakContext.get();
+ if (context == null) {
+ return null;
+ }
+ if (ThreadUtils.runningOnUiThread()) {
+ return context.getSystemService(name);
+ }
+ return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Object>() {
+ @Override
+ public Object call() {
+ return context.getSystemService(name);
+ }
+ });
+ }
+}
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index a6c4e0a..159c34d 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -77,7 +77,7 @@ class TestGenerator(unittest.TestCase):
String[] projection, String selection,
String[] selectionArgs, String sortOrder);
private native void nativeGotOrientation(
- int nativePtr /* device_orientation::DeviceOrientationAndroid */,
+ int nativePtr /* device_orientation::DataFetcherImplAndroid */,
double alpha, double beta, double gamma);
"""
natives = jni_generator.ExtractNatives(test_data)
@@ -189,7 +189,7 @@ class TestGenerator(unittest.TestCase):
name='GotOrientation',
params=[Param(datatype='int',
cpp_class_name=
- 'device_orientation::DeviceOrientationAndroid',
+ 'device_orientation::DataFetcherImplAndroid',
name='nativePtr'),
Param(datatype='double',
name='alpha'),
@@ -200,7 +200,7 @@ class TestGenerator(unittest.TestCase):
],
java_class_name='',
type='method',
- p0_type='device_orientation::DeviceOrientationAndroid'),
+ p0_type='device_orientation::DataFetcherImplAndroid'),
]
self.assertListEquals(golden_natives, natives)
h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
@@ -322,8 +322,8 @@ static void GotOrientation(JNIEnv* env, jobject obj,
jdouble beta,
jdouble gamma) {
DCHECK(nativePtr) << "GotOrientation";
- device_orientation::DeviceOrientationAndroid* native =
- reinterpret_cast<device_orientation::DeviceOrientationAndroid*>(nativePtr);
+ device_orientation::DataFetcherImplAndroid* native =
+ reinterpret_cast<device_orientation::DataFetcherImplAndroid*>(nativePtr);
return native->GotOrientation(env, obj, alpha, beta, gamma);
}
diff --git a/content/browser/device_orientation/data_fetcher.h b/content/browser/device_orientation/data_fetcher.h
index 6f6bd99..8e69f1f 100644
--- a/content/browser/device_orientation/data_fetcher.h
+++ b/content/browser/device_orientation/data_fetcher.h
@@ -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.
@@ -12,6 +12,10 @@ class Orientation;
class DataFetcher {
public:
virtual ~DataFetcher() {}
+
+ // Returns false if there was a fatal error getting the orientation.
+ // Returns true otherwise. If the fetcher has orientation data available
+ // it will fill it in, otherwise the argument will be unaltered.
virtual bool GetOrientation(Orientation*) = 0;
};
diff --git a/content/browser/device_orientation/data_fetcher_impl_android.cc b/content/browser/device_orientation/data_fetcher_impl_android.cc
new file mode 100644
index 0000000..6f54192
--- /dev/null
+++ b/content/browser/device_orientation/data_fetcher_impl_android.cc
@@ -0,0 +1,90 @@
+// 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/device_orientation/data_fetcher_impl_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "content/browser/device_orientation/orientation.h"
+#include "jni/device_orientation_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::CheckException;
+using base::android::GetClass;
+using base::android::GetMethodID;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace device_orientation {
+
+namespace {
+
+// This should match ProviderImpl::kDesiredSamplingIntervalMs.
+// TODO(husky): Make that constant public so we can use it directly.
+const int kPeriodInMilliseconds = 100;
+
+base::LazyInstance<ScopedJavaGlobalRef<jobject> >
+ g_jni_obj = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+DataFetcherImplAndroid::DataFetcherImplAndroid() {
+}
+
+void DataFetcherImplAndroid::Init(JNIEnv* env) {
+ bool result = RegisterNativesImpl(env);
+ DCHECK(result);
+
+ g_jni_obj.Get().Reset(Java_DeviceOrientation_create(env));
+}
+
+DataFetcher* DataFetcherImplAndroid::Create() {
+ scoped_ptr<DataFetcherImplAndroid> fetcher(new DataFetcherImplAndroid);
+ if (fetcher->Start(kPeriodInMilliseconds))
+ return fetcher.release();
+
+ LOG(ERROR) << "DataFetcherImplAndroid::Start failed!";
+ return NULL;
+}
+
+DataFetcherImplAndroid::~DataFetcherImplAndroid() {
+ Stop();
+}
+
+bool DataFetcherImplAndroid::GetOrientation(Orientation* orientation) {
+ // Do we have a new orientation value? (It's safe to do this outside the lock
+ // because we only skip the lock if the value is null. We always enter the
+ // lock if we're going to make use of the new value.)
+ if (next_orientation_.get()) {
+ base::AutoLock autolock(next_orientation_lock_);
+ next_orientation_.swap(current_orientation_);
+ }
+ if (current_orientation_.get())
+ *orientation = *current_orientation_;
+ return true;
+}
+
+void DataFetcherImplAndroid::GotOrientation(
+ JNIEnv*, jobject, double alpha, double beta, double gamma) {
+ base::AutoLock autolock(next_orientation_lock_);
+ next_orientation_.reset(
+ new Orientation(true, alpha, true, beta, true, gamma, true, true));
+}
+
+bool DataFetcherImplAndroid::Start(int rate_in_milliseconds) {
+ DCHECK(!g_jni_obj.Get().is_null());
+ return Java_DeviceOrientation_start(AttachCurrentThread(),
+ g_jni_obj.Get().obj(),
+ reinterpret_cast<jint>(this),
+ rate_in_milliseconds);
+}
+
+void DataFetcherImplAndroid::Stop() {
+ DCHECK(!g_jni_obj.Get().is_null());
+ Java_DeviceOrientation_stop(AttachCurrentThread(), g_jni_obj.Get().obj());
+}
+
+} // namespace device_orientation
diff --git a/content/browser/device_orientation/data_fetcher_impl_android.h b/content/browser/device_orientation/data_fetcher_impl_android.h
new file mode 100644
index 0000000..e14aa10
--- /dev/null
+++ b/content/browser/device_orientation/data_fetcher_impl_android.h
@@ -0,0 +1,63 @@
+// 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 CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_ANDROID_H_
+#define CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_ANDROID_H_
+#pragma once
+
+#include <jni.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "content/browser/device_orientation/data_fetcher.h"
+#include "content/browser/device_orientation/orientation.h"
+
+namespace device_orientation {
+
+// Android implementation of DeviceOrientation API.
+
+// Android's SensorManager has a push API, whereas Chrome wants to pull data.
+// To fit them together, we store incoming sensor events in a 1-element buffer.
+// SensorManager calls SetOrientation() which pushes a new value (discarding the
+// previous value if any). Chrome calls GetOrientation() which reads the most
+// recent value. Repeated calls to GetOrientation() will return the same value.
+
+class DataFetcherImplAndroid : public DataFetcher {
+ public:
+ // Must be called at startup, before Create().
+ static void Init(JNIEnv* env);
+
+ // Factory function. We'll listen for events for the lifetime of this object.
+ // Returns NULL on error.
+ static DataFetcher* Create();
+
+ virtual ~DataFetcherImplAndroid();
+
+ // Called from Java via JNI.
+ void GotOrientation(JNIEnv*, jobject,
+ double alpha, double beta, double gamma);
+
+ // Implementation of DataFetcher.
+ virtual bool GetOrientation(Orientation* orientation) OVERRIDE;
+
+ private:
+ DataFetcherImplAndroid();
+
+ // Wrappers for JNI methods.
+ bool Start(int rateInMilliseconds);
+ void Stop();
+
+ // Value returned by GetOrientation.
+ scoped_ptr<Orientation> current_orientation_;
+
+ // 1-element buffer, written by GotOrientation, read by GetOrientation.
+ base::Lock next_orientation_lock_;
+ scoped_ptr<Orientation> next_orientation_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataFetcherImplAndroid);
+};
+
+} // namespace device_orientation
+
+#endif // CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_IMPL_ANDROID_H_
diff --git a/content/browser/device_orientation/provider.cc b/content/browser/device_orientation/provider.cc
index 1287b0e..659956a 100644
--- a/content/browser/device_orientation/provider.cc
+++ b/content/browser/device_orientation/provider.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 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.
@@ -11,6 +11,8 @@
#if defined(OS_MACOSX)
#include "content/browser/device_orientation/accelerometer_mac.h"
+#elif defined(OS_ANDROID)
+#include "content/browser/device_orientation/data_fetcher_impl_android.h"
#endif
using content::BrowserThread;
@@ -23,6 +25,8 @@ Provider* Provider::GetInstance() {
const ProviderImpl::DataFetcherFactory default_factories[] = {
#if defined(OS_MACOSX)
AccelerometerMac::Create,
+#elif defined(OS_ANDROID)
+ DataFetcherImplAndroid::Create,
#endif
NULL
};
diff --git a/content/browser/device_orientation/provider_impl.cc b/content/browser/device_orientation/provider_impl.cc
index 537d25b..b27421c 100644
--- a/content/browser/device_orientation/provider_impl.cc
+++ b/content/browser/device_orientation/provider_impl.cc
@@ -83,7 +83,8 @@ void ProviderImpl::DoInitializePollingThread(
last_orientation_ = orientation;
// Notify observers.
- ScheduleDoNotify(orientation);
+ if (!orientation.IsEmpty())
+ ScheduleDoNotify(orientation);
// Start polling.
ScheduleDoPoll();
@@ -139,7 +140,8 @@ void ProviderImpl::DoPoll() {
return;
}
- if (SignificantlyDifferent(orientation, last_orientation_)) {
+ if (!orientation.IsEmpty() &&
+ SignificantlyDifferent(orientation, last_orientation_)) {
last_orientation_ = orientation;
ScheduleDoNotify(orientation);
}
diff --git a/content/browser/device_orientation/provider_unittest.cc b/content/browser/device_orientation/provider_unittest.cc
index e95e7b8..17d3d43 100644
--- a/content/browser/device_orientation/provider_unittest.cc
+++ b/content/browser/device_orientation/provider_unittest.cc
@@ -68,7 +68,8 @@ class UpdateChecker : public Provider::Observer {
// Class for injecting test orientation data into the Provider.
class MockOrientationFactory : public base::RefCounted<MockOrientationFactory> {
public:
- MockOrientationFactory() {
+ MockOrientationFactory()
+ : is_failing_(false) {
EXPECT_FALSE(instance_);
instance_ = this;
}
@@ -83,6 +84,11 @@ class MockOrientationFactory : public base::RefCounted<MockOrientationFactory> {
orientation_ = orientation;
}
+ void SetFailing(bool is_failing) {
+ base::AutoLock auto_lock(lock_);
+ is_failing_ = is_failing;
+ }
+
private:
friend class base::RefCounted<MockOrientationFactory>;
@@ -99,6 +105,8 @@ class MockOrientationFactory : public base::RefCounted<MockOrientationFactory> {
// From DataFetcher. Called by the Provider.
virtual bool GetOrientation(Orientation* orientation) {
base::AutoLock auto_lock(orientation_factory_->lock_);
+ if (orientation_factory_->is_failing_)
+ return false;
*orientation = orientation_factory_->orientation_;
return true;
}
@@ -109,6 +117,7 @@ class MockOrientationFactory : public base::RefCounted<MockOrientationFactory> {
static MockOrientationFactory* instance_;
Orientation orientation_;
+ bool is_failing_;
base::Lock lock_;
};
@@ -303,7 +312,7 @@ TEST_F(DeviceOrientationProviderTest, MAYBE_StartFailing) {
MessageLoop::current()->Run();
checker_a->AddExpectation(Orientation::Empty());
- orientation_factory->SetOrientation(Orientation::Empty());
+ orientation_factory->SetFailing(true);
MessageLoop::current()->Run();
checker_b->AddExpectation(Orientation::Empty());
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index a6df267..77488ba 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -243,6 +243,8 @@
'browser/device_orientation/accelerometer_mac.cc',
'browser/device_orientation/accelerometer_mac.h',
'browser/device_orientation/data_fetcher.h',
+ 'browser/device_orientation/data_fetcher_impl_android.cc',
+ 'browser/device_orientation/data_fetcher_impl_android.h',
'browser/device_orientation/message_filter.cc',
'browser/device_orientation/message_filter.h',
'browser/device_orientation/orientation.h',
diff --git a/content/content_jni.gypi b/content/content_jni.gypi
index dc6849b..6165422 100644
--- a/content/content_jni.gypi
+++ b/content/content_jni.gypi
@@ -10,6 +10,7 @@
'variables': {
'java_sources': [
'public/android/java/org/chromium/content/browser/CommandLine.java',
+ 'public/android/java/org/chromium/content/browser/DeviceOrientation.java',
'public/android/java/org/chromium/content/browser/JNIHelper.java',
'public/android/java/org/chromium/content/browser/LibraryLoader.java',
'public/android/java/org/chromium/content/browser/LocationProvider.java',
@@ -17,6 +18,7 @@
],
'jni_headers': [
'<(SHARED_INTERMEDIATE_DIR)/content/jni/command_line_jni.h',
+ '<(SHARED_INTERMEDIATE_DIR)/content/jni/device_orientation_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/jni_helper_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/library_loader_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/content/jni/location_provider_jni.h',
diff --git a/content/public/android/java/org/chromium/content/browser/DeviceOrientation.java b/content/public/android/java/org/chromium/content/browser/DeviceOrientation.java
new file mode 100644
index 0000000..9859e84
--- /dev/null
+++ b/content/public/android/java/org/chromium/content/browser/DeviceOrientation.java
@@ -0,0 +1,241 @@
+// 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.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.WeakContext;
+
+import java.util.List;
+
+/**
+ * Android implementation of the DeviceOrientation API.
+ */
+class DeviceOrientation implements SensorEventListener {
+
+ // These fields are lazily initialized by getHandler().
+ private Thread mThread;
+ private Handler mHandler;
+
+ // Non-zero if and only if we're listening for events.
+ // To avoid race conditions on the C++ side, access must be synchronized.
+ private int mNativePtr;
+
+ // The gravity vector expressed in the body frame.
+ private float[] mGravityVector;
+
+ // The geomagnetic vector expressed in the body frame.
+ private float[] mMagneticFieldVector;
+
+ // Lazily initialized when registering for notifications.
+ private SensorManager mSensorManager;
+
+ private DeviceOrientation() {
+ }
+
+ /**
+ * Start listening for sensor events. If this object is already listening
+ * for events, the old callback is unregistered first.
+ *
+ * @param nativePtr Value to pass to nativeGotOrientation() for each event.
+ * @param rateInMilliseconds Requested callback rate in milliseconds. The
+ * actual rate may be higher. Unwanted events should be ignored.
+ * @return True on success.
+ */
+ @CalledByNative
+ public synchronized boolean start(int nativePtr, int rateInMilliseconds) {
+ stop();
+ if (registerForSensors(rateInMilliseconds)) {
+ mNativePtr = nativePtr;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stop listening for sensor events. Always succeeds.
+ *
+ * We strictly guarantee that nativeGotOrientation() will not be called
+ * after this method returns.
+ */
+ @CalledByNative
+ public synchronized void stop() {
+ if (mNativePtr != 0) {
+ mNativePtr = 0;
+ unregisterForSensors();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Nothing
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ switch (event.sensor.getType()) {
+ case Sensor.TYPE_ACCELEROMETER:
+ if (mGravityVector == null) {
+ mGravityVector = new float[3];
+ }
+ System.arraycopy(event.values, 0, mGravityVector, 0, mGravityVector.length);
+ break;
+
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ if (mMagneticFieldVector == null) {
+ mMagneticFieldVector = new float[3];
+ }
+ System.arraycopy(event.values, 0, mMagneticFieldVector, 0,
+ mMagneticFieldVector.length);
+ break;
+
+ default:
+ // Unexpected
+ return;
+ }
+
+ getOrientationUsingGetRotationMatrix();
+ }
+
+ void getOrientationUsingGetRotationMatrix() {
+ if (mGravityVector == null || mMagneticFieldVector == null) {
+ return;
+ }
+
+ // Get the rotation matrix.
+ // The rotation matrix that transforms from the body frame to the earth
+ // frame.
+ float[] deviceRotationMatrix = new float[9];
+ if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mGravityVector,
+ mMagneticFieldVector)) {
+ return;
+ }
+
+ // Convert rotation matrix to rotation angles.
+ // Assuming that the rotations are appied in the order listed at
+ // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
+ // the rotations are applied about the same axes and in the same order as required by the
+ // API. The only conversions are sign changes as follows. The angles are in radians
+
+ float[] rotationAngles = new float[3];
+ SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
+
+ double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
+ while (alpha < 0.0) {
+ alpha += 360.0; // [0, 360)
+ }
+
+ double beta = Math.toDegrees(-rotationAngles[1]);
+ while (beta < -180.0) {
+ beta += 360.0; // [-180, 180)
+ }
+
+ double gamma = Math.toDegrees(rotationAngles[2]);
+ while (gamma < -90.0) {
+ gamma += 360.0; // [-90, 90)
+ }
+
+ gotOrientation(alpha, beta, gamma);
+ }
+
+ boolean registerForSensors(int rateInMilliseconds) {
+ if (registerForSensorType(Sensor.TYPE_ACCELEROMETER, rateInMilliseconds)
+ && registerForSensorType(Sensor.TYPE_MAGNETIC_FIELD, rateInMilliseconds)) {
+ return true;
+ }
+ unregisterForSensors();
+ return false;
+ }
+
+ private SensorManager getSensorManager() {
+ if (mSensorManager != null) {
+ return mSensorManager;
+ }
+ mSensorManager = (SensorManager)WeakContext.getSystemService(Context.SENSOR_SERVICE);
+ return mSensorManager;
+ }
+
+ void unregisterForSensors() {
+ SensorManager sensorManager = getSensorManager();
+ if (sensorManager == null) {
+ return;
+ }
+ sensorManager.unregisterListener(this);
+ }
+
+ boolean registerForSensorType(int type, int rateInMilliseconds) {
+ SensorManager sensorManager = getSensorManager();
+ if (sensorManager == null) {
+ return false;
+ }
+ List<Sensor> sensors = sensorManager.getSensorList(type);
+ if (sensors.isEmpty()) {
+ return false;
+ }
+
+ final int rateInMicroseconds = 1000 * rateInMilliseconds;
+ // We want to err on the side of getting more events than we need.
+ final int requestedRate = rateInMicroseconds / 2;
+
+ // TODO(steveblock): Consider handling multiple sensors.
+ return sensorManager.registerListener(this, sensors.get(0), requestedRate, getHandler());
+ }
+
+ synchronized void gotOrientation(double alpha, double beta, double gamma) {
+ if (mNativePtr != 0) {
+ nativeGotOrientation(mNativePtr, alpha, beta, gamma);
+ }
+ }
+
+ private synchronized Handler getHandler() {
+ // If we don't have a background thread, start it now.
+ if (mThread == null) {
+ mThread = new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+ // Our Handler doesn't actually have to do anything, because
+ // SensorManager posts directly to the underlying Looper.
+ setHandler(new Handler());
+ Looper.loop();
+ }
+ });
+ mThread.start();
+ }
+ // Wait for the background thread to spin up.
+ while (mHandler == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // Somebody doesn't want us to wait! That's okay, SensorManager accepts null.
+ return null;
+ }
+ }
+ return mHandler;
+ }
+
+ private synchronized void setHandler(Handler handler) {
+ mHandler = handler;
+ notify();
+ }
+
+ @CalledByNative
+ private static DeviceOrientation create() {
+ return new DeviceOrientation();
+ }
+
+ /**
+ * See chrome/browser/device_orientation/data_fetcher_impl_android.cc
+ */
+ private native void nativeGotOrientation(
+ int nativePtr /* device_orientation::DataFetcherImplAndroid */,
+ double alpha, double beta, double gamma);
+}