diff options
62 files changed, 2759 insertions, 980 deletions
diff --git a/Android.mk b/Android.mk index 30a67e3e8db4..3afbfd7a0460 100644 --- a/Android.mk +++ b/Android.mk @@ -113,7 +113,6 @@ LOCAL_SRC_FILES += \ 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 e66e52934f93..d429592251e7 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,7 +19612,7 @@ <parameter name="tab" type="android.app.ActionBar.Tab"> </parameter> </method> -<method name="selectTabAt" +<method name="setBackgroundDrawable" return="void" abstract="true" native="false" @@ -19611,10 +19622,10 @@ deprecated="not deprecated" visibility="public" > -<parameter name="position" type="int"> +<parameter name="d" type="android.graphics.drawable.Drawable"> </parameter> </method> -<method name="setBackgroundDrawable" +<method name="setCustomNavigationMode" return="void" abstract="true" native="false" @@ -19624,10 +19635,10 @@ deprecated="not deprecated" visibility="public" > -<parameter name="d" type="android.graphics.drawable.Drawable"> +<parameter name="view" type="android.view.View"> </parameter> </method> -<method name="setCustomNavigationMode" +<method name="setDisplayOptions" return="void" abstract="true" native="false" @@ -19637,7 +19648,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="view" type="android.view.View"> +<parameter name="options" type="int"> </parameter> </method> <method name="setDisplayOptions" @@ -19652,8 +19663,10 @@ > <parameter name="options" type="int"> </parameter> +<parameter name="mask" type="int"> +</parameter> </method> -<method name="setDisplayOptions" +<method name="setDropdownNavigationMode" return="void" abstract="true" native="false" @@ -19663,9 +19676,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="options" type="int"> +<parameter name="adapter" type="android.widget.SpinnerAdapter"> </parameter> -<parameter name="mask" type="int"> +<parameter name="callback" type="android.app.ActionBar.NavigationCallback"> </parameter> </method> <method name="setDropdownNavigationMode" @@ -19682,6 +19695,21 @@ </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" @@ -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 a880a913029d..457cbd4ada8d 100644 --- a/cmds/system_server/library/Android.mk +++ b/cmds/system_server/library/Android.mk @@ -10,11 +10,13 @@ LOCAL_C_INCLUDES := \ $(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 1d57fdcc4c77..a29ba733a0f0 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 @@ extern "C" status_t system_init() 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 e1124a1874d6..fbc0be382503 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -93,6 +93,35 @@ public abstract class ActionBar { 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 class ActionBar { 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 a2a74f8c91b4..8eded3d9117a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1206,7 +1206,7 @@ class ContextImpl extends Context { 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 d187dc0acc7e..5e4b5df467e6 100644 --- a/core/java/android/app/ContextualMode.java +++ b/core/java/android/app/ContextualMode.java @@ -61,7 +61,7 @@ public abstract class ContextualMode { /** * 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 @@ public abstract class ContextualMode { /** * 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 @@ public abstract class ContextualMode { * * <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 c9fdfba48c1d..7fd5dde4b578 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -172,6 +172,7 @@ class LoaderManagerImpl implements LoaderManager { // 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 eecd01e10f82..3fe6087c7c42 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -44,19 +44,16 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { * 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { } } } finally { - db.unlock(); + query.mDatabase.unlock(); } } @@ -251,7 +270,9 @@ public class SQLiteCursor extends AbstractWindowedCursor { * @return the SQLiteDatabase that this cursor is associated with. */ public SQLiteDatabase getDatabase() { - return mDatabase; + synchronized (this) { + return mQuery.mDatabase; + } } @Override @@ -287,7 +308,7 @@ public class SQLiteCursor extends AbstractWindowedCursor { } } 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { } } + private synchronized SQLiteQuery getQuery() { + return mQuery; + } + @Override public int getColumnIndex(String columnName) { // Create mColumnNameMap on demand @@ -350,9 +375,11 @@ public class SQLiteCursor extends AbstractWindowedCursor { @Override public void close() { super.close(); - deactivateCommon(); - mQuery.close(); - mDriver.cursorClosed(); + synchronized (this) { + deactivateCommon(); + mQuery.close(); + mDriver.cursorClosed(); + } } /** @@ -361,7 +388,7 @@ public class SQLiteCursor extends AbstractWindowedCursor { */ 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { } finally { queryThreadUnlock(); } - } finally { - mDatabase.unlock(); } if (Config.LOGV) { @@ -452,14 +486,14 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 d0588582ca87..32012d379586 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -341,6 +341,12 @@ public class SQLiteDatabase extends SQLiteClosable { */ /* 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 @@ public class SQLiteDatabase extends SQLiteClosable { } } + /* 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 @@ public class SQLiteDatabase extends SQLiteClosable { } /* 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 @@ public class SQLiteDatabase extends SQLiteClosable { /* 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); - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "getDbConnection threadid = " + Thread.currentThread().getId() + - ", request on # " + mConnectionNum + - ", assigned # " + db.mConnectionNum + ", " + getPath()); + return mConnectionPool.get(sql); } - 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 be4925798815..6cdfa6906635 100644 --- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java +++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java @@ -53,7 +53,7 @@ public class SQLiteDirectCursorDriver implements SQLiteCursorDriver { // 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 e6011eeafdcb..1732971ea5f8 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -53,6 +53,18 @@ public class SQLiteQuery extends SQLiteProgram { } /** + * 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 67180bd90064..000000000000 --- 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 492f8cc4d508..e6750e677705 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.IRotationWatcher; 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 @@ public class SensorManager /*-----------------------------------------------------------------------*/ - private ISensorService mSensorService; Looper mMainLooper; @SuppressWarnings("deprecation") private HashMap<SensorListener, LegacyListener> mLegacyListenersMap = @@ -356,6 +348,7 @@ public class SensorManager /* 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 @@ public class SensorManager 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 @@ public class SensorManager 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 @@ public class SensorManager } // 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 @@ public class SensorManager /*-----------------------------------------------------------------------*/ - 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 @@ public class SensorManager * {@hide} */ public SensorManager(Looper mainLooper) { - mSensorService = ISensorService.Stub.asInterface( - ServiceManager.getService(Context.SENSOR_SERVICE)); mMainLooper = mainLooper; @@ -1051,42 +1004,37 @@ public class SensorManager return false; } - try { - synchronized (sListeners) { - ListenerDelegate l = null; - for (ListenerDelegate i : sListeners) { - if (i.getListener() == listener) { - l = i; - break; - } + 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); + 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) { - l.addSensor(sensor); + 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); + } } - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in registerListener: ", e); - result = false; } return result; } @@ -1095,27 +1043,23 @@ public class SensorManager 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 @@ public class SensorManager 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 @@ public class SensorManager 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 00b6864c0393..455bd36e5a3e 100644 --- a/core/java/android/net/DownloadManager.java +++ b/core/java/android/net/DownloadManager.java @@ -22,7 +22,6 @@ import android.database.Cursor; 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 class DownloadManager { */ 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 class DownloadManager { */ 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. + */ + public static final int NOTIFICATION_WHEN_RUNNING = 1; + + /** + * Bit flag for {@link #setAllowedNetworkTypes} corresponding to + * {@link ConnectivityManager#TYPE_MOBILE}. */ - private static final int NOTIFICATION_WHEN_RUNNING = 1; + public static final int NETWORK_MOBILE = 1 << 0; - 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_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 @@ public class DownloadManager { /** * 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 @@ public class DownloadManager { 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 @@ public class DownloadManager { } 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 @@ public class DownloadManager { + " 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 @@ public class DownloadManager { } 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 @@ public class DownloadManager { * 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 4df33e009aae..23fdb0bd7045 100644 --- a/core/java/android/os/DropBoxManager.java +++ b/core/java/android/os/DropBoxManager.java @@ -53,6 +53,9 @@ public class DropBoxManager { /** 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 @@ public class DropBoxManager { /** 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 @@ public class DropBoxManager { * 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 @@ public class DropBoxManager { * 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 @@ public class DropBoxManager { * 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 @@ public class DropBoxManager { 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 class DropBoxManager { 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 @@ public class DropBoxManager { 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 @@ public class DropBoxManager { * @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 @@ public class DropBoxManager { * @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 2a612fe5e301..01db979bf36b 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 final class Downloads { */ 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 142463868743..78648ff142f5 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -410,9 +410,21 @@ class GLES20Canvas extends Canvas { 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 @@ class GLES20Canvas extends Canvas { // 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 15b833522f92..3c161cad3289 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -896,11 +896,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // 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 e6fa405b11ce..5b256f26359d 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -110,6 +110,7 @@ class LoadListener extends Handler implements EventHandler { 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 @@ class LoadListener extends Handler implements EventHandler { private void clearNativeLoader() { sNativeLoaderCount -= 1; mNativeLoader = 0; + mSetNativeResponse = false; } /* @@ -1086,13 +1088,18 @@ class LoadListener extends Handler implements EventHandler { // 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; + } + + setNativeResponse(); + } - // Commit the headers to WebCore + private void setNativeResponse() { int nativeResponse = createNativeResponse(); // The native code deletes the native response object. nativeReceivedResponse(nativeResponse); + mSetNativeResponse = true; } /** @@ -1127,6 +1134,9 @@ class LoadListener extends Handler implements EventHandler { */ private void commitLoad() { if (mCancelled) return; + if (!mSetNativeResponse) { + setNativeResponse(); + } if (mIsMainPageLoader) { String type = sCertificateTypeMap.get(mMimeType); @@ -1197,6 +1207,10 @@ class LoadListener extends Handler implements EventHandler { } if (mNativeLoader != 0) { PerfChecker checker = new PerfChecker(); + if (!mSetNativeResponse) { + setNativeResponse(); + } + nativeFinished(); checker.responseAlert("res nativeFinished"); clearNativeLoader(); @@ -1312,6 +1326,9 @@ class LoadListener extends Handler implements EventHandler { 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 663743c97366..fd0780fabe08 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -401,6 +401,10 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout + 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout invalidate(); break; } - if (mTouchMode != TOUCH_DRAG_MODE) { + + if (mTouchMode != TOUCH_DRAG_MODE && + mTouchMode != TOUCH_DRAG_LAYER_MODE) { if (!mConfirmMove) { break; @@ -4808,6 +4882,9 @@ public class WebView extends AbsoluteLayout deltaX = 0; deltaY = 0; + if (skipScaleGesture) { + startScrollingLayer(gestureX, gestureY); + } startDrag(); } @@ -4816,17 +4893,19 @@ public class WebView extends AbsoluteLayout } // 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout // 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 ec6d2be44c44..409edf99abf9 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 class ActionBarImpl extends ActionBar { } 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 @@ public class ActionBarImpl extends ActionBar { 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 @@ public class ActionBarImpl extends ActionBar { } 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 @@ public class ActionBarImpl extends ActionBar { trans.commit(); } - @Override - public void selectTabAt(int position) { - selectTab(mTabs.get(position)); - } - /** * @hide */ @@ -356,7 +383,7 @@ public class ActionBarImpl extends ActionBar { @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 @@ public class ActionBarImpl extends ActionBar { @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 class ActionBarImpl extends ActionBar { } 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 8f8b3afe62f6..d703a2f7f730 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -343,7 +343,15 @@ public class ActionBarView extends ViewGroup { 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 fea5ae302158..77c77f98eefa 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -171,6 +171,7 @@ LOCAL_SHARED_LIBRARIES := \ 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 9a90b72fe50a..7b2341813099 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 @@ struct SensorOffsets * 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 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint //---------------------------------------------------------------------------- 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; - } - } - 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); + 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); } - - // 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 @@ static JNINativeMethod gMethods[] = { {"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 fa4d23cff77e..2f1dcb650ff5 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -255,7 +255,8 @@ static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas, 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 @@ static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas, 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 bb5e02465501..525dd2db0adc 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 @@ public class DatabaseConnectionPoolTest extends AndroidTestCase { 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 @@ public class DatabaseConnectionPoolTest extends AndroidTestCase { 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 @@ public class DatabaseConnectionPoolTest extends AndroidTestCase { } 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 000000000000..3c3ff3fcc7f8 --- /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 91ef0b7a3afc..662ba97a3122 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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { } @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 @@ public class SQLiteDatabaseTest extends AndroidTestCase { } } - 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 3d63aa66aacc..6349cb33bda0 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -931,15 +931,14 @@ public class Paint { } /** - * 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 3e05076338b4..9c8afc5a08b0 100644 --- a/include/gui/ISensorServer.h +++ b/include/gui/ISensorServer.h @@ -36,7 +36,7 @@ class ISensorServer : public IInterface 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 86a16f14f124..e696d6386819 100644 --- a/include/gui/Sensor.h +++ b/include/gui/Sensor.h @@ -51,7 +51,8 @@ public: 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 d8d8128e77c1..ad36dacea629 100644 --- a/include/gui/SensorEventQueue.h +++ b/include/gui/SensorEventQueue.h @@ -42,6 +42,7 @@ namespace android { class ISensorEventConnection; class Sensor; +class PollLoop; // ---------------------------------------------------------------------------- @@ -56,13 +57,23 @@ public: 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 0d65334ce6a8..e1b1a7be45fb 100644 --- a/include/gui/SensorManager.h +++ b/include/gui/SensorManager.h @@ -47,13 +47,13 @@ public: 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 3e9d456fd85f..a5083fed0aba 100644 --- a/libs/gui/ISensorEventConnection.cpp +++ b/libs/gui/ISensorEventConnection.cpp @@ -47,6 +47,7 @@ public: 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 @@ public: 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 @@ public: 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 c6177bc7138e..71110925d9e7 100644 --- a/libs/gui/ISensorServer.cpp +++ b/libs/gui/ISensorServer.cpp @@ -48,6 +48,7 @@ public: 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 @@ public: 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 1fdd285823d6..48e1cb7a9042 100644 --- a/libs/gui/Sensor.cpp +++ b/libs/gui/Sensor.cpp @@ -36,6 +36,18 @@ Sensor::Sensor() { } +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 f922ac4b0df0..4b468424912b 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 SensorEventQueue::read(ASensorEvent* events, size_t numEvents) 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 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) return size; } -status_t SensorEventQueue::enableSensor(Sensor const* sensor) const +sp<PollLoop> SensorEventQueue::getPollLoop() const { - return mSensorEventConnection->enableDisable(sensor->getHandle(), true); + Mutex::Autolock _l(mLock); + if (mPollLoop == 0) { + mPollLoop = new PollLoop(true); + mPollLoop->setCallback(getFd(), POLLIN, NULL, NULL); + } + return mPollLoop; } -status_t SensorEventQueue::disableSensor(Sensor const* sensor) const +status_t SensorEventQueue::waitForEvent() const { - return mSensorEventConnection->enableDisable(sensor->getHandle(), false); + 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::setEventRate(Sensor const* sensor, nsecs_t ns) const +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 { + return mSensorEventConnection->enableDisable(sensor->getHandle(), false); +} + +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 cd892851f74d..d719efb5f11e 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 @@ ANDROID_SINGLETON_STATIC_INSTANCE(SensorManager) 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 1bdb9d3a44e5..172952a8f49d 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) 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 000000000000..8557b87ec924 --- /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 000000000000..c18327a99ee2 --- /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 1fa76d26cfa3..8f04d92c0981 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 @@ OpenGLRenderer::OpenGLRenderer(): mDrawColorProgram = new DrawColorProgram; mDrawTextureProgram = new DrawTextureProgram; + mDrawTextProgram = new DrawTextProgram; mDrawLinearGradientProgram = new DrawLinearGradientProgram; mCurrentProgram = mDrawTextureProgram; @@ -527,6 +529,39 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, 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 @@ void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float 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 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b 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 9dc2a43ecac7..b82366bdc338 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 @@ public: 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 @@ private: sp<Program> mCurrentProgram; sp<DrawColorProgram> mDrawColorProgram; sp<DrawTextureProgram> mDrawTextureProgram; + sp<DrawTextProgram> mDrawTextProgram; sp<DrawLinearGradientProgram> mDrawLinearGradientProgram; // Used to draw textured quads @@ -357,6 +361,9 @@ private: 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 841b6c842f9a..6e608084f5bb 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -33,6 +33,8 @@ namespace uirenderer { #include "shaders/drawTexture.vert" #include "shaders/drawTexture.frag" +#include "shaders/drawText.frag" + #include "shaders/drawLinearGradient.vert" #include "shaders/drawLinearGradient.frag" @@ -169,6 +171,12 @@ DrawTextureProgram::DrawTextureProgram(): 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 @@ void DrawTextureProgram::remove() { } /////////////////////////////////////////////////////////////////////////////// +// Draw text +/////////////////////////////////////////////////////////////////////////////// + +DrawTextProgram::DrawTextProgram(): + DrawTextureProgram(gDrawTextureVertexShader, gDrawTextFragmentShader) { +} + +/////////////////////////////////////////////////////////////////////////////// // Draw linear gradient /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 18a8e925460b..824aa0525e44 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -167,6 +167,7 @@ protected: class DrawTextureProgram: public DrawColorProgram { public: DrawTextureProgram(); + DrawTextureProgram(const char* vertex, const char* fragment); /** * Binds this program to the GL context. @@ -190,6 +191,11 @@ public: 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 000000000000..49532c777a19 --- /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 7a3907e76a58..e1fc4e7bc9ac 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -43,17 +43,18 @@ ASensorManager* ASensorManager_getInstance() 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 ASensorManager_destroyEventQueue(ASensorManager* manager, /*****************************************************************************/ -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 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, /*****************************************************************************/ -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 4291d3e88fc2..00d95d8e64fc 100644 --- a/native/include/android/sensor.h +++ b/native/include/android/sensor.h @@ -87,6 +87,7 @@ enum { * A sensor event. */ +/* NOTE: Must match hardware/sensors.h */ typedef struct ASensorVector { union { float v[3]; @@ -95,23 +96,32 @@ typedef struct ASensorVector { 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 @@ typedef struct ASensorEventQueue ASensorEventQueue; struct ASensor; typedef struct ASensor ASensor; +typedef ASensor const* ASensorRef; +typedef ASensorRef const* ASensorList; /*****************************************************************************/ @@ -141,13 +153,13 @@ ASensorManager* ASensorManager_getInstance(); /* * 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 @@ int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* /* * 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 @@ int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor* sensor); * 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 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, /* * 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 9f5718ff2465..000000000000 --- 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 dfb8f1252c17..3586d21df75e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -162,10 +162,6 @@ class ServerThread extends Thread { 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 0cf36b3ca956..cdc0a6f8fadc 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -7,7 +7,6 @@ LOCAL_SRC_FILES:= \ 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 77db6da22a6e..000000000000 --- 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 1a2d8b6975d8..cd4f0a46c517 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -9,7 +9,6 @@ int register_android_server_BatteryService(JNIEnv* env); 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 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) 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 000000000000..75f690f1867b --- /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 000000000000..a4f654976469 --- /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 000000000000..873195615307 --- /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 000000000000..45296dd6c0d4 --- /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 000000000000..e464713ddab6 --- /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 78a90fb1243a..f3baff4bd1b4 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.ContentResolver; 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 android.test.AndroidTestCase; 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 @@ public class DropBoxTest extends AndroidTestCase { 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 @@ public class DropBoxTest extends AndroidTestCase { 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 098359c73ec0..8cb9e0dd378f 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 000000000000..6665ef52b0c7 --- /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 |