Merge "Fix pressed states on system bar buttons."
diff --git a/Android.mk b/Android.mk
index 30a67e3..3afbfd7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -113,7 +113,6 @@
core/java/android/content/pm/IPackageMoveObserver.aidl \
core/java/android/content/pm/IPackageStatsObserver.aidl \
core/java/android/database/IContentObserver.aidl \
- core/java/android/hardware/ISensorService.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/IThrottleManager.aidl \
diff --git a/api/current.xml b/api/current.xml
index e66e529..d429592 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19514,6 +19514,17 @@
visibility="public"
>
</method>
+<method name="getSelectedNavigationItem"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSubtitle"
return="java.lang.CharSequence"
abstract="true"
@@ -19601,19 +19612,6 @@
<parameter name="tab" type="android.app.ActionBar.Tab">
</parameter>
</method>
-<method name="selectTabAt"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-</method>
<method name="setBackgroundDrawable"
return="void"
abstract="true"
@@ -19683,6 +19681,36 @@
<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
</parameter>
</method>
+<method name="setDropdownNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.SpinnerAdapter">
+</parameter>
+<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+</parameter>
+<parameter name="defaultSelectedPosition" type="int">
+</parameter>
+</method>
+<method name="setSelectedNavigationItem"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="setStandardNavigationMode"
return="void"
abstract="true"
@@ -24608,7 +24636,7 @@
deprecated="not deprecated"
visibility="public"
>
-<method name="onContextItemClicked"
+<method name="onContextualItemClicked"
return="boolean"
abstract="true"
native="false"
@@ -24623,7 +24651,7 @@
<parameter name="item" type="android.view.MenuItem">
</parameter>
</method>
-<method name="onCreateContextMode"
+<method name="onCreateContextualMode"
return="boolean"
abstract="true"
native="false"
@@ -24638,7 +24666,7 @@
<parameter name="menu" type="android.view.Menu">
</parameter>
</method>
-<method name="onDestroyContextMode"
+<method name="onDestroyContextualMode"
return="void"
abstract="true"
native="false"
@@ -24651,7 +24679,7 @@
<parameter name="mode" type="android.app.ContextualMode">
</parameter>
</method>
-<method name="onPrepareContextMode"
+<method name="onPrepareContextualMode"
return="boolean"
abstract="true"
native="false"
@@ -61081,7 +61109,7 @@
type="android.database.sqlite.SQLiteCursor"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
@@ -61093,6 +61121,20 @@
<parameter name="query" type="android.database.sqlite.SQLiteQuery">
</parameter>
</constructor>
+<constructor name="SQLiteCursor"
+ type="android.database.sqlite.SQLiteCursor"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="driver" type="android.database.sqlite.SQLiteCursorDriver">
+</parameter>
+<parameter name="editTable" type="java.lang.String">
+</parameter>
+<parameter name="query" type="android.database.sqlite.SQLiteQuery">
+</parameter>
+</constructor>
<method name="getColumnNames"
return="java.lang.String[]"
abstract="false"
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index a880a91..457cbd4 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -10,11 +10,13 @@
$(base)/services/camera/libcameraservice \
$(base)/services/audioflinger \
$(base)/services/surfaceflinger \
+ $(base)/services/sensorservice \
$(base)/media/libmediaplayerservice \
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
+ libsensorservice \
libsurfaceflinger \
libaudioflinger \
libcameraservice \
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index 1d57fdc..a29ba73 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -19,6 +19,7 @@
#include <CameraService.h>
#include <AudioPolicyService.h>
#include <MediaPlayerService.h>
+#include <SensorService.h>
#include <android_runtime/AndroidRuntime.h>
@@ -69,6 +70,9 @@
SurfaceFlinger::instantiate();
}
+ // Start the sensor service
+ SensorService::instantiate();
+
// On the simulator, audioflinger et al don't get started the
// same way as on the device, and we need to start them here
if (!proc->supportsProcesses()) {
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index e1124a1..fbc0be3 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -93,6 +93,35 @@
NavigationCallback callback);
/**
+ * Set the action bar into dropdown navigation mode and supply an adapter that will
+ * provide views for navigation choices.
+ *
+ * @param adapter An adapter that will provide views both to display the current
+ * navigation selection and populate views within the dropdown
+ * navigation menu.
+ * @param callback A NavigationCallback that will receive events when the user
+ * selects a navigation item.
+ * @param defaultSelectedPosition Position within the provided adapter that should be
+ * selected from the outset.
+ */
+ public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
+ NavigationCallback callback, int defaultSelectedPosition);
+
+ /**
+ * Set the selected navigation item in dropdown or tabbed navigation modes.
+ *
+ * @param position Position of the item to select.
+ */
+ public abstract void setSelectedNavigationItem(int position);
+
+ /**
+ * Get the position of the selected navigation item in dropdown or tabbed navigation modes.
+ *
+ * @return Position of the selected item.
+ */
+ public abstract int getSelectedNavigationItem();
+
+ /**
* Set the action bar into standard navigation mode, supplying a title and subtitle.
*
* Standard navigation mode is default. The title is automatically set to the
@@ -289,13 +318,6 @@
public abstract void selectTab(Tab tab);
/**
- * Select the tab at <code>position</code>
- *
- * @param position Position of the tab to select
- */
- public abstract void selectTabAt(int position);
-
- /**
* Callback interface for ActionBar navigation events.
*/
public interface NavigationCallback {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a2a74f8..8eded3d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1206,7 +1206,7 @@
private DownloadManager getDownloadManager() {
synchronized (mSync) {
if (mDownloadManager == null) {
- mDownloadManager = new DownloadManager(getContentResolver());
+ mDownloadManager = new DownloadManager(getContentResolver(), getPackageName());
}
}
return mDownloadManager;
diff --git a/core/java/android/app/ContextualMode.java b/core/java/android/app/ContextualMode.java
index d187dc0..5e4b5df 100644
--- a/core/java/android/app/ContextualMode.java
+++ b/core/java/android/app/ContextualMode.java
@@ -61,7 +61,7 @@
/**
* Invalidate the contextual mode and refresh menu content. The contextual mode's
* {@link ContextualMode.Callback} will have its
- * {@link Callback#onPrepareContextMode(ContextualMode, Menu)} method called.
+ * {@link Callback#onPrepareContextualMode(ContextualMode, Menu)} method called.
* If it returns true the menu will be scanned for updated content and any relevant changes
* will be reflected to the user.
*/
@@ -69,7 +69,7 @@
/**
* Finish and close this context mode. The context mode's {@link ContextualMode.Callback} will
- * have its {@link Callback#onDestroyContextMode(ContextualMode)} method called.
+ * have its {@link Callback#onDestroyContextualMode(ContextualMode)} method called.
*/
public abstract void finish();
@@ -104,52 +104,52 @@
*
* <p>A context mode's lifecycle is as follows:
* <ul>
- * <li>{@link Callback#onCreateContextMode(ContextualMode, Menu)} once on initial
+ * <li>{@link Callback#onCreateContextualMode(ContextualMode, Menu)} once on initial
* creation</li>
- * <li>{@link Callback#onPrepareContextMode(ContextualMode, Menu)} after creation
+ * <li>{@link Callback#onPrepareContextualMode(ContextualMode, Menu)} after creation
* and any time the {@link ContextualMode} is invalidated</li>
- * <li>{@link Callback#onContextItemClicked(ContextualMode, MenuItem)} any time a
+ * <li>{@link Callback#onContextualItemClicked(ContextualMode, MenuItem)} any time a
* contextual action button is clicked</li>
- * <li>{@link Callback#onDestroyContextMode(ContextualMode)} when the context mode
+ * <li>{@link Callback#onDestroyContextualMode(ContextualMode)} when the context mode
* is closed</li>
* </ul>
*/
public interface Callback {
/**
- * Called when a context mode is first created. The menu supplied will be used to generate
- * action buttons for the context mode.
+ * Called when a contextual mode is first created. The menu supplied will be used to
+ * generate action buttons for the contextual mode.
*
- * @param mode ContextMode being created
+ * @param mode ContextualMode being created
* @param menu Menu used to populate contextual action buttons
- * @return true if the context mode should be created, false if entering this context mode
- * should be aborted.
+ * @return true if the contextual mode should be created, false if entering this
+ * mode should be aborted.
*/
- public boolean onCreateContextMode(ContextualMode mode, Menu menu);
+ public boolean onCreateContextualMode(ContextualMode mode, Menu menu);
/**
- * Called to refresh a context mode's action menu whenever it is invalidated.
+ * Called to refresh a contextual mode's action menu whenever it is invalidated.
*
- * @param mode ContextMode being prepared
+ * @param mode ContextualMode being prepared
* @param menu Menu used to populate contextual action buttons
- * @return true if the menu or context mode was updated, false otherwise.
+ * @return true if the menu or contextual mode was updated, false otherwise.
*/
- public boolean onPrepareContextMode(ContextualMode mode, Menu menu);
+ public boolean onPrepareContextualMode(ContextualMode mode, Menu menu);
/**
* Called to report a user click on a contextual action button.
*
- * @param mode The current ContextMode
+ * @param mode The current ContextualMode
* @param item The item that was clicked
* @return true if this callback handled the event, false if the standard MenuItem
* invocation should continue.
*/
- public boolean onContextItemClicked(ContextualMode mode, MenuItem item);
+ public boolean onContextualItemClicked(ContextualMode mode, MenuItem item);
/**
- * Called when a context mode is about to be exited and destroyed.
+ * Called when a contextual mode is about to be exited and destroyed.
*
- * @param mode The current ContextMode being destroyed
+ * @param mode The current ContextualMode being destroyed
*/
- public void onDestroyContextMode(ContextualMode mode);
+ public void onDestroyContextualMode(ContextualMode mode);
}
}
\ No newline at end of file
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index c9fdfba..7fd5dde 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -172,6 +172,7 @@
// Let the loader know we're done with it
mListenerRegistered = false;
mLoader.unregisterListener(this);
+ mLoader.stopLoading();
}
}
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index eecd01e..3fe6087 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -44,19 +44,16 @@
static final int NO_COUNT = -1;
/** The name of the table to edit */
- private String mEditTable;
+ private final String mEditTable;
/** The names of the columns in the rows */
- private String[] mColumns;
+ private final String[] mColumns;
/** The query object for the cursor */
private SQLiteQuery mQuery;
- /** The database the cursor was created from */
- private SQLiteDatabase mDatabase;
-
/** The compiled query this cursor came from */
- private SQLiteCursorDriver mDriver;
+ private final SQLiteCursorDriver mDriver;
/** The number of rows in the cursor */
private int mCount = NO_COUNT;
@@ -65,7 +62,7 @@
private Map<String, Integer> mColumnNameMap;
/** Used to find out where a cursor was allocated in case it never got released. */
- private Throwable mStackTrace;
+ private final Throwable mStackTrace;
/**
* mMaxRead is the max items that each cursor window reads
@@ -140,7 +137,7 @@
break;
}
try {
- int count = mQuery.fillWindow(cw, mMaxRead, mCount);
+ int count = getQuery().fillWindow(cw, mMaxRead, mCount);
// return -1 means not finished
if (count != 0) {
if (count == NO_COUNT){
@@ -205,24 +202,46 @@
* has package scope.
*
* @param db a reference to a Database object that is already constructed
- * and opened
+ * and opened. This param is not used any longer
* @param editTable the name of the table used for this query
* @param query the rest of the query terms
* cursor is finalized
+ * @deprecated use {@link #SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)} instead
*/
+ @Deprecated
public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
String editTable, SQLiteQuery query) {
+ this(driver, editTable, query);
+ }
+
+ /**
+ * Execute a query and provide access to its result set through a Cursor
+ * interface. For a query such as: {@code SELECT name, birth, phone FROM
+ * myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
+ * phone) would be in the projection argument and everything from
+ * {@code FROM} onward would be in the params argument. This constructor
+ * has package scope.
+ *
+ * @param editTable the name of the table used for this query
+ * @param query the {@link SQLiteQuery} object associated with this cursor object.
+ */
+ public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
// The AbstractCursor constructor needs to do some setup.
super();
+ if (query == null) {
+ throw new IllegalArgumentException("query object cannot be null");
+ }
+ if (query.mDatabase == null) {
+ throw new IllegalArgumentException("query.mDatabase cannot be null");
+ }
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
- mDatabase = db;
mDriver = driver;
mEditTable = editTable;
mColumnNameMap = null;
mQuery = query;
try {
- db.lock();
+ query.mDatabase.lock();
// Setup the list of columns
int columnCount = mQuery.columnCountLocked();
@@ -243,7 +262,7 @@
}
}
} finally {
- db.unlock();
+ query.mDatabase.unlock();
}
}
@@ -251,7 +270,9 @@
* @return the SQLiteDatabase that this cursor is associated with.
*/
public SQLiteDatabase getDatabase() {
- return mDatabase;
+ synchronized (this) {
+ return mQuery.mDatabase;
+ }
}
@Override
@@ -287,7 +308,7 @@
}
}
mWindow.setStartPosition(startPos);
- mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
+ mCount = getQuery().fillWindow(mWindow, mInitialRead, 0);
// return -1 means not finished
if (mCount == NO_COUNT){
mCount = startPos + mInitialRead;
@@ -296,6 +317,10 @@
}
}
+ private synchronized SQLiteQuery getQuery() {
+ return mQuery;
+ }
+
@Override
public int getColumnIndex(String columnName) {
// Create mColumnNameMap on demand
@@ -350,9 +375,11 @@
@Override
public void close() {
super.close();
- deactivateCommon();
- mQuery.close();
- mDriver.cursorClosed();
+ synchronized (this) {
+ deactivateCommon();
+ mQuery.close();
+ mDriver.cursorClosed();
+ }
}
/**
@@ -361,7 +388,7 @@
*/
private void warnIfUiThread() {
if (Looper.getMainLooper() == Looper.myLooper()) {
- String databasePath = mDatabase.getPath();
+ String databasePath = getQuery().mDatabase.getPath();
// We show the warning once per database in order not to spam logcat.
if (!sAlreadyWarned.containsKey(databasePath)) {
sAlreadyWarned.put(databasePath, true);
@@ -383,16 +410,25 @@
if (Config.LOGV) {
timeStart = System.currentTimeMillis();
}
- /*
- * Synchronize on the database lock to ensure that mCount matches the
- * results of mQuery.requery().
- */
- mDatabase.lock();
- try {
+
+ synchronized (this) {
if (mWindow != null) {
mWindow.clear();
}
mPos = -1;
+ SQLiteDatabase db = mQuery.mDatabase.getDatabaseHandle(mQuery.mSql);
+ if (!db.equals(mQuery.mDatabase)) {
+ // since we need to use a different database connection handle,
+ // re-compile the query
+ db.lock();
+ try {
+ // close the old mQuery object and open a new one
+ mQuery.close();
+ mQuery = new SQLiteQuery(db, mQuery);
+ } finally {
+ db.unlock();
+ }
+ }
// This one will recreate the temp table, and get its count
mDriver.cursorRequeried(this);
mCount = NO_COUNT;
@@ -403,8 +439,6 @@
} finally {
queryThreadUnlock();
}
- } finally {
- mDatabase.unlock();
}
if (Config.LOGV) {
@@ -452,14 +486,14 @@
if (mWindow != null) {
int len = mQuery.mSql.length();
Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
- "database = " + mDatabase.getPath() + ", table = " + mEditTable +
+ "database = " + mQuery.mDatabase.getPath() + ", table = " + mEditTable +
", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
mStackTrace);
close();
SQLiteDebug.notifyActiveCursorFinalized();
} else {
if (Config.LOGV) {
- Log.v(TAG, "Finalizing cursor on database = " + mDatabase.getPath() +
+ Log.v(TAG, "Finalizing cursor on database = " + mQuery.mDatabase.getPath() +
", table = " + mEditTable + ", query = " + mQuery.mSql);
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index d058858..32012d3 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -341,6 +341,12 @@
*/
/* package */ final short mConnectionNum;
+ /** on pooled database connections, this member points to the parent ( = main)
+ * database connection handle.
+ * package visibility only for testing purposes
+ */
+ /* package */ SQLiteDatabase mParentConnObj = null;
+
private static final String MEMORY_DB_PATH = ":memory:";
synchronized void addSQLiteClosable(SQLiteClosable closable) {
@@ -2425,6 +2431,27 @@
}
}
+ /* package */ SQLiteDatabase getDatabaseHandle(String sql) {
+ if (isPooledConnection()) {
+ // this is a pooled database connection
+ if (isOpen()) {
+ // TODO: use another connection from the pool
+ // if this connection is currently in use by some other thread
+ // AND if there are free connections in the pool
+ return this;
+ } else {
+ // the pooled connection is not open! could have been closed either due
+ // to corruption on this or some other connection to the database
+ // OR, maybe the connection pool is disabled after this connection has been
+ // allocated to me. try to get some other pooled or main database connection
+ return getParentDbConnObj().getDbConnection(sql);
+ }
+ } else {
+ // this is NOT a pooled connection. can we get one?
+ return getDbConnection(sql);
+ }
+ }
+
/**
* Sets the database connection handle pool size to the given value.
* Database connection handle pool is enabled when the app calls
@@ -2450,7 +2477,13 @@
}
/* package */ SQLiteDatabase createPoolConnection(short connectionNum) {
- return openDatabase(mPath, mFactory, mFlags, mErrorHandler, connectionNum);
+ SQLiteDatabase db = openDatabase(mPath, mFactory, mFlags, mErrorHandler, connectionNum);
+ db.mParentConnObj = this;
+ return db;
+ }
+
+ private synchronized SQLiteDatabase getParentDbConnObj() {
+ return mParentConnObj;
}
private boolean isPooledConnection() {
@@ -2459,29 +2492,30 @@
/* package */ SQLiteDatabase getDbConnection(String sql) {
verifyDbIsOpen();
+ // this method should always be called with main database connection handle
+ // NEVER with pooled database connection handle
+ if (isPooledConnection()) {
+ throw new IllegalStateException("incorrect database connection handle");
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ // this method shoudl never be called with anything other than SELECT
+ if (sql.substring(0, 6).equalsIgnoreCase("SELECT")) {
+ throw new IllegalStateException("unexpected SQL statement: " + sql);
+ }
+ }
// use the current connection handle if
- // 1. this is a pooled connection handle
- // 2. OR, if this thread is in a transaction
- // 3. OR, if there is NO connection handle pool setup
- SQLiteDatabase db = null;
- if (isPooledConnection() ||
- (inTransaction() && mLock.isHeldByCurrentThread()) ||
- (this.mConnectionPool == null)) {
- db = this;
+ // 1. if this thread is in a transaction
+ // 2. OR, if there is NO connection handle pool setup
+ if ((inTransaction() && mLock.isHeldByCurrentThread()) || mConnectionPool == null) {
+ return this;
} else {
// get a connection handle from the pool
if (Log.isLoggable(TAG, Log.DEBUG)) {
assert mConnectionPool != null;
}
- db = mConnectionPool.get(sql);
+ return mConnectionPool.get(sql);
}
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "getDbConnection threadid = " + Thread.currentThread().getId() +
- ", request on # " + mConnectionNum +
- ", assigned # " + db.mConnectionNum + ", " + getPath());
- }
- return db;
}
private void releaseDbConnection(SQLiteDatabase db) {
diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
index be49257..6cdfa69 100644
--- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
@@ -53,7 +53,7 @@
// Create the cursor
if (factory == null) {
- mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
+ mCursor = new SQLiteCursor(this, mEditTable, query);
} else {
mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
}
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index e6011ee..1732971 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -53,6 +53,18 @@
}
/**
+ * Constructor used to create new instance to replace a given instance of this class.
+ * This constructor is used when the current Query object is now associated with a different
+ * {@link SQLiteDatabase} object.
+ *
+ * @param db The database that this query object is associated with
+ * @param query the instance of {@link SQLiteQuery} to be replaced
+ */
+ /* package */ SQLiteQuery(SQLiteDatabase db, SQLiteQuery query) {
+ this(db, query.mSql, 0, query.mBindArgs);
+ }
+
+ /**
* Reads rows into a buffer. This method acquires the database lock.
*
* @param window The window to fill into
diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl
deleted file mode 100644
index 67180bd..0000000
--- a/core/java/android/hardware/ISensorService.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* //device/java/android/android/hardware/ISensorService.aidl
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.hardware;
-
-import android.os.Bundle;
-
-/**
- * {@hide}
- */
-interface ISensorService
-{
- Bundle getDataChannel();
- boolean enableSensor(IBinder listener, String name, int sensor, int enable);
-}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 492f8cc..e6750e6 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,12 +16,7 @@
package android.hardware;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
import android.os.Looper;
-import android.os.Parcelable;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.Handler;
@@ -33,8 +28,6 @@
import android.view.IWindowManager;
import android.view.Surface;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -339,7 +332,6 @@
/*-----------------------------------------------------------------------*/
- private ISensorService mSensorService;
Looper mMainLooper;
@SuppressWarnings("deprecation")
private HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
@@ -356,6 +348,7 @@
/* The thread and the sensor list are global to the process
* but the actual thread is spawned on demand */
private static SensorThread sSensorThread;
+ private static int sQueue;
// Used within this module from outside SensorManager, don't make private
static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
@@ -370,80 +363,41 @@
boolean mSensorsReady;
SensorThread() {
- // this gets to the sensor module. We can have only one per process.
- sensors_data_init();
}
@Override
protected void finalize() {
- sensors_data_uninit();
}
// must be called with sListeners lock
- boolean startLocked(ISensorService service) {
+ boolean startLocked() {
try {
if (mThread == null) {
- Bundle dataChannel = service.getDataChannel();
- if (dataChannel != null) {
- mSensorsReady = false;
- SensorThreadRunnable runnable = new SensorThreadRunnable(dataChannel);
- Thread thread = new Thread(runnable, SensorThread.class.getName());
- thread.start();
- synchronized (runnable) {
- while (mSensorsReady == false) {
- runnable.wait();
- }
+ mSensorsReady = false;
+ SensorThreadRunnable runnable = new SensorThreadRunnable();
+ Thread thread = new Thread(runnable, SensorThread.class.getName());
+ thread.start();
+ synchronized (runnable) {
+ while (mSensorsReady == false) {
+ runnable.wait();
}
- mThread = thread;
}
+ mThread = thread;
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in startLocked: ", e);
} catch (InterruptedException e) {
}
return mThread == null ? false : true;
}
private class SensorThreadRunnable implements Runnable {
- private Bundle mDataChannel;
- SensorThreadRunnable(Bundle dataChannel) {
- mDataChannel = dataChannel;
+ SensorThreadRunnable() {
}
private boolean open() {
// NOTE: this cannot synchronize on sListeners, since
// it's held in the main thread at least until we
// return from here.
-
- // this thread is guaranteed to be unique
- Parcelable[] pfds = mDataChannel.getParcelableArray("fds");
- FileDescriptor[] fds;
- if (pfds != null) {
- int length = pfds.length;
- fds = new FileDescriptor[length];
- for (int i = 0; i < length; i++) {
- ParcelFileDescriptor pfd = (ParcelFileDescriptor)pfds[i];
- fds[i] = pfd.getFileDescriptor();
- }
- } else {
- fds = null;
- }
- int[] ints = mDataChannel.getIntArray("ints");
- sensors_data_open(fds, ints);
- if (pfds != null) {
- try {
- // close our copies of the file descriptors,
- // since we are just passing these to the JNI code and not using them here.
- for (int i = pfds.length - 1; i >= 0; i--) {
- ParcelFileDescriptor pfd = (ParcelFileDescriptor)pfds[i];
- pfd.close();
- }
- } catch (IOException e) {
- // *shrug*
- Log.e(TAG, "IOException: ", e);
- }
- }
- mDataChannel = null;
+ sQueue = sensors_create_queue();
return true;
}
@@ -466,7 +420,7 @@
while (true) {
// wait for an event
- final int sensor = sensors_data_poll(values, status, timestamp);
+ final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
int accuracy = status[0];
synchronized (sListeners) {
@@ -478,7 +432,8 @@
}
// we have no more listeners or polling failed, terminate the thread
- sensors_data_close();
+ sensors_destroy_queue(sQueue);
+ sQueue = 0;
mThread = null;
break;
}
@@ -506,7 +461,7 @@
/*-----------------------------------------------------------------------*/
- private class ListenerDelegate extends Binder {
+ private class ListenerDelegate {
final SensorEventListener mSensorEventListener;
private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
private final Handler mHandler;
@@ -602,8 +557,6 @@
* {@hide}
*/
public SensorManager(Looper mainLooper) {
- mSensorService = ISensorService.Stub.asInterface(
- ServiceManager.getService(Context.SENSOR_SERVICE));
mMainLooper = mainLooper;
@@ -1051,42 +1004,37 @@
return false;
}
- try {
- synchronized (sListeners) {
- ListenerDelegate l = null;
- for (ListenerDelegate i : sListeners) {
- if (i.getListener() == listener) {
- l = i;
- break;
- }
- }
-
- String name = sensor.getName();
- int handle = sensor.getHandle();
- if (l == null) {
- result = false;
- l = new ListenerDelegate(listener, sensor, handler);
- sListeners.add(l);
- if (!sListeners.isEmpty()) {
- result = sSensorThread.startLocked(mSensorService);
- if (result) {
- result = mSensorService.enableSensor(l, name, handle, delay);
- if (!result) {
- // there was an error, remove the listeners
- sListeners.remove(l);
- }
- }
- }
- } else {
- result = mSensorService.enableSensor(l, name, handle, delay);
- if (result) {
- l.addSensor(sensor);
- }
+ synchronized (sListeners) {
+ ListenerDelegate l = null;
+ for (ListenerDelegate i : sListeners) {
+ if (i.getListener() == listener) {
+ l = i;
+ break;
}
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in registerListener: ", e);
- result = false;
+
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ if (l == null) {
+ result = false;
+ l = new ListenerDelegate(listener, sensor, handler);
+ sListeners.add(l);
+ if (!sListeners.isEmpty()) {
+ result = sSensorThread.startLocked();
+ if (result) {
+ result = sensors_enable_sensor(sQueue, name, handle, delay);
+ if (!result) {
+ // there was an error, remove the listeners
+ sListeners.remove(l);
+ }
+ }
+ }
+ } else {
+ result = sensors_enable_sensor(sQueue, name, handle, delay);
+ if (result) {
+ l.addSensor(sensor);
+ }
+ }
}
return result;
}
@@ -1095,27 +1043,23 @@
if (listener == null || sensor == null) {
return;
}
- try {
- synchronized (sListeners) {
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate l = sListeners.get(i);
- if (l.getListener() == listener) {
- // disable these sensors
- String name = sensor.getName();
- int handle = sensor.getHandle();
- mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
- // if we have no more sensors enabled on this listener,
- // take it off the list.
- if (l.removeSensor(sensor) == 0) {
- sListeners.remove(i);
- }
- break;
+ synchronized (sListeners) {
+ final int size = sListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate l = sListeners.get(i);
+ if (l.getListener() == listener) {
+ // disable these sensors
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
+ // if we have no more sensors enabled on this listener,
+ // take it off the list.
+ if (l.removeSensor(sensor) == 0) {
+ sListeners.remove(i);
}
+ break;
}
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in unregisterListener: ", e);
}
}
@@ -1123,25 +1067,21 @@
if (listener == null) {
return;
}
- try {
- synchronized (sListeners) {
- final int size = sListeners.size();
- for (int i=0 ; i<size ; i++) {
- ListenerDelegate l = sListeners.get(i);
- if (l.getListener() == listener) {
- // disable all sensors for this listener
- for (Sensor sensor : l.getSensors()) {
- String name = sensor.getName();
- int handle = sensor.getHandle();
- mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
- }
- sListeners.remove(i);
- break;
+ synchronized (sListeners) {
+ final int size = sListeners.size();
+ for (int i=0 ; i<size ; i++) {
+ ListenerDelegate l = sListeners.get(i);
+ if (l.getListener() == listener) {
+ // disable all sensors for this listener
+ for (Sensor sensor : l.getSensors()) {
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
}
+ sListeners.remove(i);
+ break;
}
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in unregisterListener: ", e);
}
}
@@ -1794,9 +1734,8 @@
private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
// Used within this module from outside SensorManager, don't make private
- static native int sensors_data_init();
- static native int sensors_data_uninit();
- static native int sensors_data_open(FileDescriptor[] fds, int[] ints);
- static native int sensors_data_close();
- static native int sensors_data_poll(float[] values, int[] status, long[] timestamp);
+ static native int sensors_create_queue();
+ static native void sensors_destroy_queue(int queue);
+ static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable);
+ static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);
}
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index 00b6864..455bd36 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -22,7 +22,6 @@
import android.database.CursorWrapper;
import android.os.ParcelFileDescriptor;
import android.provider.Downloads;
-import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
@@ -187,6 +186,23 @@
*/
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
+ /**
+ * Broadcast intent action sent by the download manager when a download completes.
+ */
+ public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
+
+ /**
+ * Broadcast intent action sent by the download manager when a running download notification is
+ * clicked.
+ */
+ public final static String ACTION_NOTIFICATION_CLICKED =
+ "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
+
+ /**
+ * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a
+ * long) of the download that just completed.
+ */
+ public static final String EXTRA_DOWNLOAD_ID = "extra_download_id";
// this array must contain all public columns
private static final String[] COLUMNS = new String[] {
@@ -227,19 +243,38 @@
*/
public static class Request {
/**
- * Bit flag for setShowNotification indicated a notification should be created while the
- * download is running.
+ * Bit flag for {@link #setShowNotification} indicating a notification should be created
+ * while the download is running.
*/
- private static final int NOTIFICATION_WHEN_RUNNING = 1;
+ public static final int NOTIFICATION_WHEN_RUNNING = 1;
- Uri mUri;
- Uri mDestinationUri;
- Map<String, String> mRequestHeaders = new HashMap<String, String>();
- String mTitle;
- String mDescription;
- int mNotificationFlags;
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_MOBILE}.
+ */
+ public static final int NETWORK_MOBILE = 1 << 0;
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_WIFI}.
+ */
+ public static final int NETWORK_WIFI = 1 << 1;
+
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_WIMAX}.
+ */
+ public static final int NETWORK_WIMAX = 1 << 2;
+
+ private Uri mUri;
+ private Uri mDestinationUri;
+ private Map<String, String> mRequestHeaders = new HashMap<String, String>();
+ private String mTitle;
+ private String mDescription;
+ private int mNotificationFlags = 0;
private String mMediaType;
+ private boolean mRoamingAllowed = true;
+ private int mAllowedNetworkTypes = ~0; // default to all network types allowed
/**
* @param uri the HTTP URI to download.
@@ -313,7 +348,7 @@
/**
* Control system notifications posted by the download manager for this download. If
* enabled, the download manager posts notifications about downloads through the system
- * {@link android.app.NotificationManager}.
+ * {@link android.app.NotificationManager}. By default, no notification is shown.
*
* @param flags any combination of the NOTIFICATION_* bit flags
* @return this object
@@ -323,23 +358,37 @@
return this;
}
+ /**
+ * Restrict the types of networks over which this download may proceed. By default, all
+ * network types are allowed.
+ * @param flags any combination of the NETWORK_* bit flags.
+ * @return this object
+ */
public Request setAllowedNetworkTypes(int flags) {
- // TODO allowed networks support
- throw new UnsupportedOperationException();
+ mAllowedNetworkTypes = flags;
+ return this;
}
+ /**
+ * Set whether this download may proceed over a roaming connection. By default, roaming is
+ * allowed.
+ * @param allowed whether to allow a roaming connection to be used
+ * @return this object
+ */
public Request setAllowedOverRoaming(boolean allowed) {
- // TODO roaming support
- throw new UnsupportedOperationException();
+ mRoamingAllowed = allowed;
+ return this;
}
/**
* @return ContentValues to be passed to DownloadProvider.insert()
*/
- ContentValues toContentValues() {
+ ContentValues toContentValues(String packageName) {
ContentValues values = new ContentValues();
assert mUri != null;
values.put(Downloads.COLUMN_URI, mUri.toString());
+ values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
+ values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE, packageName);
if (mDestinationUri != null) {
values.put(Downloads.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
@@ -363,6 +412,9 @@
}
values.put(Downloads.COLUMN_VISIBILITY, visibility);
+ values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
+ values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
+
return values;
}
@@ -441,7 +493,6 @@
+ " AND " + statusClause("<", 600) + ")");
}
selection = joinStrings(" OR ", parts);
- Log.w("DownloadManagerPublic", selection);
}
String orderBy = Downloads.COLUMN_LAST_MODIFICATION + " DESC";
return resolver.query(uri, projection, selection, null, orderBy);
@@ -466,12 +517,14 @@
}
private ContentResolver mResolver;
+ private String mPackageName;
/**
* @hide
*/
- public DownloadManager(ContentResolver resolver) {
+ public DownloadManager(ContentResolver resolver, String packageName) {
mResolver = resolver;
+ mPackageName = packageName;
}
/**
@@ -483,7 +536,7 @@
* calls related to this download.
*/
public long enqueue(Request request) {
- ContentValues values = request.toContentValues();
+ ContentValues values = request.toContentValues(mPackageName);
Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
long id = Long.parseLong(downloadUri.getLastPathSegment());
return id;
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 4df33e0..23fdb0b 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -53,6 +53,9 @@
/** Flag value: Content can be decompressed with {@link java.util.zip.GZIPOutputStream}. */
public static final int IS_GZIPPED = 4;
+ /** Flag value for serialization only: Value is a byte array, not a file descriptor */
+ private static final int HAS_BYTE_ARRAY = 8;
+
/**
* A single entry retrieved from the drop box.
* This may include a reference to a stream, so you must call
@@ -68,12 +71,25 @@
/** Create a new empty Entry with no contents. */
public Entry(String tag, long millis) {
- this(tag, millis, (Object) null, IS_EMPTY);
+ if (tag == null) throw new NullPointerException("tag == null");
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = null;
+ mFileDescriptor = null;
+ mFlags = IS_EMPTY;
}
/** Create a new Entry with plain text contents. */
public Entry(String tag, long millis, String text) {
- this(tag, millis, (Object) text.getBytes(), IS_TEXT);
+ if (tag == null) throw new NullPointerException("tag == null");
+ if (text == null) throw new NullPointerException("text == null");
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = text.getBytes();
+ mFileDescriptor = null;
+ mFlags = IS_TEXT;
}
/**
@@ -81,7 +97,16 @@
* The data array must not be modified after creating this entry.
*/
public Entry(String tag, long millis, byte[] data, int flags) {
- this(tag, millis, (Object) data, flags);
+ if (tag == null) throw new NullPointerException("tag == null");
+ if (((flags & IS_EMPTY) != 0) != (data == null)) {
+ throw new IllegalArgumentException("Bad flags: " + flags);
+ }
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = data;
+ mFileDescriptor = null;
+ mFlags = flags;
}
/**
@@ -89,7 +114,16 @@
* Takes ownership of the ParcelFileDescriptor.
*/
public Entry(String tag, long millis, ParcelFileDescriptor data, int flags) {
- this(tag, millis, (Object) data, flags);
+ if (tag == null) throw new NullPointerException("tag == null");
+ if (((flags & IS_EMPTY) != 0) != (data == null)) {
+ throw new IllegalArgumentException("Bad flags: " + flags);
+ }
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = null;
+ mFileDescriptor = data;
+ mFlags = flags;
}
/**
@@ -97,31 +131,14 @@
* The file will be read when the entry's contents are requested.
*/
public Entry(String tag, long millis, File data, int flags) throws IOException {
- this(tag, millis, (Object) ParcelFileDescriptor.open(
- data, ParcelFileDescriptor.MODE_READ_ONLY), flags);
- }
-
- /** Internal constructor for CREATOR.createFromParcel(). */
- private Entry(String tag, long millis, Object value, int flags) {
- if (tag == null) throw new NullPointerException();
- if (((flags & IS_EMPTY) != 0) != (value == null)) throw new IllegalArgumentException();
+ if (tag == null) throw new NullPointerException("tag == null");
+ if ((flags & IS_EMPTY) != 0) throw new IllegalArgumentException("Bad flags: " + flags);
mTag = tag;
mTimeMillis = millis;
+ mData = null;
+ mFileDescriptor = ParcelFileDescriptor.open(data, ParcelFileDescriptor.MODE_READ_ONLY);
mFlags = flags;
-
- if (value == null) {
- mData = null;
- mFileDescriptor = null;
- } else if (value instanceof byte[]) {
- mData = (byte[]) value;
- mFileDescriptor = null;
- } else if (value instanceof ParcelFileDescriptor) {
- mData = null;
- mFileDescriptor = (ParcelFileDescriptor) value;
- } else {
- throw new IllegalArgumentException();
- }
}
/** Close the input stream associated with this entry. */
@@ -149,6 +166,7 @@
InputStream is = null;
try {
is = getInputStream();
+ if (is == null) return null;
byte[] buf = new byte[maxBytes];
return new String(buf, 0, Math.max(0, is.read(buf)));
} catch (IOException e) {
@@ -174,8 +192,14 @@
public static final Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator() {
public Entry[] newArray(int size) { return new Entry[size]; }
public Entry createFromParcel(Parcel in) {
- return new Entry(
- in.readString(), in.readLong(), in.readValue(null), in.readInt());
+ String tag = in.readString();
+ long millis = in.readLong();
+ int flags = in.readInt();
+ if ((flags & HAS_BYTE_ARRAY) != 0) {
+ return new Entry(tag, millis, in.createByteArray(), flags & ~HAS_BYTE_ARRAY);
+ } else {
+ return new Entry(tag, millis, in.readFileDescriptor(), flags);
+ }
}
};
@@ -187,11 +211,12 @@
out.writeString(mTag);
out.writeLong(mTimeMillis);
if (mFileDescriptor != null) {
- out.writeValue(mFileDescriptor);
+ out.writeInt(mFlags & ~HAS_BYTE_ARRAY); // Clear bit just to be safe
+ mFileDescriptor.writeToParcel(out, flags);
} else {
- out.writeValue(mData);
+ out.writeInt(mFlags | HAS_BYTE_ARRAY);
+ out.writeByteArray(mData);
}
- out.writeInt(mFlags);
}
}
@@ -225,7 +250,7 @@
* @param flags describing the data
*/
public void addData(String tag, byte[] data, int flags) {
- if (data == null) throw new NullPointerException();
+ if (data == null) throw new NullPointerException("data == null");
try { mService.add(new Entry(tag, 0, data, flags)); } catch (RemoteException e) {}
}
@@ -239,7 +264,7 @@
* @throws IOException if the file can't be opened
*/
public void addFile(String tag, File file, int flags) throws IOException {
- if (file == null) throw new NullPointerException();
+ if (file == null) throw new NullPointerException("file == null");
Entry entry = new Entry(tag, 0, file, flags);
try {
mService.add(entry);
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 2a612fe..01db979 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -17,11 +17,6 @@
package android.provider;
import android.net.Uri;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-
-import java.io.File;
/**
* The Download Manager
@@ -856,6 +851,30 @@
*/
public static final String COLUMN_DESCRIPTION = "description";
+ /**
+ * The name of the column indicating whether the download was requesting through the public
+ * API. This controls some differences in behavior.
+ * <P>Type: BOOLEAN</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_IS_PUBLIC_API = "is_public_api";
+
+ /**
+ * The name of the column indicating whether roaming connections can be used. This is only
+ * used for public API downloads.
+ * <P>Type: BOOLEAN</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_ALLOW_ROAMING = "allow_roaming";
+
+ /**
+ * The name of the column holding a bitmask of allowed network types. This is only used for
+ * public API downloads.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
+
/*
* Lists the destinations that an application can specify for a download.
*/
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 1424638..78648ff 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -410,9 +410,21 @@
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
// Shaders are ignored when drawing bitmaps
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint
- );
+
+ int left, top, right, bottom;
+ if (src == null) {
+ left = top = 0;
+ right = bitmap.getWidth();
+ bottom = bitmap.getHeight();
+ } else {
+ left = src.left;
+ right = src.right;
+ top = src.top;
+ bottom = src.bottom;
+ }
+
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
@Override
@@ -420,8 +432,7 @@
// Shaders are ignored when drawing bitmaps
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint
- );
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
private native void nDrawBitmap(int renderer, int bitmap,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 15b8335..3c161ca 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -896,11 +896,25 @@
return super.dispatchTouchEvent(ev);
}
+ // Calculate the offset point into the target's local coordinates
+ float xc;
+ float yc;
+ if (target.hasIdentityMatrix() || mAttachInfo == null) {
+ xc = scrolledXFloat - (float) target.mLeft;
+ yc = scrolledYFloat - (float) target.mTop;
+ } else {
+ // non-identity matrix: transform the point into the view's coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = scrolledXFloat;
+ localXY[1] = scrolledYFloat;
+ target.getInverseMatrix().mapPoints(localXY);
+ xc = localXY[0] - (float) target.mLeft;
+ yc = localXY[1] - (float) target.mTop;
+ }
+
// if have a target, see if we're allowed to and want to intercept its
// events
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
- final float xc = scrolledXFloat - (float) target.mLeft;
- final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
@@ -922,20 +936,6 @@
// finally offset the event to the target's coordinate system and
// dispatch the event.
- float xc;
- float yc;
- if (hasIdentityMatrix() || mAttachInfo == null) {
- xc = scrolledXFloat - (float) target.mLeft;
- yc = scrolledYFloat - (float) target.mTop;
- } else {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = scrolledXFloat;
- localXY[1] = scrolledYFloat;
- getInverseMatrix().mapPoints(localXY);
- xc = localXY[0] - (float) target.mLeft;
- yc = localXY[1] - (float) target.mTop;
- }
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index e6fa405..5b256f2 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -110,6 +110,7 @@
private RequestHandle mRequestHandle;
private RequestHandle mSslErrorRequestHandle;
private long mPostIdentifier;
+ private boolean mSetNativeResponse;
// Request data. It is only valid when we are doing a load from the
// cache. It is needed if the cache returns a redirect
@@ -181,6 +182,7 @@
private void clearNativeLoader() {
sNativeLoaderCount -= 1;
mNativeLoader = 0;
+ mSetNativeResponse = false;
}
/*
@@ -1086,13 +1088,18 @@
// request with some credentials then don't commit the headers
// of this response; wait for the response to the request with the
// credentials.
- if (mAuthHeader != null)
+ if (mAuthHeader != null) {
return;
+ }
- // Commit the headers to WebCore
+ setNativeResponse();
+ }
+
+ private void setNativeResponse() {
int nativeResponse = createNativeResponse();
// The native code deletes the native response object.
nativeReceivedResponse(nativeResponse);
+ mSetNativeResponse = true;
}
/**
@@ -1127,6 +1134,9 @@
*/
private void commitLoad() {
if (mCancelled) return;
+ if (!mSetNativeResponse) {
+ setNativeResponse();
+ }
if (mIsMainPageLoader) {
String type = sCertificateTypeMap.get(mMimeType);
@@ -1197,6 +1207,10 @@
}
if (mNativeLoader != 0) {
PerfChecker checker = new PerfChecker();
+ if (!mSetNativeResponse) {
+ setNativeResponse();
+ }
+
nativeFinished();
checker.responseAlert("res nativeFinished");
clearNativeLoader();
@@ -1312,6 +1326,9 @@
final String text = mContext
.getString(R.string.open_permission_deny)
+ "\n" + redirectTo;
+ if (!mSetNativeResponse) {
+ setNativeResponse();
+ }
nativeAddData(text.getBytes(), text.length());
nativeFinished();
clearNativeLoader();
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 663743c..fd0780f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -401,6 +401,10 @@
private float mLastVelX;
private float mLastVelY;
+ // A pointer to the native scrollable layer when dragging layers. Only
+ // valid when mTouchMode is TOUCH_DRAG_LAYER_MODE.
+ private int mScrollingLayer;
+
// only trigger accelerated fling if the new velocity is at least
// MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
@@ -417,6 +421,7 @@
private static final int TOUCH_DOUBLE_TAP_MODE = 6;
private static final int TOUCH_DONE_MODE = 7;
private static final int TOUCH_PINCH_DRAG = 8;
+ private static final int TOUCH_DRAG_LAYER_MODE = 9;
// Whether to forward the touch events to WebCore
private boolean mForwardTouchEvents = false;
@@ -883,6 +888,9 @@
mNavSlop = (int) (16 * density);
mZoomManager.init(density);
mMaximumFling = configuration.getScaledMaximumFlingVelocity();
+
+ // Compute the inverse of the density squared.
+ DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
}
/**
@@ -4540,6 +4548,25 @@
startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
}
+ private void startScrollingLayer(float gestureX, float gestureY) {
+ if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+ int contentX = viewToContentX((int) gestureX + mScrollX);
+ int contentY = viewToContentY((int) gestureY + mScrollY);
+ mScrollingLayer = nativeScrollableLayer(contentX, contentY);
+ if (mScrollingLayer != 0) {
+ mTouchMode = TOUCH_DRAG_LAYER_MODE;
+ }
+ }
+ }
+
+ // 1/(density * density) used to compute the distance between points.
+ // Computed in init().
+ private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
+
+ // The distance between two points reported in onTouchEvent scaled by the
+ // density of the screen.
+ private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
@@ -4551,20 +4578,73 @@
+ mTouchMode);
}
- int action;
- float x, y;
+ int action = ev.getAction();
+ float x = ev.getX();
+ float y = ev.getY();
long eventTime = ev.getEventTime();
+ final ScaleGestureDetector detector =
+ mZoomManager.getMultiTouchGestureDetector();
+ boolean skipScaleGesture = false;
+ // Set to the mid-point of a two-finger gesture used to detect if the
+ // user has touched a layer.
+ float gestureX = x;
+ float gestureY = y;
+ if (!detector.isInProgress()) {
+ // The gesture for scrolling a layer is two fingers close together.
+ // FIXME: we may consider giving WebKit an option to handle
+ // multi-touch events later.
+ if (ev.getPointerCount() > 1) {
+ float dx = ev.getX(1) - ev.getX(0);
+ float dy = ev.getY(1) - ev.getY(0);
+ float dist = (dx * dx + dy * dy) *
+ DRAG_LAYER_INVERSE_DENSITY_SQUARED;
+ // Use the approximate center to determine if the gesture is in
+ // a layer.
+ gestureX = ev.getX(0) + (dx * .5f);
+ gestureY = ev.getY(0) + (dy * .5f);
+ // Now use a consistent point for tracking movement.
+ if (ev.getX(0) < ev.getX(1)) {
+ x = ev.getX(0);
+ y = ev.getY(0);
+ } else {
+ x = ev.getX(1);
+ y = ev.getY(1);
+ }
+ action = ev.getActionMasked();
+ if (dist < DRAG_LAYER_FINGER_DISTANCE) {
+ skipScaleGesture = true;
+ } else if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+ // Fingers moved too far apart while dragging, the user
+ // might be trying to zoom.
+ mTouchMode = TOUCH_INIT_MODE;
+ }
+ }
+ }
+
// FIXME: we may consider to give WebKit an option to handle multi-touch
// events later.
- if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1) {
+ if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 &&
+ mTouchMode != TOUCH_DRAG_LAYER_MODE && !skipScaleGesture) {
- // if the page disallows zoom, then skip multi-pointer action
+ // if the page disallows zoom, skip multi-pointer action
if (mZoomManager.isZoomScaleFixed()) {
return true;
}
- ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ if (!detector.isInProgress() &&
+ ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) {
+ // Insert a fake pointer down event in order to start
+ // the zoom scale detector.
+ MotionEvent temp = MotionEvent.obtain(ev);
+ // Clear the original event and set it to
+ // ACTION_POINTER_DOWN.
+ temp.setAction(temp.getAction() &
+ ~MotionEvent.ACTION_MASK |
+ MotionEvent.ACTION_POINTER_DOWN);
+ detector.onTouchEvent(temp);
+ }
+
detector.onTouchEvent(ev);
if (detector.isInProgress()) {
@@ -4588,22 +4668,14 @@
return true;
}
}
- } else {
- action = ev.getAction();
- x = ev.getX();
- y = ev.getY();
}
// Due to the touch screen edge effect, a touch closer to the edge
// always snapped to the edge. As getViewWidth() can be different from
// getWidth() due to the scrollbar, adjusting the point to match
// getViewWidth(). Same applied to the height.
- if (x > getViewWidth() - 1) {
- x = getViewWidth() - 1;
- }
- if (y > getViewHeightWithTitle() - 1) {
- y = getViewHeightWithTitle() - 1;
- }
+ x = Math.min(x, getViewWidth() - 1);
+ y = Math.min(y, getViewHeightWithTitle() - 1);
float fDeltaX = mLastTouchX - x;
float fDeltaY = mLastTouchY - y;
@@ -4776,7 +4848,9 @@
invalidate();
break;
}
- if (mTouchMode != TOUCH_DRAG_MODE) {
+
+ if (mTouchMode != TOUCH_DRAG_MODE &&
+ mTouchMode != TOUCH_DRAG_LAYER_MODE) {
if (!mConfirmMove) {
break;
@@ -4808,6 +4882,9 @@
deltaX = 0;
deltaY = 0;
+ if (skipScaleGesture) {
+ startScrollingLayer(gestureX, gestureY);
+ }
startDrag();
}
@@ -4816,17 +4893,19 @@
}
// do pan
- int newScrollX = pinLocX(mScrollX + deltaX);
- int newDeltaX = newScrollX - mScrollX;
- if (deltaX != newDeltaX) {
- deltaX = newDeltaX;
- fDeltaX = (float) newDeltaX;
- }
- int newScrollY = pinLocY(mScrollY + deltaY);
- int newDeltaY = newScrollY - mScrollY;
- if (deltaY != newDeltaY) {
- deltaY = newDeltaY;
- fDeltaY = (float) newDeltaY;
+ if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+ int newScrollX = pinLocX(mScrollX + deltaX);
+ int newDeltaX = newScrollX - mScrollX;
+ if (deltaX != newDeltaX) {
+ deltaX = newDeltaX;
+ fDeltaX = (float) newDeltaX;
+ }
+ int newScrollY = pinLocY(mScrollY + deltaY);
+ int newDeltaY = newScrollY - mScrollY;
+ if (deltaY != newDeltaY) {
+ deltaY = newDeltaY;
+ fDeltaY = (float) newDeltaY;
+ }
}
boolean done = false;
boolean keepScrollBarsVisible = false;
@@ -4894,7 +4973,9 @@
doDrag(deltaX, deltaY);
- if (keepScrollBarsVisible) {
+ // Turn off scrollbars when dragging a layer.
+ if (keepScrollBarsVisible &&
+ mTouchMode != TOUCH_DRAG_LAYER_MODE) {
if (mHeldMotionless != MOTIONLESS_TRUE) {
mHeldMotionless = MOTIONLESS_TRUE;
invalidate();
@@ -5021,6 +5102,7 @@
invalidate();
// fall through
case TOUCH_DRAG_START_MODE:
+ case TOUCH_DRAG_LAYER_MODE:
// TOUCH_DRAG_START_MODE should not happen for the real
// device as we almost certain will get a MOVE. But this
// is possible on emulator.
@@ -5086,6 +5168,14 @@
private void doDrag(int deltaX, int deltaY) {
if ((deltaX | deltaY) != 0) {
+ if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+ deltaX = viewToContentDimension(deltaX);
+ deltaY = viewToContentDimension(deltaY);
+ if (nativeScrollLayer(mScrollingLayer, deltaX, deltaY)) {
+ invalidate();
+ }
+ return;
+ }
scrollBy(deltaX, deltaY);
}
mZoomManager.keepZoomPickerVisible();
@@ -5115,7 +5205,8 @@
mVelocityTracker.recycle();
mVelocityTracker = null;
}
- if (mTouchMode == TOUCH_DRAG_MODE) {
+ if (mTouchMode == TOUCH_DRAG_MODE ||
+ mTouchMode == TOUCH_DRAG_LAYER_MODE) {
WebViewCore.resumePriority();
WebViewCore.resumeUpdatePicture(mWebViewCore);
}
@@ -7117,4 +7208,8 @@
// return NO_LEFTEDGE means failure.
static final int NO_LEFTEDGE = -1;
native int nativeGetBlockLeftEdge(int x, int y, float scale);
+
+ // Returns a pointer to the scrollable LayerAndroid at the given point.
+ private native int nativeScrollableLayer(int x, int y);
+ private native boolean nativeScrollLayer(int layer, int dx, int dy);
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index ec6d2be..409edf9 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -17,7 +17,6 @@
package com.android.internal.app;
import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuItemImpl;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.SubMenuBuilder;
import com.android.internal.widget.ActionBarContextView;
@@ -114,10 +113,18 @@
}
public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+ setDropdownNavigationMode(adapter, callback, -1);
+ }
+
+ public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
+ int defaultSelectedPosition) {
cleanupTabs();
- mActionView.setCallback(callback);
mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
mActionView.setDropdownAdapter(adapter);
+ if (defaultSelectedPosition >= 0) {
+ mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
+ }
+ mActionView.setCallback(callback);
}
public void setStandardNavigationMode() {
@@ -139,6 +146,31 @@
mActionView.setCallback(null);
}
+ public void setSelectedNavigationItem(int position) {
+ switch (mActionView.getNavigationMode()) {
+ case NAVIGATION_MODE_TABS:
+ selectTab(mTabs.get(position));
+ break;
+ case NAVIGATION_MODE_DROPDOWN_LIST:
+ mActionView.setDropdownSelectedPosition(position);
+ break;
+ default:
+ throw new IllegalStateException(
+ "setSelectedNavigationItem not valid for current navigation mode");
+ }
+ }
+
+ public int getSelectedNavigationItem() {
+ switch (mActionView.getNavigationMode()) {
+ case NAVIGATION_MODE_TABS:
+ return mSelectedTab.getPosition();
+ case NAVIGATION_MODE_DROPDOWN_LIST:
+ return mActionView.getDropdownSelectedPosition();
+ default:
+ return -1;
+ }
+ }
+
private void cleanupTabs() {
if (mSelectedTab != null) {
selectTab(null);
@@ -208,7 +240,7 @@
}
ContextMode mode = new ContextMode(callback);
- if (callback.onCreateContextMode(mode, mode.getMenu())) {
+ if (callback.onCreateContextualMode(mode, mode.getMenu())) {
mode.invalidate();
mUpperContextView.initForMode(mode);
mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
@@ -330,11 +362,6 @@
trans.commit();
}
- @Override
- public void selectTabAt(int position) {
- selectTab(mTabs.get(position));
- }
-
/**
* @hide
*/
@@ -356,7 +383,7 @@
@Override
public void finish() {
- mCallback.onDestroyContextMode(this);
+ mCallback.onDestroyContextualMode(this);
mAnimatorView.setDisplayedChild(NORMAL_VIEW);
// Clear out the context mode views after the animation finishes
@@ -372,7 +399,7 @@
@Override
public void invalidate() {
- if (mCallback.onPrepareContextMode(this, mMenu)) {
+ if (mCallback.onPrepareContextualMode(this, mMenu)) {
// Refresh content in both context views
}
}
@@ -409,7 +436,7 @@
}
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- return mCallback.onContextItemClicked(this, item);
+ return mCallback.onContextualItemClicked(this, item);
}
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 8f8b3af..d703a2f 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -343,7 +343,15 @@
public void setDropdownAdapter(SpinnerAdapter adapter) {
mSpinner.setAdapter(adapter);
}
-
+
+ public void setDropdownSelectedPosition(int position) {
+ mSpinner.setSelection(position);
+ }
+
+ public int getDropdownSelectedPosition() {
+ return mSpinner.getSelectedItemPosition();
+ }
+
public View getCustomNavigationView() {
return mCustomNavView;
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index fea5ae3..77c77f9 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -171,6 +171,7 @@
libnetutils \
libui \
libhwui \
+ libgui \
libsurfaceflinger_client \
libcamera_client \
libskiagl \
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 9a90b72..7b23418 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -18,8 +18,9 @@
#include "utils/Log.h"
-#include <hardware/sensors.h>
-#include <cutils/native_handle.h>
+#include <gui/Sensor.h>
+#include <gui/SensorManager.h>
+#include <gui/SensorEventQueue.h>
#include "jni.h"
#include "JNIHelp.h"
@@ -43,44 +44,36 @@
* The method below are not thread-safe and not intended to be
*/
-static sensors_module_t* sSensorModule = 0;
-static sensors_data_device_t* sSensorDevice = 0;
static jint
sensors_module_init(JNIEnv *env, jclass clazz)
{
- int err = 0;
- sensors_module_t const* module;
- err = hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t **)&module);
- if (err == 0)
- sSensorModule = (sensors_module_t*)module;
- return err;
+ SensorManager::getInstance();
+ return 0;
}
static jint
sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint next)
{
- if (sSensorModule == NULL)
- return 0;
+ SensorManager& mgr(SensorManager::getInstance());
- SensorOffsets& sensorOffsets = gSensorOffsets;
- const struct sensor_t* list;
- int count = sSensorModule->get_sensors_list(sSensorModule, &list);
+ Sensor const* const* sensorList;
+ size_t count = mgr.getSensorList(&sensorList);
if (next >= count)
return -1;
- list += next;
-
- jstring name = env->NewStringUTF(list->name);
- jstring vendor = env->NewStringUTF(list->vendor);
+ Sensor const* const list = sensorList[next];
+ const SensorOffsets& sensorOffsets(gSensorOffsets);
+ jstring name = env->NewStringUTF(list->getName().string());
+ jstring vendor = env->NewStringUTF(list->getVendor().string());
env->SetObjectField(sensor, sensorOffsets.name, name);
env->SetObjectField(sensor, sensorOffsets.vendor, vendor);
- env->SetIntField(sensor, sensorOffsets.version, list->version);
- env->SetIntField(sensor, sensorOffsets.handle, list->handle);
- env->SetIntField(sensor, sensorOffsets.type, list->type);
- env->SetFloatField(sensor, sensorOffsets.range, list->maxRange);
- env->SetFloatField(sensor, sensorOffsets.resolution, list->resolution);
- env->SetFloatField(sensor, sensorOffsets.power, list->power);
+ env->SetIntField(sensor, sensorOffsets.version, 1);
+ env->SetIntField(sensor, sensorOffsets.handle, list->getHandle());
+ env->SetIntField(sensor, sensorOffsets.type, list->getType());
+ env->SetFloatField(sensor, sensorOffsets.range, list->getMaxValue());
+ env->SetFloatField(sensor, sensorOffsets.resolution, list->getResolution());
+ env->SetFloatField(sensor, sensorOffsets.power, list->getPowerUsage());
next++;
return next<count ? next : 0;
@@ -88,75 +81,64 @@
//----------------------------------------------------------------------------
static jint
-sensors_data_init(JNIEnv *env, jclass clazz)
+sensors_create_queue(JNIEnv *env, jclass clazz)
{
- if (sSensorModule == NULL)
- return -1;
- int err = sensors_data_open(&sSensorModule->common, &sSensorDevice);
- return err;
+ SensorManager& mgr(SensorManager::getInstance());
+ sp<SensorEventQueue> queue(mgr.createEventQueue());
+ queue->incStrong(clazz);
+ return reinterpret_cast<int>(queue.get());
}
-static jint
-sensors_data_uninit(JNIEnv *env, jclass clazz)
+static void
+sensors_destroy_queue(JNIEnv *env, jclass clazz, jint nativeQueue)
{
- int err = 0;
- if (sSensorDevice) {
- err = sensors_data_close(sSensorDevice);
- if (err == 0)
- sSensorDevice = 0;
+ sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
+ if (queue != 0) {
+ queue->decStrong(clazz);
}
- return err;
}
-static jint
-sensors_data_open(JNIEnv *env, jclass clazz, jobjectArray fdArray, jintArray intArray)
+static jboolean
+sensors_enable_sensor(JNIEnv *env, jclass clazz,
+ jint nativeQueue, jstring name, jint sensor, jint delay)
{
- jclass FileDescriptor = env->FindClass("java/io/FileDescriptor");
- jfieldID fieldOffset = env->GetFieldID(FileDescriptor, "descriptor", "I");
- int numFds = (fdArray ? env->GetArrayLength(fdArray) : 0);
- int numInts = (intArray ? env->GetArrayLength(intArray) : 0);
- native_handle_t* handle = native_handle_create(numFds, numInts);
- int offset = 0;
-
- for (int i = 0; i < numFds; i++) {
- jobject fdo = env->GetObjectArrayElement(fdArray, i);
- if (fdo) {
- handle->data[offset++] = env->GetIntField(fdo, fieldOffset);
- } else {
- handle->data[offset++] = -1;
- }
+ sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
+ if (queue == 0) return JNI_FALSE;
+ status_t res;
+ if (delay >= 0) {
+ res = queue->enableSensor(sensor, delay);
+ } else {
+ res = queue->disableSensor(sensor);
}
- if (numInts > 0) {
- jint* ints = env->GetIntArrayElements(intArray, 0);
- for (int i = 0; i < numInts; i++) {
- handle->data[offset++] = ints[i];
- }
- env->ReleaseIntArrayElements(intArray, ints, 0);
- }
-
- // doesn't take ownership of the native handle
- return sSensorDevice->data_open(sSensorDevice, handle);
+ return res == NO_ERROR ? true : false;
}
static jint
-sensors_data_close(JNIEnv *env, jclass clazz)
-{
- return sSensorDevice->data_close(sSensorDevice);
-}
-
-static jint
-sensors_data_poll(JNIEnv *env, jclass clazz,
+sensors_data_poll(JNIEnv *env, jclass clazz, jint nativeQueue,
jfloatArray values, jintArray status, jlongArray timestamp)
{
- sensors_data_t data;
- int res = sSensorDevice->poll(sSensorDevice, &data);
- if (res >= 0) {
- jint accuracy = data.vector.status;
- env->SetFloatArrayRegion(values, 0, 3, data.vector.v);
- env->SetIntArrayRegion(status, 0, 1, &accuracy);
- env->SetLongArrayRegion(timestamp, 0, 1, &data.time);
+ sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
+ if (queue == 0) return -1;
+
+ status_t res;
+ ASensorEvent event;
+
+ res = queue->read(&event, 1);
+ if (res == -EAGAIN) {
+ res = queue->waitForEvent();
+ if (res != NO_ERROR)
+ return -1;
+ res = queue->read(&event, 1);
}
- return res;
+ if (res < 0)
+ return -1;
+
+ jint accuracy = event.vector.status;
+ env->SetFloatArrayRegion(values, 0, 3, event.vector.v);
+ env->SetIntArrayRegion(status, 0, 1, &accuracy);
+ env->SetLongArrayRegion(timestamp, 0, 1, &event.timestamp);
+
+ return event.sensor;
}
static void
@@ -179,11 +161,13 @@
{"sensors_module_init","()I", (void*)sensors_module_init },
{"sensors_module_get_next_sensor","(Landroid/hardware/Sensor;I)I",
(void*)sensors_module_get_next_sensor },
- {"sensors_data_init", "()I", (void*)sensors_data_init },
- {"sensors_data_uninit", "()I", (void*)sensors_data_uninit },
- {"sensors_data_open", "([Ljava/io/FileDescriptor;[I)I", (void*)sensors_data_open },
- {"sensors_data_close", "()I", (void*)sensors_data_close },
- {"sensors_data_poll", "([F[I[J)I", (void*)sensors_data_poll },
+
+ {"sensors_create_queue", "()I", (void*)sensors_create_queue },
+ {"sensors_destroy_queue", "(I)V", (void*)sensors_destroy_queue },
+ {"sensors_enable_sensor", "(ILjava/lang/String;II)Z",
+ (void*)sensors_enable_sensor },
+
+ {"sensors_data_poll", "(I[F[I[J)I", (void*)sensors_data_poll },
};
}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index fa4d23c..2f1dcb6 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -255,7 +255,8 @@
OpenGLRenderer* renderer, jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- // TODO: draw from textArray + index
+ // TODO: Prepare the text for RTL
+ renderer->drawText((const char*) (textArray + index), count, x, y, paint);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
@@ -263,7 +264,8 @@
OpenGLRenderer* renderer, jstring text, int start, int end,
jfloat x, jfloat y, int flags, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
- // TODO: draw from textArray + start
+ // TODO: Prepare the text for RTL
+ renderer->drawText((const char*) (textArray + start), end - start, x, y, paint);
env->ReleaseStringChars(text, textArray);
}
diff --git a/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java b/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java
index bb5e024..525dd2d 100644
--- a/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java
@@ -17,6 +17,7 @@
package android.database.sqlite;
import android.content.Context;
+import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -73,6 +74,7 @@
SQLiteDatabase db = mTestPool.get(TEST_SQL);
// pool size should be one - since only one should be allocated for the above get()
assertEquals(1, mTestPool.getSize());
+ assertEquals(mDatabase, db.mParentConnObj);
// no free connections should be available
assertEquals(0, mTestPool.getFreePoolSize());
assertFalse(mTestPool.isDatabaseObjFree(db));
@@ -104,6 +106,7 @@
SQLiteDatabase db = mTestPool.get(TEST_SQL);
assertFalse(dbObjs.contains(db));
dbObjs.add(db);
+ assertEquals(mDatabase, db.mParentConnObj);
}
assertEquals(0, mTestPool.getFreePoolSize());
assertEquals(MAX_CONN, mTestPool.getSize());
@@ -197,11 +200,12 @@
}
private void executeSqlOnDatabaseConn(SQLiteDatabase db, String sql) {
- // execute the given SQL on the given database connection so that the prepared
- // statement for SQL is cached by the given database connection
+ // get the given sql be compiled on the given database connection.
// this will help DatabaseConenctionPool figure out if a given SQL statement
// is already cached by a database connection.
- db.execSQL(sql, new String[]{1+""});
+ ClassToTestSqlCompilationAndCaching c =
+ ClassToTestSqlCompilationAndCaching.create(db, sql);
+ c.close();
}
/**
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
new file mode 100644
index 0000000..3c3ff3f
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+
+public class SQLiteCursorTest extends AndroidTestCase {
+ private SQLiteDatabase mDatabase;
+ private File mDatabaseFile;
+ private static final String TABLE_NAME = "testCursor";
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+ mDatabaseFile = new File(dbDir, "sqlitecursor_test.db");
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+ assertNotNull(mDatabase);
+ // create a test table
+ mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ super.tearDown();
+ }
+
+ @SmallTest
+ public void testQueryObjReassignment() {
+ mDatabase.enableWriteAheadLogging();
+ // have a few connections in the database connection pool
+ DatabaseConnectionPool pool = mDatabase.mConnectionPool;
+ pool.setMaxPoolSize(5);
+ SQLiteCursor cursor =
+ (SQLiteCursor) mDatabase.rawQuery("select * from " + TABLE_NAME, null);
+ assertNotNull(cursor);
+ // it should use a pooled database connection
+ SQLiteDatabase db = cursor.getDatabase();
+ assertTrue(db.mConnectionNum > 0);
+ assertFalse(mDatabase.equals(db));
+ assertEquals(mDatabase, db.mParentConnObj);
+ assertTrue(pool.getConnectionList().contains(db));
+ assertTrue(db.isOpen());
+ // do a requery. cursor should continue to use the above pooled connection
+ cursor.requery();
+ SQLiteDatabase dbAgain = cursor.getDatabase();
+ assertEquals(db, dbAgain);
+ // disable WAL so that the pooled connection held by the above cursor is closed
+ mDatabase.disableWriteAheadLogging();
+ assertFalse(db.isOpen());
+ assertNull(mDatabase.mConnectionPool);
+ // requery - which should make the cursor use mDatabase connection since the pooled
+ // connection is no longer available
+ cursor.requery();
+ SQLiteDatabase db1 = cursor.getDatabase();
+ assertTrue(db1.mConnectionNum == 0);
+ assertEquals(mDatabase, db1);
+ assertNull(mDatabase.mConnectionPool);
+ assertTrue(db1.isOpen());
+ assertFalse(mDatabase.equals(db));
+ // enable WAL and requery - this time a pooled connection should be used
+ mDatabase.enableWriteAheadLogging();
+ cursor.requery();
+ db = cursor.getDatabase();
+ assertTrue(db.mConnectionNum > 0);
+ assertFalse(mDatabase.equals(db));
+ assertEquals(mDatabase, db.mParentConnObj);
+ assertTrue(mDatabase.mConnectionPool.getConnectionList().contains(db));
+ assertTrue(db.isOpen());
+ }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 91ef0b7..662ba97 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -17,6 +17,7 @@
package android.database.sqlite;
import android.content.Context;
+import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
@@ -78,6 +79,55 @@
}
@SmallTest
+ public void testDisableWriteAheadLogging() {
+ mDatabase.execSQL("create table test (i int);");
+ mDatabase.enableWriteAheadLogging();
+ assertNotNull(mDatabase.mConnectionPool);
+ // get a pooled database connection
+ SQLiteDatabase db = mDatabase.getDbConnection("select * from test");
+ assertNotNull(db);
+ assertFalse(mDatabase.equals(db));
+ assertTrue(db.isOpen());
+ // disable WAL - which should close connection pool and all pooled connections
+ mDatabase.disableWriteAheadLogging();
+ assertNull(mDatabase.mConnectionPool);
+ assertFalse(db.isOpen());
+ }
+
+ @SmallTest
+ public void testCursorsWithClosedDbConnAfterDisableWriteAheadLogging() {
+ mDatabase.disableWriteAheadLogging();
+ mDatabase.beginTransactionNonExclusive();
+ mDatabase.execSQL("create table test (i int);");
+ mDatabase.execSQL("insert into test values(1);");
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ mDatabase.enableWriteAheadLogging();
+ assertNotNull(mDatabase.mConnectionPool);
+ assertEquals(0, mDatabase.mConnectionPool.getSize());
+ assertEquals(0, mDatabase.mConnectionPool.getFreePoolSize());
+ // get a cursor which should use pooled database connection
+ Cursor c = mDatabase.rawQuery("select * from test", null);
+ assertEquals(1, c.getCount());
+ assertEquals(1, mDatabase.mConnectionPool.getSize());
+ assertEquals(1, mDatabase.mConnectionPool.getFreePoolSize());
+ SQLiteDatabase db = mDatabase.mConnectionPool.getConnectionList().get(0);
+ assertTrue(mDatabase.mConnectionPool.isDatabaseObjFree(db));
+ // disable WAL - which should close connection pool and all pooled connections
+ mDatabase.disableWriteAheadLogging();
+ assertNull(mDatabase.mConnectionPool);
+ assertFalse(db.isOpen());
+ // cursor data should still be accessible because it is fetching data from CursorWindow
+ c.moveToNext();
+ assertEquals(1, c.getInt(0));
+ c.requery();
+ assertEquals(1, c.getCount());
+ c.moveToNext();
+ assertEquals(1, c.getInt(0));
+ c.close();
+ }
+
+ @SmallTest
public void testSetConnectionPoolSize() {
mDatabase.enableWriteAheadLogging();
// can't set pool size to zero
@@ -279,11 +329,11 @@
}
}
- private static class ClassToTestSqlCompilationAndCaching extends SQLiteProgram {
+ public static class ClassToTestSqlCompilationAndCaching extends SQLiteProgram {
private ClassToTestSqlCompilationAndCaching(SQLiteDatabase db, String sql) {
super(db, sql);
}
- private static ClassToTestSqlCompilationAndCaching create(SQLiteDatabase db, String sql) {
+ public static ClassToTestSqlCompilationAndCaching create(SQLiteDatabase db, String sql) {
db.lock();
try {
return new ClassToTestSqlCompilationAndCaching(db, sql);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3d63aa6..6349cb3 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -931,15 +931,14 @@
}
/**
- * Temporary API to expose layer drawing. This draws a shadow layer below
- * the main layer, with the specified offset and color, and blur radius.
- * If radius is 0, then the shadow layer is removed.
+ * This draws a shadow layer below the main layer, with the specified
+ * offset and color, and blur radius. If radius is 0, then the shadow
+ * layer is removed.
*/
- public native void setShadowLayer(float radius, float dx, float dy,
- int color);
+ public native void setShadowLayer(float radius, float dx, float dy, int color);
/**
- * Temporary API to clear the shadow layer.
+ * Clear the shadow layer.
*/
public void clearShadowLayer() {
setShadowLayer(0, 0, 0, 0);
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
index 3e05076..9c8afc5 100644
--- a/include/gui/ISensorServer.h
+++ b/include/gui/ISensorServer.h
@@ -36,7 +36,7 @@
public:
DECLARE_META_INTERFACE(SensorServer);
- virtual Vector<Sensor> getSensorList()= 0;
+ virtual Vector<Sensor> getSensorList() = 0;
virtual sp<ISensorEventConnection> createSensorEventConnection() = 0;
};
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index 86a16f1..e696d63 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -51,7 +51,8 @@
TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY
};
- Sensor();
+ Sensor();
+ Sensor(struct sensor_t const* hwSensor);
virtual ~Sensor();
const String8& getName() const;
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
index d8d8128..ad36dac 100644
--- a/include/gui/SensorEventQueue.h
+++ b/include/gui/SensorEventQueue.h
@@ -42,6 +42,7 @@
class ISensorEventConnection;
class Sensor;
+class PollLoop;
// ----------------------------------------------------------------------------
@@ -56,13 +57,23 @@
ssize_t write(ASensorEvent const* events, size_t numEvents);
ssize_t read(ASensorEvent* events, size_t numEvents);
+ status_t waitForEvent() const;
+ status_t wake() const;
+
status_t enableSensor(Sensor const* sensor) const;
status_t disableSensor(Sensor const* sensor) const;
status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
+ // these are here only to support SensorManager.java
+ status_t enableSensor(int32_t handle, int32_t ms) const;
+ status_t disableSensor(int32_t handle) const;
+
private:
+ sp<PollLoop> getPollLoop() const;
sp<ISensorEventConnection> mSensorEventConnection;
sp<SensorChannel> mSensorChannel;
+ mutable Mutex mLock;
+ mutable sp<PollLoop> mPollLoop;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
index 0d65334..e1b1a7be 100644
--- a/include/gui/SensorManager.h
+++ b/include/gui/SensorManager.h
@@ -47,13 +47,13 @@
SensorManager();
~SensorManager();
- ssize_t getSensorList(Sensor**) const;
- Sensor* getDefaultSensor(int type);
+ ssize_t getSensorList(Sensor const* const** list) const;
+ Sensor const* getDefaultSensor(int type);
sp<SensorEventQueue> createEventQueue();
private:
sp<ISensorServer> mSensorServer;
- Sensor* mSensorList;
+ Sensor const** mSensorList;
Vector<Sensor> mSensors;
};
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
index 3e9d456..a5083fe 100644
--- a/libs/gui/ISensorEventConnection.cpp
+++ b/libs/gui/ISensorEventConnection.cpp
@@ -47,6 +47,7 @@
virtual sp<SensorChannel> getSensorChannel() const
{
Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
return new SensorChannel(reply);
}
@@ -54,6 +55,7 @@
virtual status_t enableDisable(int handle, bool enabled)
{
Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
data.writeInt32(handle);
data.writeInt32(enabled);
remote()->transact(ENABLE_DISABLE, data, &reply);
@@ -63,6 +65,7 @@
virtual status_t setEventRate(int handle, nsecs_t ns)
{
Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
data.writeInt32(handle);
data.writeInt64(ns);
remote()->transact(SET_EVENT_RATE, data, &reply);
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
index c6177bc..7111092 100644
--- a/libs/gui/ISensorServer.cpp
+++ b/libs/gui/ISensorServer.cpp
@@ -48,6 +48,7 @@
virtual Vector<Sensor> getSensorList()
{
Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
remote()->transact(GET_SENSOR_LIST, data, &reply);
Sensor s;
Vector<Sensor> v;
@@ -63,6 +64,7 @@
virtual sp<ISensorEventConnection> createSensorEventConnection()
{
Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
}
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index 1fdd285..48e1cb7 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -36,6 +36,18 @@
{
}
+Sensor::Sensor(struct sensor_t const* hwSensor)
+{
+ mName = hwSensor->name;
+ mVendor = hwSensor->vendor;
+ mHandle = hwSensor->handle;
+ mType = hwSensor->type;
+ mMinValue = 0; // FIXME: minValue
+ mMaxValue = hwSensor->maxRange; // FIXME: maxValue
+ mResolution = hwSensor->resolution;
+ mPower = hwSensor->power;
+}
+
Sensor::~Sensor()
{
}
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index f922ac4..4b46842 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#define LOG_TAG "Sensors"
+
#include <stdint.h>
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <utils/PollLoop.h>
#include <gui/Sensor.h>
#include <gui/SensorChannel.h>
@@ -68,7 +72,7 @@
ssize_t size = mSensorChannel->read(events, numEvents*sizeof(events[0]));
if (size >= 0) {
if (size % sizeof(events[0])) {
- // partial write!!! should never happen.
+ // partial read!!! should never happen.
return -EINVAL;
}
// returns number of events read
@@ -77,18 +81,52 @@
return size;
}
-status_t SensorEventQueue::enableSensor(Sensor const* sensor) const
+sp<PollLoop> SensorEventQueue::getPollLoop() const
{
+ Mutex::Autolock _l(mLock);
+ if (mPollLoop == 0) {
+ mPollLoop = new PollLoop(true);
+ mPollLoop->setCallback(getFd(), POLLIN, NULL, NULL);
+ }
+ return mPollLoop;
+}
+
+status_t SensorEventQueue::waitForEvent() const
+{
+ const int fd = getFd();
+ sp<PollLoop> pollLoop(getPollLoop());
+ int32_t result = pollLoop->pollOnce(-1, NULL, NULL);
+ return (result == fd) ? NO_ERROR : -1;
+}
+
+status_t SensorEventQueue::wake() const
+{
+ sp<PollLoop> pollLoop(getPollLoop());
+ pollLoop->wake();
+ return NO_ERROR;
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
return mSensorEventConnection->enableDisable(sensor->getHandle(), true);
}
-status_t SensorEventQueue::disableSensor(Sensor const* sensor) const
-{
+status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
return mSensorEventConnection->enableDisable(sensor->getHandle(), false);
}
-status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const
-{
+status_t SensorEventQueue::enableSensor(int32_t handle, int32_t ms) const {
+ status_t err = mSensorEventConnection->enableDisable(handle, true);
+ if (err == NO_ERROR) {
+ mSensorEventConnection->setEventRate(handle, ms2ns(ms));
+ }
+ return err;
+}
+
+status_t SensorEventQueue::disableSensor(int32_t handle) const {
+ return mSensorEventConnection->enableDisable(handle, false);
+}
+
+status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
}
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
index cd89285..d719efb 100644
--- a/libs/gui/SensorManager.cpp
+++ b/libs/gui/SensorManager.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "Sensors"
+
#include <stdint.h>
#include <sys/types.h>
@@ -21,6 +23,8 @@
#include <utils/RefBase.h>
#include <utils/Singleton.h>
+#include <binder/IServiceManager.h>
+
#include <gui/ISensorServer.h>
#include <gui/ISensorEventConnection.h>
#include <gui/Sensor.h>
@@ -36,25 +40,40 @@
SensorManager::SensorManager()
: mSensorList(0)
{
+ const String16 name("sensorservice");
+ while (getService(name, &mSensorServer) != NO_ERROR) {
+ usleep(250000);
+ }
+
mSensors = mSensorServer->getSensorList();
- // TODO: needs implementation
+ size_t count = mSensors.size();
+ mSensorList = (Sensor const**)malloc(count * sizeof(Sensor*));
+ for (size_t i=0 ; i<count ; i++) {
+ mSensorList[i] = mSensors.array() + i;
+ }
}
SensorManager::~SensorManager()
{
- // TODO: needs implementation
+ free(mSensorList);
}
-ssize_t SensorManager::getSensorList(Sensor** list) const
+ssize_t SensorManager::getSensorList(Sensor const* const** list) const
{
*list = mSensorList;
return mSensors.size();
}
-Sensor* SensorManager::getDefaultSensor(int type)
+Sensor const* SensorManager::getDefaultSensor(int type)
{
- // TODO: needs implementation
- return mSensorList;
+ // For now we just return the first sensor of that type we find.
+ // in the future it will make sense to let the SensorService make
+ // that decision.
+ for (size_t i=0 ; i<mSensors.size() ; i++) {
+ if (mSensorList[i]->getType() == type)
+ return mSensorList[i];
+ }
+ return NULL;
}
sp<SensorEventQueue> SensorManager::createEventQueue()
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1bdb9d3..172952a 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ FontRenderer.cpp \
GradientCache.cpp \
LayerCache.cpp \
Matrix.cpp \
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
new file mode 100644
index 0000000..8557b87
--- /dev/null
+++ b/libs/hwui/FontRenderer.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "FontRenderer.h"
+
+#include <SkUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
+ mState(state), mFontId(fontId), mFontSize(fontSize) {
+}
+
+
+Font::~Font() {
+ for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
+ if (mState->mActiveFonts[ct] == this) {
+ mState->mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
+ delete glyph;
+ }
+}
+
+void Font::invalidateTextureCache() {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ mCachedGlyphs.valueAt(i)->mIsValid = false;
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
+ FontRenderer *state = mState;
+
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+ state->appendMeshQuad(nPenX, nPenY, 0, glyph->mBitmapMinU, glyph->mBitmapMaxV,
+ nPenX + (int) glyph->mBitmapWidth, nPenY, 0, glyph->mBitmapMaxU, glyph->mBitmapMaxV,
+ nPenX + (int) glyph->mBitmapWidth, nPenY - (int) glyph->mBitmapHeight,
+ 0, glyph->mBitmapMaxU, glyph->mBitmapMinV, nPenX, nPenY - (int) glyph->mBitmapHeight,
+ 0, glyph->mBitmapMinU, glyph->mBitmapMinV);
+}
+
+void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, int numGlyphs,
+ int x, int y) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ int penX = x, penY = y;
+ int glyphsLeft = 1;
+ if (numGlyphs > 0) {
+ glyphsLeft = numGlyphs;
+ }
+
+ //size_t index = start;
+ //size_t nextIndex = 0;
+
+ text += start;
+
+ while (glyphsLeft > 0) {
+ //int32_t utfChar = utf32_at(text, len, index, &nextIndex);
+ int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
+
+ // Reached the end of the string or encountered
+ if (utfChar < 0) {
+ break;
+ }
+
+ // Move to the next character in the array
+ //index = nextIndex;
+
+ CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor(utfChar);
+
+ if (cachedGlyph == NULL) {
+ cachedGlyph = cacheGlyph(paint, utfChar);
+ }
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph);
+ }
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, penY);
+ }
+
+ // TODO: Check how to do this conversion
+ penX += SkFixedRound(cachedGlyph->mAdvanceX);
+
+ // If we were given a specific number of glyphs, decrement
+ if (numGlyphs > 0) {
+ glyphsLeft--;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Let the font state figure out where to put the bitmap
+ FontRenderer *state = mState;
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ glyph->mIsValid = state->cacheBitmap(skiaGlyph, &startX, &startY);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = state->getCacheWidth();
+ uint32_t cacheHeight = state->getCacheHeight();
+
+ glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
+ glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
+
+ state->mUploadTexture = true;
+}
+
+Font::CachedGlyphInfo *Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
+ CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font *ithFont = activeFonts[i];
+ if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) {
+ return ithFont;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontRenderer
+///////////////////////////////////////////////////////////////////////////////
+
+FontRenderer::FontRenderer() {
+ mInitialized = false;
+ mMaxNumberOfQuads = 1024;
+ mCurrentQuadIndex = 0;
+
+ mIndexBufferID = 0;
+
+ mCacheWidth = 1024;
+ mCacheHeight = 256;
+}
+
+FontRenderer::~FontRenderer() {
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ delete mCacheLines[i];
+ }
+ mCacheLines.clear();
+
+ delete mTextTexture;
+
+ Vector<Font*> fontsToDereference = mActiveFonts;
+ for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
+ delete fontsToDereference[i];
+ }
+}
+
+void FontRenderer::flushAllAndInvalidate() {
+ if (mCurrentQuadIndex != 0) {
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+ for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
+ mActiveFonts[i]->invalidateTextureCache();
+ }
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ mCacheLines[i]->mCurrentCol = 0;
+ }
+}
+
+bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+ // If the glyph is too tall, don't cache it
+ if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+ LOGE("Font size to large to fit in cache. width, height = %i, %i",
+ (int) glyph.fWidth, (int) glyph.fHeight);
+ return false;
+ }
+
+ // Now copy the bitmap into the cache texture
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ bool bitmapFit = false;
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+ if (bitmapFit) {
+ break;
+ }
+ }
+
+ // If the new glyph didn't fit, flush the state so far and invalidate everything
+ if (!bitmapFit) {
+ flushAllAndInvalidate();
+
+ // Try to fit it again
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+ if (bitmapFit) {
+ break;
+ }
+ }
+
+ // if we still don't fit, something is wrong and we shouldn't draw
+ if (!bitmapFit) {
+ LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
+ (int) glyph.fWidth, (int) glyph.fHeight);
+ return false;
+ }
+ }
+
+ *retOriginX = startX;
+ *retOriginY = startY;
+
+ uint32_t endX = startX + glyph.fWidth;
+ uint32_t endY = startY + glyph.fHeight;
+
+ uint32_t cacheWidth = mCacheWidth;
+
+ unsigned char *cacheBuffer = mTextTexture;
+ unsigned char *bitmapBuffer = (unsigned char*) glyph.fImage;
+ unsigned int stride = glyph.rowBytes();
+
+ uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+ for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
+ unsigned char tempCol = bitmapBuffer[bY * stride + bX];
+ cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
+ }
+ }
+
+ return true;
+}
+
+void FontRenderer::initTextTexture() {
+ mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
+ mUploadTexture = false;
+
+ glGenTextures(1, &mTextureId);
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ // Split up our cache texture into lines of certain widths
+ int nextLine = 0;
+ mCacheLines.push(new CacheTextureLine(16, mCacheWidth, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, nextLine, 0));
+}
+
+// Avoid having to reallocate memory and render quad by quad
+void FontRenderer::initVertexArrayBuffers() {
+ uint32_t numIndicies = mMaxNumberOfQuads * 6;
+ uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
+ uint16_t *indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
+
+ // Four verts, two triangles , six indices per quad
+ for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
+ int i6 = i * 6;
+ int i4 = i * 4;
+
+ indexBufferData[i6 + 0] = i4 + 0;
+ indexBufferData[i6 + 1] = i4 + 1;
+ indexBufferData[i6 + 2] = i4 + 2;
+
+ indexBufferData[i6 + 3] = i4 + 0;
+ indexBufferData[i6 + 4] = i4 + 2;
+ indexBufferData[i6 + 5] = i4 + 3;
+ }
+
+ glGenBuffers(1, &mIndexBufferID);
+ glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
+ glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ free(indexBufferData);
+
+ uint32_t coordSize = 3;
+ uint32_t uvSize = 2;
+ uint32_t vertsPerQuad = 4;
+ uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize *
+ uvSize * sizeof(float);
+ mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes);
+}
+
+// We don't want to allocate anything unless we actually draw text
+void FontRenderer::checkInit() {
+ if (mInitialized) {
+ return;
+ }
+
+ initTextTexture();
+ initVertexArrayBuffers();
+
+ mInitialized = true;
+}
+
+void FontRenderer::issueDrawCommand() {
+ if (mUploadTexture) {
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA,
+ GL_UNSIGNED_BYTE, mTextTexture);
+ mUploadTexture = false;
+ }
+
+ float *vtx = mTextMeshPtr;
+ float *tex = vtx + 3;
+
+ // position is slot 0
+ uint32_t slot = 0;
+ glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
+
+ // texture0 is slot 1
+ slot = 1;
+ glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+ glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
+}
+
+void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
+ float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+ float x4, float y4, float z4, float u4, float v4) {
+ const uint32_t vertsPerQuad = 4;
+ const uint32_t floatsPerVert = 5;
+ float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+
+ // TODO: Cull things that are off the screen
+ // float width = (float)mRSC->getWidth();
+ // float height = (float)mRSC->getHeight();
+ //
+ // if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
+ // return;
+ // }
+
+ (*currentPos++) = x1;
+ (*currentPos++) = y1;
+ (*currentPos++) = z1;
+ (*currentPos++) = u1;
+ (*currentPos++) = v1;
+
+ (*currentPos++) = x2;
+ (*currentPos++) = y2;
+ (*currentPos++) = z2;
+ (*currentPos++) = u2;
+ (*currentPos++) = v2;
+
+ (*currentPos++) = x3;
+ (*currentPos++) = y3;
+ (*currentPos++) = z3;
+ (*currentPos++) = u3;
+ (*currentPos++) = v3;
+
+ (*currentPos++) = x4;
+ (*currentPos++) = y4;
+ (*currentPos++) = z4;
+ (*currentPos++) = u4;
+ (*currentPos++) = v4;
+
+ mCurrentQuadIndex++;
+
+ if (mCurrentQuadIndex == mMaxNumberOfQuads) {
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+}
+
+void FontRenderer::setFont(uint32_t fontId, float fontSize) {
+ mCurrentFont = Font::create(this, fontId, fontSize);
+}
+
+void FontRenderer::renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex,
+ int numGlyphs, int x, int y) {
+ checkInit();
+
+ // Render code here
+ Font *currentFont = mCurrentFont;
+ if (!currentFont) {
+ LOGE("Unable to initialize any fonts");
+ return;
+ }
+
+ currentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y);
+
+ if (mCurrentQuadIndex != 0) {
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+}
+
+void FontRenderer::renderText(SkPaint* paint, const char *text, int x, int y) {
+ size_t textLen = strlen(text);
+ renderText(paint, text, textLen, 0, -1, x, y);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
new file mode 100644
index 0000000..c18327a
--- /dev/null
+++ b/libs/hwui/FontRenderer.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_FONT_RENDERER_H
+#define ANDROID_UI_FONT_RENDERER_H
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace uirenderer {
+
+class FontRenderer;
+
+class Font {
+public:
+ ~Font();
+
+ // Pointer to the utf data, length of data, where to start, number of glyphs ot read
+ // (each glyph may be longer than a char because we are dealing with utf data)
+ // Last two variables are the initial pen position
+ void renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start,
+ int numGlyphs, int x, int y);
+
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize);
+
+protected:
+
+ friend class FontRenderer;
+
+ void invalidateTextureCache();
+ struct CachedGlyphInfo {
+ // Has the cache been invalidated?
+ bool mIsValid;
+ // Location of the cached glyph in the bitmap
+ // in case we need to resize the texture
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ // Also cache texture coords for the quad
+ float mBitmapMinU;
+ float mBitmapMinV;
+ float mBitmapMaxU;
+ float mBitmapMaxV;
+ // Minimize how much we call freetype
+ uint32_t mGlyphIndex;
+ uint32_t mAdvanceX;
+ uint32_t mAdvanceY;
+ // Values below contain a glyph's origin in the bitmap
+ uint32_t mBitmapLeft;
+ uint32_t mBitmapTop;
+ };
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize);
+
+ DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ CachedGlyphInfo *cacheGlyph(SkPaint* paint, int32_t glyph);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph);
+ void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
+};
+
+class FontRenderer {
+public:
+ FontRenderer();
+ ~FontRenderer();
+
+ void init();
+ void deinit();
+
+ void setFont(uint32_t fontId, float fontSize);
+ void renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex,
+ int numGlyphs, int x, int y);
+ void renderText(SkPaint* paint, const char *text, int x, int y);
+
+ GLuint getTexture() {
+ checkInit();
+ return mTextureId;
+ }
+
+protected:
+ friend class Font;
+
+ struct CacheTextureLine {
+ uint16_t mMaxHeight;
+ uint16_t mMaxWidth;
+ uint32_t mCurrentRow;
+ uint32_t mCurrentCol;
+
+ CacheTextureLine(uint16_t maxHeight, uint16_t maxWidth, uint32_t currentRow,
+ uint32_t currentCol):
+ mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow),
+ mCurrentCol(currentCol) {
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+ if (glyph.fHeight > mMaxHeight) {
+ return false;
+ }
+
+ if (mCurrentCol + glyph.fWidth < mMaxWidth) {
+ *retOriginX = mCurrentCol;
+ *retOriginY = mCurrentRow;
+ mCurrentCol += glyph.fWidth;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ uint32_t getCacheWidth() const {
+ return mCacheWidth;
+ }
+
+ uint32_t getCacheHeight() const {
+ return mCacheHeight;
+ }
+
+ void initTextTexture();
+
+ bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
+
+ void flushAllAndInvalidate();
+ void initVertexArrayBuffers();
+
+ void checkInit();
+
+ void issueDrawCommand();
+
+ void appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, float y2,
+ float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+ float x4, float y4, float z4, float u4, float v4);
+
+ uint32_t mCacheWidth;
+ uint32_t mCacheHeight;
+
+ Font* mCurrentFont;
+
+ Vector<CacheTextureLine*> mCacheLines;
+
+ Vector<Font*> mActiveFonts;
+
+ // Texture to cache glyph bitmaps
+ unsigned char* mTextTexture;
+ GLuint mTextureId;
+ bool mUploadTexture;
+
+ // Pointer to vertex data to speed up frame to frame work
+ float *mTextMeshPtr;
+ uint32_t mCurrentQuadIndex;
+ uint32_t mMaxNumberOfQuads;
+
+ uint32_t mIndexBufferID;
+
+ bool mInitialized;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_FONT_RENDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1fa76d2..8f04d92 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <SkCanvas.h>
+#include <SkTypeface.h>
#include <cutils/properties.h>
#include <utils/Log.h>
@@ -134,6 +135,7 @@
mDrawColorProgram = new DrawColorProgram;
mDrawTextureProgram = new DrawTextureProgram;
+ mDrawTextProgram = new DrawTextProgram;
mDrawLinearGradientProgram = new DrawLinearGradientProgram;
mCurrentProgram = mDrawTextureProgram;
@@ -527,6 +529,39 @@
drawColorRect(left, top, right, bottom, color, mode);
}
+void OpenGLRenderer::drawText(const char* text, int count, float x, float y, SkPaint* paint) {
+ // TODO: Support paint's text alignments, proper clipping
+ if (quickReject(x, y, x + 1, y +1)) {
+ return;
+ }
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ uint32_t color = paint->getColor();
+ const GLfloat a = alpha / 255.0f;
+ const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+ const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
+ const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
+
+ mModelView.loadIdentity();
+
+ useProgram(mDrawTextProgram);
+ mDrawTextProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+
+ chooseBlending(true, mode);
+ bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+ // Always premultiplied
+ glUniform4f(mDrawTextProgram->color, r, g, b, a);
+
+ mFontRenderer.setFont(SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
+ mFontRenderer.renderText(paint, text, count, 0, count, x, y);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Shaders
///////////////////////////////////////////////////////////////////////////////
@@ -687,6 +722,7 @@
float u2 = right - left;
float v2 = bottom - top;
+ // TODO: If the texture is not pow, use a shader to support repeat/mirror
if (mShaderMatrix) {
SkMatrix inverse;
mShaderMatrix->invert(&inverse);
@@ -742,7 +778,6 @@
bindTexture(texture, mShaderTileX, mShaderTileY);
// Always premultiplied
- //glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 9dc2a43..b82366b 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -39,6 +39,7 @@
#include "GradientCache.h"
#include "PatchCache.h"
#include "Vertex.h"
+#include "FontRenderer.h"
namespace android {
namespace uirenderer {
@@ -108,6 +109,8 @@
float* positions, int count, SkShader::TileMode tileMode,
SkMatrix* matrix, bool hasAlpha);
+ void drawText(const char* text, int count, float x, float y, SkPaint* paint);
+
private:
/**
* Type of Skia shader in use.
@@ -329,6 +332,7 @@
sp<Program> mCurrentProgram;
sp<DrawColorProgram> mDrawColorProgram;
sp<DrawTextureProgram> mDrawTextureProgram;
+ sp<DrawTextProgram> mDrawTextProgram;
sp<DrawLinearGradientProgram> mDrawLinearGradientProgram;
// Used to draw textured quads
@@ -357,6 +361,9 @@
float* mShaderPositions;
int mShaderCount;
+ // Font renderer
+ FontRenderer mFontRenderer;
+
// Various caches
TextureCache mTextureCache;
LayerCache mLayerCache;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 841b6c8..6e60808 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -33,6 +33,8 @@
#include "shaders/drawTexture.vert"
#include "shaders/drawTexture.frag"
+#include "shaders/drawText.frag"
+
#include "shaders/drawLinearGradient.vert"
#include "shaders/drawLinearGradient.frag"
@@ -169,6 +171,12 @@
sampler = addUniform("sampler");
}
+DrawTextureProgram::DrawTextureProgram(const char* vertex, const char* fragment):
+ DrawColorProgram(vertex, fragment) {
+ texCoords = addAttrib("texCoords");
+ sampler = addUniform("sampler");
+}
+
void DrawTextureProgram::use() {
DrawColorProgram::use();
glActiveTexture(GL_TEXTURE0);
@@ -182,6 +190,14 @@
}
///////////////////////////////////////////////////////////////////////////////
+// Draw text
+///////////////////////////////////////////////////////////////////////////////
+
+DrawTextProgram::DrawTextProgram():
+ DrawTextureProgram(gDrawTextureVertexShader, gDrawTextFragmentShader) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Draw linear gradient
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 18a8e92..824aa05 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -167,6 +167,7 @@
class DrawTextureProgram: public DrawColorProgram {
public:
DrawTextureProgram();
+ DrawTextureProgram(const char* vertex, const char* fragment);
/**
* Binds this program to the GL context.
@@ -190,6 +191,11 @@
int texCoords;
};
+class DrawTextProgram: public DrawTextureProgram {
+public:
+ DrawTextProgram();
+};
+
/**
* Program used to draw linear gradients. In addition to everything that the
* DrawColorProgram supports, the following two attributes must be specified:
diff --git a/libs/hwui/shaders/drawText.frag b/libs/hwui/shaders/drawText.frag
new file mode 100644
index 0000000..49532c7
--- /dev/null
+++ b/libs/hwui/shaders/drawText.frag
@@ -0,0 +1,14 @@
+SHADER_SOURCE(gDrawTextFragmentShader,
+
+precision mediump float;
+
+varying vec2 outTexCoords;
+
+uniform vec4 color;
+uniform sampler2D sampler;
+
+void main(void) {
+ gl_FragColor = color * texture2D(sampler, outTexCoords).a;
+}
+
+);
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 7a3907e..e1fc4e7 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -43,17 +43,18 @@
return &SensorManager::getInstance();
}
-int ASensorManager_getSensorList(ASensorManager* manager, ASensor** list)
+int ASensorManager_getSensorList(ASensorManager* manager,
+ ASensorList* list)
{
- Sensor* l;
+ Sensor const* const* l;
int c = static_cast<SensorManager*>(manager)->getSensorList(&l);
if (list) {
- *list = l;
+ *list = reinterpret_cast<ASensorList>(l);
}
return c;
}
-ASensor* ASensorManager_getDefaultSensor(ASensorManager* manager, int type)
+ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type)
{
return static_cast<SensorManager*>(manager)->getDefaultSensor(type);
}
@@ -82,23 +83,23 @@
/*****************************************************************************/
-int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor* sensor)
+int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor)
{
return static_cast<SensorEventQueue*>(queue)->enableSensor(
- static_cast<Sensor*>(sensor));
+ static_cast<Sensor const*>(sensor));
}
-int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor* sensor)
+int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor)
{
return static_cast<SensorEventQueue*>(queue)->disableSensor(
- static_cast<Sensor*>(sensor));
+ static_cast<Sensor const*>(sensor));
}
-int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor* sensor,
+int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor,
int32_t usec)
{
return static_cast<SensorEventQueue*>(queue)->setEventRate(
- static_cast<Sensor*>(sensor), us2ns(usec));
+ static_cast<Sensor const*>(sensor), us2ns(usec));
}
int ASensorEventQueue_hasEvents(ASensorEventQueue* queue)
@@ -128,23 +129,23 @@
/*****************************************************************************/
-const char* ASensor_getName(ASensor* sensor)
+const char* ASensor_getName(ASensor const* sensor)
{
- return static_cast<Sensor*>(sensor)->getName().string();
+ return static_cast<Sensor const*>(sensor)->getName().string();
}
-const char* ASensor_getVendor(ASensor* sensor)
+const char* ASensor_getVendor(ASensor const* sensor)
{
- return static_cast<Sensor*>(sensor)->getVendor().string();
+ return static_cast<Sensor const*>(sensor)->getVendor().string();
}
-int ASensor_getType(ASensor* sensor)
+int ASensor_getType(ASensor const* sensor)
{
- return static_cast<Sensor*>(sensor)->getType();
+ return static_cast<Sensor const*>(sensor)->getType();
}
-float ASensor_getResolution(ASensor* sensor)
+float ASensor_getResolution(ASensor const* sensor)
{
- return static_cast<Sensor*>(sensor)->getResolution();
+ return static_cast<Sensor const*>(sensor)->getResolution();
}
diff --git a/native/include/android/sensor.h b/native/include/android/sensor.h
index 4291d3e..00d95d8 100644
--- a/native/include/android/sensor.h
+++ b/native/include/android/sensor.h
@@ -87,6 +87,7 @@
* A sensor event.
*/
+/* NOTE: Must match hardware/sensors.h */
typedef struct ASensorVector {
union {
float v[3];
@@ -95,23 +96,32 @@
float y;
float z;
};
+ struct {
+ float azimuth;
+ float pitch;
+ float roll;
+ };
};
int8_t status;
uint8_t reserved[3];
} ASensorVector;
+/* NOTE: Must match hardware/sensors.h */
typedef struct ASensorEvent {
- int sensor;
+ int32_t version; /* sizeof(struct ASensorEvent) */
+ int32_t sensor;
+ int32_t type;
int32_t reserved0;
+ int64_t timestamp;
union {
float data[16];
+ ASensorVector vector;
ASensorVector acceleration;
ASensorVector magnetic;
float temperature;
float distance;
float light;
};
- int64_t timestamp;
int32_t reserved1[4];
} ASensorEvent;
@@ -124,6 +134,8 @@
struct ASensor;
typedef struct ASensor ASensor;
+typedef ASensor const* ASensorRef;
+typedef ASensorRef const* ASensorList;
/*****************************************************************************/
@@ -141,13 +153,13 @@
/*
* Returns the list of available sensors.
*/
-int ASensorManager_getSensorList(ASensorManager* manager, ASensor** list);
+int ASensorManager_getSensorList(ASensorManager* manager, ASensorList* list);
/*
* Returns the default sensor for the given type, or NULL if no sensor
* of that type exist.
*/
-ASensor* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
+ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
/*
* Creates a new sensor event queue and associate it with a looper.
@@ -166,12 +178,12 @@
/*
* Enable the selected sensor. Returns a negative error code on failure.
*/
-int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor* sensor);
+int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor);
/*
* Disable the selected sensor. Returns a negative error code on failure.
*/
-int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor* sensor);
+int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor);
/*
* Sets the delivery rate of events in microseconds for the given sensor.
@@ -179,7 +191,7 @@
* rate.
* Returns a negative error code on failure.
*/
-int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor* sensor, int32_t usec);
+int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec);
/*
* Returns true if there are one or more events available in the
@@ -210,22 +222,22 @@
/*
* Returns this sensor's name (non localized)
*/
-const char* ASensor_getName(ASensor* sensor);
+const char* ASensor_getName(ASensor const* sensor);
/*
* Returns this sensor's vendor's name (non localized)
*/
-const char* ASensor_getVendor(ASensor* sensor);
+const char* ASensor_getVendor(ASensor const* sensor);
/*
* Return this sensor's type
*/
-int ASensor_getType(ASensor* sensor);
+int ASensor_getType(ASensor const* sensor);
/*
* Returns this sensors's resolution
*/
-float ASensor_getResolution(ASensor* sensor);
+float ASensor_getResolution(ASensor const* sensor);
#ifdef __cplusplus
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
deleted file mode 100644
index 9f5718f..0000000
--- a/services/java/com/android/server/SensorService.java
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.hardware.ISensorService;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.util.Config;
-import android.util.Slog;
-import android.util.PrintWriterPrinter;
-import android.util.Printer;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.server.am.BatteryStatsService;
-
-
-/**
- * Class that manages the device's sensors. It register clients and activate
- * the needed sensors. The sensor events themselves are not broadcasted from
- * this service, instead, a file descriptor is provided to each client they
- * can read events from.
- */
-
-class SensorService extends ISensorService.Stub {
- static final String TAG = SensorService.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
- private static final int SENSOR_DISABLE = -1;
- private int mCurrentDelay = 0;
-
- /**
- * Battery statistics to be updated when sensors are enabled and disabled.
- */
- final IBatteryStats mBatteryStats = BatteryStatsService.getService();
-
- private final class Listener implements IBinder.DeathRecipient {
- final IBinder mToken;
- final int mUid;
-
- int mSensors = 0;
- int mDelay = 0x7FFFFFFF;
-
- Listener(IBinder token, int uid) {
- mToken = token;
- mUid = uid;
- }
-
- void addSensor(int sensor, int delay) {
- mSensors |= (1<<sensor);
- if (delay < mDelay)
- mDelay = delay;
- }
-
- void removeSensor(int sensor) {
- mSensors &= ~(1<<sensor);
- }
-
- boolean hasSensor(int sensor) {
- return ((mSensors & (1<<sensor)) != 0);
- }
-
- public void binderDied() {
- if (localLOGV) Slog.d(TAG, "sensor listener died");
- synchronized(mListeners) {
- mListeners.remove(this);
- mToken.unlinkToDeath(this, 0);
- // go through the lists of sensors used by the listener that
- // died and deactivate them.
- for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) {
- if (hasSensor(sensor)) {
- removeSensor(sensor);
- deactivateIfUnusedLocked(sensor);
- try {
- mBatteryStats.noteStopSensor(mUid, sensor);
- } catch (RemoteException e) {
- // oops. not a big deal.
- }
- }
- }
- if (mListeners.size() == 0) {
- _sensors_control_wake();
- _sensors_control_close();
- } else {
- // TODO: we should recalculate the delay, since removing
- // a listener may increase the overall rate.
- }
- mListeners.notify();
- }
- }
- }
-
- @SuppressWarnings("unused")
- public SensorService(Context context) {
- if (localLOGV) Slog.d(TAG, "SensorService startup");
- _sensors_control_init();
- }
-
- public Bundle getDataChannel() throws RemoteException {
- // synchronize so we do not require sensor HAL to be thread-safe.
- synchronized(mListeners) {
- return _sensors_control_open();
- }
- }
-
- public boolean enableSensor(IBinder binder, String name, int sensor, int enable)
- throws RemoteException {
-
- if (localLOGV) Slog.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
-
- if (binder == null) {
- Slog.e(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")");
- return false;
- }
-
- if (enable < 0 && (enable != SENSOR_DISABLE)) {
- Slog.e(TAG, "invalid enable parameter (enable=" + enable +
- ", sensor=" + name + ", id=" + sensor + ")");
- return false;
- }
-
- boolean res;
- int uid = Binder.getCallingUid();
- synchronized(mListeners) {
- res = enableSensorInternalLocked(binder, uid, name, sensor, enable);
- if (res == true) {
- // Inform battery statistics service of status change
- long identity = Binder.clearCallingIdentity();
- if (enable == SENSOR_DISABLE) {
- mBatteryStats.noteStopSensor(uid, sensor);
- } else {
- mBatteryStats.noteStartSensor(uid, sensor);
- }
- Binder.restoreCallingIdentity(identity);
- }
- }
- return res;
- }
-
- private boolean enableSensorInternalLocked(IBinder binder, int uid,
- String name, int sensor, int enable) throws RemoteException {
-
- // check if we have this listener
- Listener l = null;
- for (Listener listener : mListeners) {
- if (binder == listener.mToken) {
- l = listener;
- break;
- }
- }
-
- if (enable != SENSOR_DISABLE) {
- // Activate the requested sensor
- if (_sensors_control_activate(sensor, true) == false) {
- Slog.w(TAG, "could not enable sensor " + sensor);
- return false;
- }
-
- if (l == null) {
- /*
- * we don't have a listener for this binder yet, so
- * create a new one and add it to the list.
- */
- l = new Listener(binder, uid);
- binder.linkToDeath(l, 0);
- mListeners.add(l);
- mListeners.notify();
- }
-
- // take note that this sensor is now used by this client
- l.addSensor(sensor, enable);
-
- } else {
-
- if (l == null) {
- /*
- * This client isn't in the list, this usually happens
- * when enabling the sensor failed, but the client
- * didn't handle the error and later tries to shut that
- * sensor off.
- */
- Slog.w(TAG, "listener with binder " + binder +
- ", doesn't exist (sensor=" + name +
- ", id=" + sensor + ")");
- return false;
- }
-
- // remove this sensor from this client
- l.removeSensor(sensor);
-
- // see if we need to deactivate this sensors=
- deactivateIfUnusedLocked(sensor);
-
- // if the listener doesn't have any more sensors active
- // we can get rid of it
- if (l.mSensors == 0) {
- // we won't need this death notification anymore
- binder.unlinkToDeath(l, 0);
- // remove the listener from the list
- mListeners.remove(l);
- // and if the list is empty, turn off the whole sensor h/w
- if (mListeners.size() == 0) {
- _sensors_control_wake();
- _sensors_control_close();
- }
- mListeners.notify();
- }
- }
-
- // calculate and set the new delay
- int minDelay = 0x7FFFFFFF;
- for (Listener listener : mListeners) {
- if (listener.mDelay < minDelay)
- minDelay = listener.mDelay;
- }
- if (minDelay != 0x7FFFFFFF) {
- mCurrentDelay = minDelay;
- _sensors_control_set_delay(minDelay);
- }
-
- return true;
- }
-
- private void deactivateIfUnusedLocked(int sensor) {
- int size = mListeners.size();
- for (int i=0 ; i<size ; i++) {
- if (mListeners.get(i).hasSensor(sensor)) {
- // this sensor is still in use, don't turn it off
- return;
- }
- }
- if (_sensors_control_activate(sensor, false) == false) {
- Slog.w(TAG, "could not disable sensor " + sensor);
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- synchronized (mListeners) {
- Printer pr = new PrintWriterPrinter(pw);
- int c = 0;
- pr.println(mListeners.size() + " listener(s), delay=" + mCurrentDelay + " ms");
- for (Listener l : mListeners) {
- pr.println("listener[" + c + "] " +
- "sensors=0x" + Integer.toString(l.mSensors, 16) +
- ", uid=" + l.mUid +
- ", delay=" +
- l.mDelay + " ms");
- c++;
- }
- }
- }
-
- private ArrayList<Listener> mListeners = new ArrayList<Listener>();
-
- private static native int _sensors_control_init();
- private static native Bundle _sensors_control_open();
- private static native int _sensors_control_close();
- private static native boolean _sensors_control_activate(int sensor, boolean activate);
- private static native int _sensors_control_set_delay(int ms);
- private static native int _sensors_control_wake();
-}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dfb8f12..3586d21 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -162,10 +162,6 @@
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
- // Sensor Service is needed by Window Manager, so this goes first
- Slog.i(TAG, "Sensor Service");
- ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
-
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 0cf36b3..cdc0a6f 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -7,7 +7,6 @@
com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
com_android_server_PowerManagerService.cpp \
- com_android_server_SensorService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_VibratorService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
diff --git a/services/jni/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp
deleted file mode 100644
index 77db6da..0000000
--- a/services/jni/com_android_server_SensorService.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SensorService"
-
-#include "utils/Log.h"
-
-#include <hardware/sensors.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-namespace android {
-
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
-static struct parcel_file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
-} gParcelFileDescriptorOffsets;
-
-static struct bundle_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jmethodID mPutIntArray;
- jmethodID mPutParcelableArray;
-} gBundleOffsets;
-
-/*
- * The method below are not thread-safe and not intended to be
- */
-
-static sensors_control_device_t* sSensorDevice = 0;
-
-static jint
-android_init(JNIEnv *env, jclass clazz)
-{
- sensors_module_t* module;
- if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
- if (sensors_control_open(&module->common, &sSensorDevice) == 0) {
- const struct sensor_t* list;
- int count = module->get_sensors_list(module, &list);
- return count;
- }
- }
- return 0;
-}
-
-static jobject
-android_open(JNIEnv *env, jclass clazz)
-{
- native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice);
- if (!handle) {
- return NULL;
- }
-
- // new Bundle()
- jobject bundle = env->NewObject(
- gBundleOffsets.mClass,
- gBundleOffsets.mConstructor);
-
- if (handle->numFds > 0) {
- jobjectArray fdArray = env->NewObjectArray(handle->numFds,
- gParcelFileDescriptorOffsets.mClass, NULL);
- for (int i = 0; i < handle->numFds; i++) {
- // new FileDescriptor()
- jobject fd = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]);
- // new ParcelFileDescriptor()
- jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass,
- gParcelFileDescriptorOffsets.mConstructor, fd);
- env->SetObjectArrayElement(fdArray, i, pfd);
- }
- // bundle.putParcelableArray("fds", fdArray);
- env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray,
- env->NewStringUTF("fds"), fdArray);
- }
-
- if (handle->numInts > 0) {
- jintArray intArray = env->NewIntArray(handle->numInts);
- env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]);
- // bundle.putIntArray("ints", intArray);
- env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray,
- env->NewStringUTF("ints"), intArray);
- }
-
- // delete the file handle, but don't close any file descriptors
- native_handle_delete(handle);
- return bundle;
-}
-
-static jint
-android_close(JNIEnv *env, jclass clazz)
-{
- if (sSensorDevice->close_data_source)
- return sSensorDevice->close_data_source(sSensorDevice);
- else
- return 0;
-}
-
-static jboolean
-android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate)
-{
- int active = sSensorDevice->activate(sSensorDevice, sensor, activate);
- return (active<0) ? false : true;
-}
-
-static jint
-android_set_delay(JNIEnv *env, jclass clazz, jint ms)
-{
- return sSensorDevice->set_delay(sSensorDevice, ms);
-}
-
-static jint
-android_data_wake(JNIEnv *env, jclass clazz)
-{
- int res = sSensorDevice->wake(sSensorDevice);
- return res;
-}
-
-
-static JNINativeMethod gMethods[] = {
- {"_sensors_control_init", "()I", (void*) android_init },
- {"_sensors_control_open", "()Landroid/os/Bundle;", (void*) android_open },
- {"_sensors_control_close", "()I", (void*) android_close },
- {"_sensors_control_activate", "(IZ)Z", (void*) android_activate },
- {"_sensors_control_wake", "()I", (void*) android_data_wake },
- {"_sensors_control_set_delay","(I)I", (void*) android_set_delay },
-};
-
-int register_android_server_SensorService(JNIEnv *env)
-{
- jclass clazz;
-
- clazz = env->FindClass("java/io/FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass)env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
-
- clazz = env->FindClass("android/os/ParcelFileDescriptor");
- gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
- "(Ljava/io/FileDescriptor;)V");
-
- clazz = env->FindClass("android/os/Bundle");
- gBundleOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gBundleOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gBundleOffsets.mPutIntArray = env->GetMethodID(clazz, "putIntArray", "(Ljava/lang/String;[I)V");
- gBundleOffsets.mPutParcelableArray = env->GetMethodID(clazz, "putParcelableArray",
- "(Ljava/lang/String;[Landroid/os/Parcelable;)V");
-
- return jniRegisterNativeMethods(env, "com/android/server/SensorService",
- gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 1a2d8b6..cd4f0a4 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -9,7 +9,6 @@
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
-int register_android_server_SensorService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -33,7 +32,6 @@
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
- register_android_server_SensorService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
new file mode 100644
index 0000000..75f690f
--- /dev/null
+++ b/services/sensorservice/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ SensorService.cpp
+
+LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
+
+# need "-lrt" on Linux simulator to pick up clock_gettime
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -lpthread
+ endif
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libhardware \
+ libutils \
+ libbinder \
+ libui \
+ libgui
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE:= libsensorservice
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
new file mode 100644
index 0000000..a4f6549
--- /dev/null
+++ b/services/sensorservice/SensorService.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <binder/BinderService.h>
+#include <binder/IServiceManager.h>
+
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+#include <hardware/sensors.h>
+
+#include "SensorService.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+/*
+ * TODO:
+ * - handle per-connection event rate
+ * - filter events per connection
+ * - make sure to keep the last value of each event type so we can quickly
+ * send something to application when they enable a sensor that is already
+ * active (the issue here is that it can take time before a value is
+ * produced by the h/w if the rate is low or if it's a one-shot sensor).
+ * - send sensor info to battery service
+ */
+
+// ---------------------------------------------------------------------------
+
+class BatteryService : public Singleton<BatteryService> {
+ friend class Singleton<BatteryService>;
+ sp<IBinder> mBatteryStatService;
+ BatteryService() {
+ const String16 name("batteryinfo");
+ //getService(name, &mBatteryStatService);
+ }
+public:
+ void enableSensor(int handle) {
+ if (mBatteryStatService != 0) {
+ int uid = IPCThreadState::self()->getCallingUid();
+ //mBatteryStatService->noteStartSensor(uid, handle);
+ }
+ }
+ void disableSensor(int handle) {
+ if (mBatteryStatService != 0) {
+ int uid = IPCThreadState::self()->getCallingUid();
+ //mBatteryStatService->noteStopSensor(uid, handle);
+ }
+ }
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BatteryService)
+
+// ---------------------------------------------------------------------------
+
+// 100 events/s max
+static const nsecs_t MINIMUM_EVENT_PERIOD = ms2ns(10);
+
+SensorService::SensorService()
+ : Thread(false),
+ mSensorDevice(0),
+ mSensorModule(0),
+ mDump("android.permission.DUMP"),
+ mInitCheck(NO_INIT)
+{
+}
+
+void SensorService::onFirstRef()
+{
+ LOGD("nuSensorService starting...");
+
+ status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&mSensorModule);
+
+ LOGE_IF(err, "couldn't load %s module (%s)",
+ SENSORS_HARDWARE_MODULE_ID, strerror(-err));
+
+ if (mSensorModule) {
+ err = sensors_open(&mSensorModule->common, &mSensorDevice);
+
+ LOGE_IF(err, "couldn't open device for module %s (%s)",
+ SENSORS_HARDWARE_MODULE_ID, strerror(-err));
+
+ struct sensor_t const* list;
+ int count = mSensorModule->get_sensors_list(mSensorModule, &list);
+ for (int i=0 ; i<count ; i++) {
+ Sensor sensor(list + i);
+ LOGI("%s", sensor.getName().string());
+ mSensorList.add(sensor);
+ if (mSensorDevice) {
+ mSensorDevice->activate(mSensorDevice, sensor.getHandle(), 0);
+ }
+ }
+
+ if (mSensorDevice) {
+ run("SensorService", PRIORITY_URGENT_DISPLAY);
+ mInitCheck = NO_ERROR;
+ }
+ }
+}
+
+SensorService::~SensorService()
+{
+}
+
+status_t SensorService::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 1024;
+ char buffer[SIZE];
+ String8 result;
+ if (!mDump.checkCalling()) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump SurfaceFlinger from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ } else {
+ Mutex::Autolock _l(mLock);
+ snprintf(buffer, SIZE, "%d connections / %d active\n",
+ mConnections.size(), mActiveConnections.size());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Active sensors:\n");
+ result.append(buffer);
+ for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
+ int handle = mActiveSensors.keyAt(i);
+ snprintf(buffer, SIZE, "%s (handle=%d, connections=%d)\n",
+ getSensorName(handle).string(),
+ handle,
+ mActiveSensors.valueAt(i)->getNumConnections());
+ result.append(buffer);
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+bool SensorService::threadLoop()
+{
+ LOGD("nuSensorService thread starting...");
+
+ sensors_event_t buffer[16];
+ struct sensors_poll_device_t* device = mSensorDevice;
+ ssize_t count;
+
+ do {
+ count = device->poll(device, buffer, sizeof(buffer)/sizeof(*buffer));
+ if (count<0) {
+ LOGE("sensor poll failed (%s)", strerror(-count));
+ break;
+ }
+
+ const SortedVector< wp<SensorEventConnection> > activeConnections(
+ getActiveConnections());
+
+ size_t numConnections = activeConnections.size();
+ if (numConnections) {
+ for (size_t i=0 ; i<numConnections ; i++) {
+ sp<SensorEventConnection> connection(activeConnections[i].promote());
+ if (connection != 0) {
+ connection->sendEvents(buffer, count);
+ }
+ }
+ }
+
+ } while (count >= 0 || Thread::exitPending());
+
+ LOGW("Exiting SensorService::threadLoop!");
+ return false;
+}
+
+SortedVector< wp<SensorService::SensorEventConnection> >
+SensorService::getActiveConnections() const
+{
+ Mutex::Autolock _l(mLock);
+ return mActiveConnections;
+}
+
+String8 SensorService::getSensorName(int handle) const {
+ size_t count = mSensorList.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const Sensor& sensor(mSensorList[i]);
+ if (sensor.getHandle() == handle) {
+ return sensor.getName();
+ }
+ }
+ String8 result("unknown");
+ return result;
+}
+
+Vector<Sensor> SensorService::getSensorList()
+{
+ return mSensorList;
+}
+
+sp<ISensorEventConnection> SensorService::createSensorEventConnection()
+{
+ sp<SensorEventConnection> result(new SensorEventConnection(this));
+ Mutex::Autolock _l(mLock);
+ mConnections.add(result);
+ return result;
+}
+
+void SensorService::cleanupConnection(const wp<SensorEventConnection>& connection)
+{
+ Mutex::Autolock _l(mLock);
+ ssize_t index = mConnections.indexOf(connection);
+ if (index >= 0) {
+
+ size_t size = mActiveSensors.size();
+ for (size_t i=0 ; i<size ; ) {
+ SensorRecord* rec = mActiveSensors.valueAt(i);
+ if (rec && rec->removeConnection(connection)) {
+ mSensorDevice->activate(mSensorDevice, mActiveSensors.keyAt(i), 0);
+ mActiveSensors.removeItemsAt(i, 1);
+ delete rec;
+ size--;
+ } else {
+ i++;
+ }
+ }
+
+ mActiveConnections.remove(connection);
+ mConnections.removeItemsAt(index, 1);
+ }
+}
+
+status_t SensorService::enable(const sp<SensorEventConnection>& connection,
+ int handle)
+{
+ if (mInitCheck != NO_ERROR)
+ return mInitCheck;
+
+ status_t err = NO_ERROR;
+ Mutex::Autolock _l(mLock);
+ SensorRecord* rec = mActiveSensors.valueFor(handle);
+ if (rec == 0) {
+ rec = new SensorRecord(connection);
+ mActiveSensors.add(handle, rec);
+ err = mSensorDevice->activate(mSensorDevice, handle, 1);
+ LOGE_IF(err, "Error activating sensor %d (%s)", handle, strerror(-err));
+ if (err == 0) {
+ BatteryService::getInstance().enableSensor(handle);
+ }
+ } else {
+ err = rec->addConnection(connection);
+ }
+ if (err == NO_ERROR) {
+ // connection now active
+ connection->addSensor(handle);
+ if (mActiveConnections.indexOf(connection) < 0) {
+ mActiveConnections.add(connection);
+ }
+ }
+ return err;
+}
+
+status_t SensorService::disable(const sp<SensorEventConnection>& connection,
+ int handle)
+{
+ if (mInitCheck != NO_ERROR)
+ return mInitCheck;
+
+ status_t err = NO_ERROR;
+ Mutex::Autolock _l(mLock);
+ SensorRecord* rec = mActiveSensors.valueFor(handle);
+ if (rec) {
+ // see if this connection becomes inactive
+ connection->removeSensor(handle);
+ if (connection->hasAnySensor() == false) {
+ mActiveConnections.remove(connection);
+ }
+ // see if this sensor becomes inactive
+ if (rec->removeConnection(connection)) {
+ mActiveSensors.removeItem(handle);
+ delete rec;
+ err = mSensorDevice->activate(mSensorDevice, handle, 0);
+ if (err == 0) {
+ BatteryService::getInstance().disableSensor(handle);
+ }
+ }
+ }
+ return err;
+}
+
+status_t SensorService::setRate(const sp<SensorEventConnection>& connection,
+ int handle, nsecs_t ns)
+{
+ if (mInitCheck != NO_ERROR)
+ return mInitCheck;
+
+ if (ns < 0)
+ return BAD_VALUE;
+
+ if (ns < MINIMUM_EVENT_PERIOD)
+ ns = MINIMUM_EVENT_PERIOD;
+
+ status_t err = NO_ERROR;
+ Mutex::Autolock _l(mLock);
+
+ err = mSensorDevice->setDelay(mSensorDevice, handle, ns);
+
+ // TODO: handle rate per connection
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+
+SensorService::SensorRecord::SensorRecord(
+ const sp<SensorEventConnection>& connection)
+{
+ mConnections.add(connection);
+}
+
+status_t SensorService::SensorRecord::addConnection(
+ const sp<SensorEventConnection>& connection)
+{
+ if (mConnections.indexOf(connection) < 0) {
+ mConnections.add(connection);
+ }
+ return NO_ERROR;
+}
+
+bool SensorService::SensorRecord::removeConnection(
+ const wp<SensorEventConnection>& connection)
+{
+ ssize_t index = mConnections.indexOf(connection);
+ if (index >= 0) {
+ mConnections.removeItemsAt(index, 1);
+ }
+ return mConnections.size() ? false : true;
+}
+
+// ---------------------------------------------------------------------------
+
+SensorService::SensorEventConnection::SensorEventConnection(
+ const sp<SensorService>& service)
+ : mService(service), mChannel(new SensorChannel())
+{
+}
+
+SensorService::SensorEventConnection::~SensorEventConnection()
+{
+ mService->cleanupConnection(this);
+}
+
+void SensorService::SensorEventConnection::onFirstRef()
+{
+}
+
+void SensorService::SensorEventConnection::addSensor(int32_t handle) {
+ if (mSensorList.indexOf(handle) <= 0) {
+ mSensorList.add(handle);
+ }
+}
+
+void SensorService::SensorEventConnection::removeSensor(int32_t handle) {
+ mSensorList.remove(handle);
+}
+
+bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const {
+ return mSensorList.indexOf(handle) >= 0;
+}
+
+bool SensorService::SensorEventConnection::hasAnySensor() const {
+ return mSensorList.size() ? true : false;
+}
+
+status_t SensorService::SensorEventConnection::sendEvents(
+ sensors_event_t const* buffer, size_t count)
+{
+ // TODO: we should only send the events for the sensors this connection
+ // is registered for.
+
+ ssize_t size = mChannel->write(buffer, count*sizeof(sensors_event_t));
+ if (size == -EAGAIN) {
+ // the destination doesn't accept events anymore, it's probably
+ // full. For now, we just drop the events on the floor.
+ LOGW("dropping %d events on the floor", count);
+ return size;
+ }
+
+ LOGE_IF(size<0, "dropping %d events on the floor (%s)",
+ count, strerror(-size));
+
+ return size < 0 ? size : NO_ERROR;
+}
+
+sp<SensorChannel> SensorService::SensorEventConnection::getSensorChannel() const
+{
+ return mChannel;
+}
+
+status_t SensorService::SensorEventConnection::enableDisable(
+ int handle, bool enabled)
+{
+ status_t err;
+ if (enabled) {
+ err = mService->enable(this, handle);
+ } else {
+ err = mService->disable(this, handle);
+ }
+ return err;
+}
+
+status_t SensorService::SensorEventConnection::setEventRate(
+ int handle, nsecs_t ns)
+{
+ return mService->setRate(this, handle, ns);
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
new file mode 100644
index 0000000..8731956
--- /dev/null
+++ b/services/sensorservice/SensorService.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_SERVICE_H
+#define ANDROID_SENSOR_SERVICE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+
+#include <binder/BinderService.h>
+#include <binder/Permission.h>
+
+#include <gui/Sensor.h>
+#include <gui/SensorChannel.h>
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+// ---------------------------------------------------------------------------
+
+struct sensors_poll_device_t;
+struct sensors_module_t;
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class SensorService :
+ public BinderService<SensorService>,
+ public BnSensorServer,
+ protected Thread
+{
+ friend class BinderService<SensorService>;
+
+ SensorService();
+ virtual ~SensorService();
+
+ virtual void onFirstRef();
+
+ // Thread interface
+ virtual bool threadLoop();
+
+ // ISensorServer interface
+ virtual Vector<Sensor> getSensorList();
+ virtual sp<ISensorEventConnection> createSensorEventConnection();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+
+ class SensorEventConnection : public BnSensorEventConnection {
+ virtual sp<SensorChannel> getSensorChannel() const;
+ virtual status_t enableDisable(int handle, bool enabled);
+ virtual status_t setEventRate(int handle, nsecs_t ns);
+ sp<SensorService> const mService;
+ sp<SensorChannel> const mChannel;
+ SortedVector<int32_t> mSensorList;
+ public:
+ SensorEventConnection(const sp<SensorService>& service);
+ virtual ~SensorEventConnection();
+ virtual void onFirstRef();
+ status_t sendEvents(sensors_event_t const* buffer, size_t count);
+ bool hasSensor(int32_t handle) const;
+ bool hasAnySensor() const;
+ void addSensor(int32_t handle);
+ void removeSensor(int32_t handle);
+ };
+
+ class SensorRecord {
+ SortedVector< wp<SensorEventConnection> > mConnections;
+ public:
+ SensorRecord(const sp<SensorEventConnection>& connection);
+ status_t addConnection(const sp<SensorEventConnection>& connection);
+ bool removeConnection(const wp<SensorEventConnection>& connection);
+ size_t getNumConnections() const { return mConnections.size(); }
+ };
+
+ SortedVector< wp<SensorEventConnection> > getActiveConnections() const;
+ String8 getSensorName(int handle) const;
+
+ // constants
+ Vector<Sensor> mSensorList;
+ struct sensors_poll_device_t* mSensorDevice;
+ struct sensors_module_t* mSensorModule;
+ Permission mDump;
+ status_t mInitCheck;
+
+ // protected by mLock
+ mutable Mutex mLock;
+ SortedVector< wp<SensorEventConnection> > mConnections;
+ DefaultKeyedVector<int, SensorRecord*> mActiveSensors;
+ SortedVector< wp<SensorEventConnection> > mActiveConnections;
+
+public:
+ static char const* getServiceName() { return "sensorservice"; }
+
+ void cleanupConnection(const wp<SensorEventConnection>& connection);
+ status_t enable(const sp<SensorEventConnection>& connection, int handle);
+ status_t disable(const sp<SensorEventConnection>& connection, int handle);
+ status_t setRate(const sp<SensorEventConnection>& connection, int handle, nsecs_t ns);
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SENSOR_SERVICE_H
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
new file mode 100644
index 0000000..45296dd
--- /dev/null
+++ b/services/sensorservice/tests/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ sensorservicetest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils libutils libui libgui
+
+LOCAL_MODULE:= test-sensorservice
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
new file mode 100644
index 0000000..e464713
--- /dev/null
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/sensor.h>
+#include <gui/Sensor.h>
+#include <gui/SensorManager.h>
+#include <gui/SensorEventQueue.h>
+#include <utils/PollLoop.h>
+
+using namespace android;
+
+bool receiver(int fd, int events, void* data)
+{
+ sp<SensorEventQueue> q((SensorEventQueue*)data);
+ ssize_t n;
+ ASensorEvent buffer[8];
+ while ((n = q->read(buffer, 8)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ if (buffer[i].type == Sensor::TYPE_ACCELEROMETER) {
+ printf("time=%lld, value=<%5.1f,%5.1f,%5.1f>\n",
+ buffer[i].timestamp,
+ buffer[i].acceleration.x,
+ buffer[i].acceleration.y,
+ buffer[i].acceleration.z);
+ }
+ }
+ }
+ if (n<0 && n != -EAGAIN) {
+ printf("error reading events (%s)\n", strerror(-n));
+ }
+ return true;
+}
+
+
+int main(int argc, char** argv)
+{
+ SensorManager& mgr(SensorManager::getInstance());
+
+ Sensor const* const* list;
+ ssize_t count = mgr.getSensorList(&list);
+ printf("numSensors=%d\n", count);
+
+ sp<SensorEventQueue> q = mgr.createEventQueue();
+ printf("queue=%p\n", q.get());
+
+ Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER);
+ printf("accelerometer=%p (%s)\n",
+ accelerometer, accelerometer->getName().string());
+ q->enableSensor(accelerometer);
+
+ q->setEventRate(accelerometer, ms2ns(10));
+
+ sp<PollLoop> loop = new PollLoop(false);
+ loop->setCallback(q->getFd(), POLLIN, receiver, q.get());
+
+ do {
+ //printf("about to poll...\n");
+ int32_t ret = loop->pollOnce(-1, 0, 0);
+ switch (ret) {
+ case ALOOPER_POLL_CALLBACK:
+ //("ALOOPER_POLL_CALLBACK\n");
+ break;
+ case ALOOPER_POLL_TIMEOUT:
+ printf("ALOOPER_POLL_TIMEOUT\n");
+ break;
+ case ALOOPER_POLL_ERROR:
+ printf("ALOOPER_POLL_TIMEOUT\n");
+ break;
+ default:
+ printf("ugh? poll returned %d\n", ret);
+ break;
+ }
+ } while (1);
+
+
+ return 0;
+}
diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
index 78a90fb..f3baff4 100644
--- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
@@ -20,6 +20,10 @@
import android.content.Context;
import android.content.Intent;
import android.os.DropBoxManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.ServiceManager;
import android.os.StatFs;
import android.provider.Settings;
@@ -27,10 +31,13 @@
import com.android.server.DropBoxManagerService;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.Random;
import java.util.zip.GZIPOutputStream;
@@ -531,6 +538,203 @@
service.stop();
}
+ public void testDropBoxEntrySerialization() throws Exception {
+ // Make sure DropBoxManager.Entry can be serialized to a Parcel and back
+ // under a variety of conditions.
+
+ Parcel parcel = Parcel.obtain();
+ File dir = getEmptyDir("testDropBoxEntrySerialization");
+
+ new DropBoxManager.Entry("empty", 1000000).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("string", 2000000, "String Value").writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("bytes", 3000000, "Bytes Value".getBytes(),
+ DropBoxManager.IS_TEXT).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("zerobytes", 4000000, new byte[0], 0).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("emptybytes", 5000000, (byte[]) null,
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+
+ try {
+ new DropBoxManager.Entry("badbytes", 99999,
+ "Bad Bytes Value".getBytes(),
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+ fail("IllegalArgumentException expected for non-null byte[] and IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badbytes", 99999, (byte[]) null, 0).writeToParcel(parcel, 0);
+ fail("IllegalArgumentException expected for null byte[] and non-IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ File f = new File(dir, "file.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write("File Value".getBytes());
+ os.close();
+
+ new DropBoxManager.Entry("file", 6000000, f, DropBoxManager.IS_TEXT).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("binfile", 7000000, f, 0).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("emptyfile", 8000000, (ParcelFileDescriptor) null,
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, new File(dir, "nonexist.dat"), 0);
+ fail("IOException expected for nonexistent file");
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, f, DropBoxManager.IS_EMPTY).writeToParcel(
+ parcel, 0);
+ fail("IllegalArgumentException expected for non-null file and IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, (ParcelFileDescriptor) null, 0);
+ fail("IllegalArgumentException expected for null PFD and non-IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ File gz = new File(dir, "file.gz");
+ GZIPOutputStream gzout = new GZIPOutputStream(new FileOutputStream(gz));
+ gzout.write("Gzip File Value".getBytes());
+ gzout.close();
+
+ new DropBoxManager.Entry("gzipfile", 9000000, gz,
+ DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("gzipbinfile", 10000000, gz,
+ DropBoxManager.IS_GZIPPED).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ //
+ // Switch from writing to reading
+ //
+
+ parcel.setDataPosition(0);
+ DropBoxManager.Entry e;
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("empty", e.getTag());
+ assertEquals(1000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("string", e.getTag());
+ assertEquals(2000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("String Value", e.getText(100));
+ assertEquals("String Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("bytes", e.getTag());
+ assertEquals(3000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("Bytes Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("zerobytes", e.getTag());
+ assertEquals(4000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null,
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("emptybytes", e.getTag());
+ assertEquals(5000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("file", e.getTag());
+ assertEquals(6000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("File Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("binfile", e.getTag());
+ assertEquals(7000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals("File Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("emptyfile", e.getTag());
+ assertEquals(8000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("gzipfile", e.getTag());
+ assertEquals(9000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("Gzip File Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("gzipbinfile", e.getTag());
+ assertEquals(10000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals("Gzip File Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ assertEquals(0, parcel.dataAvail());
+ parcel.recycle();
+ }
+
+ public void testDropBoxEntrySerializationDoesntLeakFileDescriptors() throws Exception {
+ File dir = getEmptyDir("testDropBoxEntrySerialization");
+ File f = new File(dir, "file.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write("File Value".getBytes());
+ os.close();
+
+ int before = countOpenFiles();
+ assertTrue(before > 0);
+
+ for (int i = 0; i < 1000; i++) {
+ Parcel parcel = Parcel.obtain();
+ new DropBoxManager.Entry("file", 1000000, f, 0).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ parcel.setDataPosition(0);
+ DropBoxManager.Entry e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("file", e.getTag());
+ e.close();
+
+ parcel.recycle();
+ }
+
+ int after = countOpenFiles();
+ assertTrue(after > 0);
+ assertTrue(after < before + 20);
+ }
+
private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception {
byte[] bytes = new byte[size];
new Random(System.currentTimeMillis()).nextBytes(bytes);
@@ -564,4 +768,8 @@
assertTrue(dir.listFiles().length == 0);
return dir;
}
+
+ private int countOpenFiles() {
+ return new File("/proc/" + Process.myPid() + "/fd").listFiles().length;
+ }
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 098359c..8cb9e0d 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -105,6 +105,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="TextActivity"
+ android:label="_Text"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
new file mode 100644
index 0000000..6665ef5
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TextActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new CustomTextView(this));
+ }
+
+ static class CustomTextView extends View {
+ private final Paint mMediumPaint;
+ private final Paint mLargePaint;
+
+ CustomTextView(Context c) {
+ super(c);
+
+ mMediumPaint = new Paint();
+ mMediumPaint.setAntiAlias(true);
+ mMediumPaint.setColor(0xffff0000);
+ mLargePaint = new Paint();
+ mLargePaint.setAntiAlias(true);
+ mLargePaint.setTextSize(36.0f);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
+ canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint);
+ }
+ }
+}
\ No newline at end of file