diff options
97 files changed, 2952 insertions, 2081 deletions
diff --git a/api/current.xml b/api/current.xml index 192e0bf6329d..c6df75920453 100644 --- a/api/current.xml +++ b/api/current.xml @@ -205332,6 +205332,21 @@ <parameter name="boundPosition" type="int"> </parameter> </method> +<method name="smoothScrollToPositionFromTop" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="position" type="int"> +</parameter> +<parameter name="offset" type="int"> +</parameter> +</method> <method name="verifyDrawable" return="boolean" abstract="false" diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 75948a5d532c..10668a496fb5 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -1031,6 +1031,7 @@ void CameraService::Client::handleShutter(image_rect_type *size) { mHardware->getRawHeap()); mSurface->registerBuffers(buffers); + IPCThreadState::self()->flushCommands(); } mLock.unlock(); diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index d43368b1b1e2..161161c7d8fe 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -5,12 +5,14 @@ import dalvik.system.PathClassLoader; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.graphics.PixelFormat; import android.os.Bundle; import android.os.Looper; import android.os.MessageQueue; import android.view.InputChannel; import android.view.InputQueue; import android.view.KeyEvent; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; @@ -41,10 +43,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, private native void onStopNative(int handle); private native void onLowMemoryNative(int handle); private native void onWindowFocusChangedNative(int handle, boolean focused); - private native void onSurfaceCreatedNative(int handle, SurfaceHolder holder); - private native void onSurfaceChangedNative(int handle, SurfaceHolder holder, + private native void onSurfaceCreatedNative(int handle, Surface surface); + private native void onSurfaceChangedNative(int handle, Surface surface, int format, int width, int height); - private native void onSurfaceDestroyedNative(int handle, SurfaceHolder holder); + private native void onSurfaceDestroyedNative(int handle); private native void onInputChannelCreatedNative(int handle, InputChannel channel); private native void onInputChannelDestroyedNative(int handle, InputChannel channel); @@ -55,6 +57,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, getWindow().takeSurface(this); getWindow().takeInputQueue(this); + getWindow().setFormat(PixelFormat.RGB_565); try { ai = getPackageManager().getActivityInfo( @@ -98,7 +101,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, protected void onDestroy() { mDestroyed = true; if (mCurSurfaceHolder != null) { - onSurfaceDestroyedNative(mNativeHandle, mCurSurfaceHolder); + onSurfaceDestroyedNative(mNativeHandle); mCurSurfaceHolder = null; } if (mCurInputQueue != null) { @@ -158,21 +161,21 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, public void surfaceCreated(SurfaceHolder holder) { if (!mDestroyed) { mCurSurfaceHolder = holder; - onSurfaceCreatedNative(mNativeHandle, holder); + onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (!mDestroyed) { mCurSurfaceHolder = holder; - onSurfaceChangedNative(mNativeHandle, holder, format, width, height); + onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); } } public void surfaceDestroyed(SurfaceHolder holder) { mCurSurfaceHolder = null; if (!mDestroyed) { - onSurfaceDestroyedNative(mNativeHandle, holder); + onSurfaceDestroyedNative(mNativeHandle); } } @@ -196,4 +199,12 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, decor.dispatchKeyEvent(event); } } + + void setWindowFlags(int flags, int mask) { + getWindow().setFlags(flags, mask); + } + + void setWindowFormat(int format) { + getWindow().setFormat(format); + } } diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index 8434ca5c5c81..9b14998eeba2 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -462,8 +462,8 @@ public abstract class AbstractCursor implements CrossProcessCursor { protected int mPos; /** - * If {@link mRowIdColumnIndex} is not -1 this contains contains the value of - * the column at {@link mRowIdColumnIndex} for the current row this cursor is + * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of + * the column at {@link #mRowIdColumnIndex} for the current row this cursor is * pointing at. */ protected Long mCurrentRowID; diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index eec03b21e52a..1a096971e4f2 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -16,7 +16,6 @@ package android.database.sqlite; -import android.app.ActivityThread; import android.app.AppGlobals; import android.content.ContentValues; import android.database.Cursor; @@ -1045,6 +1044,9 @@ public class SQLiteDatabase extends SQLiteClosable { if (!isOpen()) { return; // already closed } + if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { + Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum); + } lock(); try { closeClosable(); @@ -2123,7 +2125,8 @@ public class SQLiteDatabase extends SQLiteClosable { /* package */ void verifyDbIsOpen() { if (!isOpen()) { - throw new IllegalStateException("database " + getPath() + " already closed"); + throw new IllegalStateException("database " + getPath() + " (conn# " + + mConnectionNum + ") already closed"); } } diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index a3513e61e6a3..83cec37aee1c 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -28,6 +28,11 @@ public abstract class SQLiteProgram extends SQLiteClosable { private static final String TAG = "SQLiteProgram"; + /** the type of sql statement being processed by this object */ + private static final int SELECT_STMT = 1; + private static final int UPDATE_STMT = 2; + private static final int OTHER_STMT = 3; + /** The database this program is compiled against. * @deprecated do not use this */ @@ -61,35 +66,37 @@ public abstract class SQLiteProgram extends SQLiteClosable { /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { mDatabase = db; mSql = sql.trim(); - db.acquireReference(); - db.addSQLiteClosable(this); - this.nHandle = db.mNativeHandle; + attachObjectToDatabase(db); + compileSql(); + } + + private void compileSql() { + if (nStatement > 0) { + // already compiled. + return; + } // only cache CRUD statements - String prefixSql = mSql.substring(0, 6); - if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") && - !prefixSql.equalsIgnoreCase("REPLAC") && - !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) { - mCompiledSql = new SQLiteCompiledSql(db, sql); + if (getSqlStatementType(mSql) == OTHER_STMT) { + mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); nStatement = mCompiledSql.nStatement; // since it is not in the cache, no need to acquire() it. return; } - // it is not pragma - mCompiledSql = db.getCompiledStatementForSql(sql); + mCompiledSql = mDatabase.getCompiledStatementForSql(mSql); if (mCompiledSql == null) { // create a new compiled-sql obj - mCompiledSql = new SQLiteCompiledSql(db, sql); + mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); // add it to the cache of compiled-sqls // but before adding it and thus making it available for anyone else to use it, // make sure it is acquired by me. mCompiledSql.acquire(); - db.addToCompiledQueries(sql, mCompiledSql); + mDatabase.addToCompiledQueries(mSql, mCompiledSql); if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement + - ") for sql: " + sql); + ") for sql: " + mSql); } } else { // it is already in compiled-sql cache. @@ -100,12 +107,12 @@ public abstract class SQLiteProgram extends SQLiteClosable { // we can't have two different SQLiteProgam objects can't share the same // CompiledSql object. create a new one. // finalize it when I am done with it in "this" object. - mCompiledSql = new SQLiteCompiledSql(db, sql); + mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" + mCompiledSql.nStatement + ") because the previously created DbObj (id#" + last + - ") was not released for sql:" + sql); + ") was not released for sql:" + mSql); } // since it is not in the cache, no need to acquire() it. } @@ -113,11 +120,37 @@ public abstract class SQLiteProgram extends SQLiteClosable { nStatement = mCompiledSql.nStatement; } + private int getSqlStatementType(String sql) { + if (mSql.length() < 6) { + return OTHER_STMT; + } + String prefixSql = mSql.substring(0, 6); + if (prefixSql.equalsIgnoreCase("SELECT")) { + return SELECT_STMT; + } else if (prefixSql.equalsIgnoreCase("INSERT") || + prefixSql.equalsIgnoreCase("UPDATE") || + prefixSql.equalsIgnoreCase("REPLAC") || + prefixSql.equalsIgnoreCase("DELETE")) { + return UPDATE_STMT; + } + return OTHER_STMT; + } + + private synchronized void attachObjectToDatabase(SQLiteDatabase db) { + db.acquireReference(); + db.addSQLiteClosable(this); + nHandle = db.mNativeHandle; + } + + private synchronized void detachObjectFromDatabase() { + mDatabase.removeSQLiteClosable(this); + mDatabase.releaseReference(); + } + @Override protected void onAllReferencesReleased() { releaseCompiledSqlIfNotInCache(); - mDatabase.releaseReference(); - mDatabase.removeSQLiteClosable(this); + detachObjectFromDatabase(); } @Override @@ -135,13 +168,13 @@ public abstract class SQLiteProgram extends SQLiteClosable { // it is NOT in compiled-sql cache. i.e., responsibility of // releasing this statement is on me. mCompiledSql.releaseSqlStatement(); - mCompiledSql = null; - nStatement = 0; } else { // it is in compiled-sql cache. reset its CompiledSql#mInUse flag mCompiledSql.release(); } - } + } + mCompiledSql = null; + nStatement = 0; } /** @@ -150,7 +183,9 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @return a unique identifier for this program */ public final int getUniqueId() { - return (mCompiledSql != null) ? mCompiledSql.nStatement : 0; + synchronized (this) { + return (mCompiledSql != null) ? mCompiledSql.nStatement : 0; + } } /* package */ String getSqlString() { @@ -176,12 +211,14 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param index The 1-based index to the parameter to bind null to */ public void bindNull(int index) { - mDatabase.verifyDbIsOpen(); - acquireReference(); - try { - native_bind_null(index); - } finally { - releaseReference(); + synchronized (this) { + mDatabase.verifyDbIsOpen(); + acquireReference(); + try { + native_bind_null(index); + } finally { + releaseReference(); + } } } @@ -193,12 +230,14 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindLong(int index, long value) { - mDatabase.verifyDbIsOpen(); - acquireReference(); - try { - native_bind_long(index, value); - } finally { - releaseReference(); + synchronized (this) { + mDatabase.verifyDbIsOpen(); + acquireReference(); + try { + native_bind_long(index, value); + } finally { + releaseReference(); + } } } @@ -210,12 +249,14 @@ public abstract class SQLiteProgram extends SQLiteClosable { * @param value The value to bind */ public void bindDouble(int index, double value) { - mDatabase.verifyDbIsOpen(); - acquireReference(); - try { - native_bind_double(index, value); - } finally { - releaseReference(); + synchronized (this) { + mDatabase.verifyDbIsOpen(); + acquireReference(); + try { + native_bind_double(index, value); + } finally { + releaseReference(); + } } } @@ -230,12 +271,14 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } - mDatabase.verifyDbIsOpen(); - acquireReference(); - try { - native_bind_string(index, value); - } finally { - releaseReference(); + synchronized (this) { + mDatabase.verifyDbIsOpen(); + acquireReference(); + try { + native_bind_string(index, value); + } finally { + releaseReference(); + } } } @@ -250,12 +293,14 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (value == null) { throw new IllegalArgumentException("the bind value at index " + index + " is null"); } - mDatabase.verifyDbIsOpen(); - acquireReference(); - try { - native_bind_blob(index, value); - } finally { - releaseReference(); + synchronized (this) { + mDatabase.verifyDbIsOpen(); + acquireReference(); + try { + native_bind_blob(index, value); + } finally { + releaseReference(); + } } } @@ -263,12 +308,14 @@ public abstract class SQLiteProgram extends SQLiteClosable { * Clears all existing bindings. Unset bindings are treated as NULL. */ public void clearBindings() { - mDatabase.verifyDbIsOpen(); - acquireReference(); - try { - native_clear_bindings(); - } finally { - releaseReference(); + synchronized (this) { + mDatabase.verifyDbIsOpen(); + acquireReference(); + try { + native_clear_bindings(); + } finally { + releaseReference(); + } } } @@ -276,10 +323,16 @@ public abstract class SQLiteProgram extends SQLiteClosable { * Release this program's resources, making it invalid. */ public void close() { - if (!mDatabase.isOpen()) { - return; + synchronized (this) { + if (nStatement == 0 || nHandle == 0 || !mDatabase.isOpen()) { + return; + } + releaseReference(); + // set all database objects to null/0, so that the user can't use a closed Object. + mCompiledSql = null; + nStatement = 0; + nHandle = 0; } - releaseReference(); } /** diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index e1ad306c76d4..f6ca1d128d9f 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -31,6 +31,9 @@ import dalvik.system.BlockGuard; */ public class SQLiteStatement extends SQLiteProgram { + private static final boolean READ = true; + private static final boolean WRITE = false; + /** * Don't use SQLiteStatement constructor directly, please use * {@link SQLiteDatabase#compileStatement(String)} @@ -49,19 +52,12 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public void execute() { - mDatabase.verifyDbIsOpen(); - BlockGuard.getThreadPolicy().onWriteToDisk(); - long timeStart = SystemClock.uptimeMillis(); - mDatabase.lock(); - - acquireReference(); + long timeStart = acquireAndLock(WRITE); try { - mDatabase.closePendingStatements(); native_execute(); mDatabase.logTimeStat(mSql, timeStart); } finally { - releaseReference(); - mDatabase.unlock(); + releaseAndUnlock(); } } @@ -75,20 +71,13 @@ public class SQLiteStatement extends SQLiteProgram * some reason */ public long executeInsert() { - mDatabase.verifyDbIsOpen(); - BlockGuard.getThreadPolicy().onWriteToDisk(); - long timeStart = SystemClock.uptimeMillis(); - mDatabase.lock(); - - acquireReference(); + long timeStart = acquireAndLock(WRITE); try { - mDatabase.closePendingStatements(); native_execute(); mDatabase.logTimeStat(mSql, timeStart); return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1; } finally { - releaseReference(); - mDatabase.unlock(); + releaseAndUnlock(); } } @@ -101,20 +90,13 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public long simpleQueryForLong() { - mDatabase.verifyDbIsOpen(); - BlockGuard.getThreadPolicy().onReadFromDisk(); - long timeStart = SystemClock.uptimeMillis(); - mDatabase.lock(); - - acquireReference(); + long timeStart = acquireAndLock(READ); try { - mDatabase.closePendingStatements(); long retValue = native_1x1_long(); mDatabase.logTimeStat(mSql, timeStart); return retValue; } finally { - releaseReference(); - mDatabase.unlock(); + releaseAndUnlock(); } } @@ -127,21 +109,50 @@ public class SQLiteStatement extends SQLiteProgram * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows */ public String simpleQueryForString() { - mDatabase.verifyDbIsOpen(); - BlockGuard.getThreadPolicy().onReadFromDisk(); - long timeStart = SystemClock.uptimeMillis(); - mDatabase.lock(); - - acquireReference(); + long timeStart = acquireAndLock(READ); try { - mDatabase.closePendingStatements(); String retValue = native_1x1_string(); mDatabase.logTimeStat(mSql, timeStart); return retValue; } finally { - releaseReference(); - mDatabase.unlock(); + releaseAndUnlock(); + } + } + + /** + * Called before every method in this class before executing a SQL statement, + * this method does the following: + * <ul> + * <li>make sure the database is open</li> + * <li>notifies {@link BlockGuard} of read/write</li> + * <li>get lock on the database</li> + * <li>acquire reference on this object</li> + * <li>and then return the current time _before_ the database lock was acquired</li> + * </ul> + * <p> + * This method removes the duplcate code from the other public + * methods in this class. + */ + private long acquireAndLock(boolean rwFlag) { + mDatabase.verifyDbIsOpen(); + if (rwFlag == WRITE) { + BlockGuard.getThreadPolicy().onWriteToDisk(); + } else { + BlockGuard.getThreadPolicy().onReadFromDisk(); } + long startTime = SystemClock.uptimeMillis(); + mDatabase.lock(); + acquireReference(); + mDatabase.closePendingStatements(); + return startTime; + } + + /** + * this method releases locks and references acquired in {@link #acquireAndLock(boolean)}. + */ + private void releaseAndUnlock() { + releaseReference(); + mDatabase.unlock(); } private final native void native_execute(); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index ccf122120ee8..b46091081c96 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -368,19 +368,19 @@ class GLES20Canvas extends Canvas { @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { - final float width = bitmap.getWidth(); - final float height = bitmap.getHeight(); final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, 0.0f, 0.0f, width, height, - 0.0f, 0.0f, width, height, matrix.native_instance, nativePaint, + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint, bitmap.getDensity(), mDensity, mScreenDensity); } + private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint, + int bitmapDensity, int canvasDensity, int screenDensity); + @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { 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, 0, nativePaint, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, bitmap.getDensity(), mDensity, mScreenDensity); } @@ -388,7 +388,7 @@ class GLES20Canvas extends Canvas { public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { 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, 0, nativePaint, + dst.left, dst.top, dst.right, dst.bottom, nativePaint, bitmap.getDensity(), mDensity, mScreenDensity); } @@ -397,7 +397,7 @@ class GLES20Canvas extends Canvas { private native void nDrawBitmap(int renderer, int bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, - float left, float top, float right, float bottom, int matrix, int paint, + float left, float top, float right, float bottom, int paint, int bitmapDensity, int canvasDensity, int screenDensity); @Override diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index a1ee3d3dfba8..d30bce9bb89a 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -413,33 +413,30 @@ abstract class HardwareRenderer { } EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { - int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, - num_config)) { + int[] index = new int[1]; + if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) { throw new IllegalArgumentException("eglChooseConfig failed"); } - int numConfigs = num_config[0]; - + int numConfigs = index[0]; if (numConfigs <= 0) { - throw new IllegalArgumentException( - "No configs match configSpec"); + throw new IllegalArgumentException("No configs match configSpec"); } EGLConfig[] configs = new EGLConfig[numConfigs]; - if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig#2 failed"); + if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) { + throw new IllegalArgumentException("eglChooseConfig failed"); } + EGLConfig config = chooseConfig(egl, display, configs); if (config == null) { throw new IllegalArgumentException("No config chosen"); } + return config; } - abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs); + abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs); private int[] filterConfigSpec(int[] configSpec) { if (mGlVersion != 2) { @@ -450,10 +447,10 @@ abstract class HardwareRenderer { */ int len = configSpec.length; int[] newConfigSpec = new int[len + 2]; - System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); - newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; + System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); + newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ - newConfigSpec[len+1] = EGL10.EGL_NONE; + newConfigSpec[len + 1] = EGL10.EGL_NONE; return newConfigSpec; } } @@ -496,13 +493,12 @@ abstract class HardwareRenderer { for (EGLConfig config : configs) { int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0); int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0); - if ((d >= mDepthSize) && (s >= mStencilSize)) { + if (d >= mDepthSize && s >= mStencilSize) { int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0); int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0); int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0); int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); - if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) && - (a == mAlphaSize)) { + if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) { return config; } } @@ -510,16 +506,15 @@ abstract class HardwareRenderer { return null; } - private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue) { - + private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, + int attribute, int defaultValue) { if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { return mValue[0]; } return defaultValue; } - } + } } /** diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 15ffffdb4413..1b5651b324c8 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -1222,9 +1222,7 @@ class CallbackProxy extends Handler { // for null. WebHistoryItem i = mBackForwardList.getCurrentItem(); if (i != null) { - if (precomposed || i.getTouchIconUrl() == null) { - i.setTouchIconUrl(url); - } + i.setTouchIconUrl(url, precomposed); } // Do an unsynchronized quick check to avoid posting if no callback has // been set. diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java index 428a59ce90f4..7c0e47821b7f 100644 --- a/core/java/android/webkit/WebHistoryItem.java +++ b/core/java/android/webkit/WebHistoryItem.java @@ -18,6 +18,9 @@ package android.webkit; import android.graphics.Bitmap; +import java.net.MalformedURLException; +import java.net.URL; + /** * A convenience class for accessing fields in an entry in the back/forward list * of a WebView. Each WebHistoryItem is a snapshot of the requested history @@ -39,8 +42,12 @@ public class WebHistoryItem implements Cloneable { private Bitmap mFavicon; // The pre-flattened data used for saving the state. private byte[] mFlattenedData; - // The apple-touch-icon url for use when adding the site to the home screen - private String mTouchIconUrl; + // The apple-touch-icon url for use when adding the site to the home screen, + // as obtained from a <link> element in the page. + private String mTouchIconUrlFromLink; + // If no <link> is specified, this holds the default location of the + // apple-touch-icon. + private String mTouchIconUrlServerDefault; // Custom client data that is not flattened or read by native code. private Object mCustomData; @@ -132,10 +139,28 @@ public class WebHistoryItem implements Cloneable { /** * Return the touch icon url. + * If no touch icon <link> tag was specified, returns + * <host>/apple-touch-icon.png. The DownloadTouchIcon class that + * attempts to retrieve the touch icon will handle the case where + * that file does not exist. An icon set by a <link> tag is always + * used in preference to an icon saved on the server. * @hide */ public String getTouchIconUrl() { - return mTouchIconUrl; + if (mTouchIconUrlFromLink != null) { + return mTouchIconUrlFromLink; + } else if (mTouchIconUrlServerDefault != null) { + return mTouchIconUrlServerDefault; + } + + try { + URL url = new URL(mOriginalUrl); + mTouchIconUrlServerDefault = new URL(url.getProtocol(), url.getHost(), url.getPort(), + "/apple-touch-icon.png").toString(); + } catch (MalformedURLException e) { + return null; + } + return mTouchIconUrlServerDefault; } /** @@ -171,11 +196,14 @@ public class WebHistoryItem implements Cloneable { } /** - * Set the touch icon url. + * Set the touch icon url. Will not overwrite an icon that has been + * set already from a <link> tag, unless the new icon is precomposed. * @hide */ - /*package*/ void setTouchIconUrl(String url) { - mTouchIconUrl = url; + /*package*/ void setTouchIconUrl(String url, boolean precomposed) { + if (precomposed || mTouchIconUrlFromLink == null) { + mTouchIconUrlFromLink = url; + } } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 91d62f6a249e..d5136ae98c34 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2314,7 +2314,9 @@ public class WebView extends AbsoluteLayout } /** - * Get the touch icon url for the apple-touch-icon <link> element. + * Get the touch icon url for the apple-touch-icon <link> element, or + * a URL on this site's server pointing to the standard location of a + * touch icon. * @hide */ public String getTouchIconUrl() { diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 00fadb5a45f6..7f7f46e0cb23 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -768,8 +768,8 @@ class ZoomManager { mMinZoomScale = zoomOverviewScale; } // fit the content width to the current view. Ignore the rounding error case. - if (!mWebView.drawHistory() && (mInZoomOverview || mInitialZoomOverview) - && Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1) { + if (!mWebView.drawHistory() && (mInitialZoomOverview || (mInZoomOverview + && Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1))) { mInitialZoomOverview = false; setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale)); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 10927a7524d5..1d5c5f944185 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2541,6 +2541,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private static final int MOVE_UP_POS = 2; private static final int MOVE_DOWN_BOUND = 3; private static final int MOVE_UP_BOUND = 4; + private static final int MOVE_OFFSET = 5; private int mMode; private int mTargetPos; @@ -2548,6 +2549,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private int mLastSeenPos; private int mScrollDuration; private final int mExtraScroll; + + private int mOffsetFromTop; PositionScroller() { mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength(); @@ -2639,11 +2642,37 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te post(this); } - + + void startWithOffset(int position, int offset) { + mTargetPos = position; + mOffsetFromTop = offset; + mBoundPos = INVALID_POSITION; + mLastSeenPos = INVALID_POSITION; + mMode = MOVE_OFFSET; + + final int firstPos = mFirstPosition; + final int lastPos = firstPos + getChildCount() - 1; + + int viewTravelCount = 0; + if (position < firstPos) { + viewTravelCount = firstPos - position; + } else if (position > lastPos) { + viewTravelCount = position - lastPos; + } else { + // On-screen, just scroll. + final int targetTop = getChildAt(position - firstPos).getTop(); + smoothScrollBy(targetTop - offset, SCROLL_DURATION); + return; + } + + mScrollDuration = SCROLL_DURATION / viewTravelCount; + post(this); + } + void stop() { removeCallbacks(this); } - + public void run() { final int listHeight = getHeight(); final int firstPos = mFirstPosition; @@ -2769,6 +2798,33 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; } + case MOVE_OFFSET: { + if (firstPos == mLastSeenPos) { + // No new views, let things keep going. + post(this); + } + + final int position = mTargetPos; + final int lastPos = firstPos + getChildCount() - 1; + + if (position < firstPos) { + final View firstView = getChildAt(0); + final int firstViewPixelsShowing = firstView.getHeight() + firstView.getTop(); + smoothScrollBy(-firstViewPixelsShowing, mScrollDuration); + post(this); + } else if (position > lastPos) { + final View lastView = getChildAt(getChildCount() - 1); + final int lastViewPixelsShowing = lastView.getBottom() - lastView.getHeight(); + smoothScrollBy(lastViewPixelsShowing, mScrollDuration); + post(this); + } else { + // On-screen, just scroll. + final int targetTop = getChildAt(position - firstPos).getTop(); + smoothScrollBy(targetTop - mOffsetFromTop, mScrollDuration); + } + break; + } + default: break; } @@ -2788,6 +2844,24 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * Smoothly scroll to the specified adapter position. The view will scroll + * such that the indicated position is displayed <code>offset</code> pixels from + * the top edge of the view. If this is impossible, (e.g. the offset would scroll + * the first or last item beyond the boundaries of the list) it will get as close + * as possible. + * + * @param position Position to scroll to + * @param offset Desired distance in pixels of <code>position</code> from the top + * of the view when scrolling is finished + */ + public void smoothScrollToPositionFromTop(int position, int offset) { + if (mPositionScroller == null) { + mPositionScroller = new PositionScroller(); + } + mPositionScroller.startWithOffset(position, offset); + } + + /** * Smoothly scroll to the specified adapter position. The view will * scroll such that the indicated position is displayed, but it will * stop early if scrolling further would scroll boundPosition out of diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java index 7b9b7bdafc0f..3fadf4c54075 100644 --- a/core/java/android/widget/CursorTreeAdapter.java +++ b/core/java/android/widget/CursorTreeAdapter.java @@ -134,14 +134,16 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem /** * Sets the group Cursor. * - * @param cursor The Cursor to set for the group. + * @param cursor The Cursor to set for the group. If there is an existing cursor + * it will be closed. */ public void setGroupCursor(Cursor cursor) { mGroupCursorHelper.changeCursor(cursor, false); } /** - * Sets the children Cursor for a particular group. + * Sets the children Cursor for a particular group. If there is an existing cursor + * it will be closed. * <p> * This is useful when asynchronously querying to prevent blocking the UI. * @@ -476,7 +478,7 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem mCursor.unregisterContentObserver(mContentObserver); mCursor.unregisterDataSetObserver(mDataSetObserver); - mCursor.deactivate(); + mCursor.close(); mCursor = null; } diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java index a1c65f0a81ab..a0335426ddf5 100644 --- a/core/java/android/widget/SimpleCursorTreeAdapter.java +++ b/core/java/android/widget/SimpleCursorTreeAdapter.java @@ -38,6 +38,10 @@ import android.view.View; * binding can be found, an {@link IllegalStateException} is thrown. */ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter { + + /** The name of the columns that contain the data to display for a group. */ + private String[] mGroupFromNames; + /** The indices of columns that contain data to display for a group. */ private int[] mGroupFrom; /** @@ -46,6 +50,9 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter */ private int[] mGroupTo; + /** The name of the columns that contain the data to display for a child. */ + private String[] mChildFromNames; + /** The indices of columns that contain data to display for a child. */ private int[] mChildFrom; /** @@ -171,38 +178,12 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter private void init(String[] groupFromNames, int[] groupTo, String[] childFromNames, int[] childTo) { + + mGroupFromNames = groupFromNames; mGroupTo = groupTo; + mChildFromNames = childFromNames; mChildTo = childTo; - - // Get the group cursor column indices, the child cursor column indices will come - // when needed - initGroupFromColumns(groupFromNames); - - // Get a temporary child cursor to init the column indices - if (getGroupCount() > 0) { - MyCursorHelper tmpCursorHelper = getChildrenCursorHelper(0, true); - if (tmpCursorHelper != null) { - initChildrenFromColumns(childFromNames, tmpCursorHelper.getCursor()); - deactivateChildrenCursorHelper(0); - } - } - } - - private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) { - for (int i = fromColumnNames.length - 1; i >= 0; i--) { - fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]); - } - } - - private void initGroupFromColumns(String[] groupFromNames) { - mGroupFrom = new int[groupFromNames.length]; - initFromColumns(mGroupCursorHelper.getCursor(), groupFromNames, mGroupFrom); - } - - private void initChildrenFromColumns(String[] childFromNames, Cursor childCursor) { - mChildFrom = new int[childFromNames.length]; - initFromColumns(childCursor, childFromNames, mChildFrom); } /** @@ -257,13 +238,29 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter } } + private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) { + for (int i = fromColumnNames.length - 1; i >= 0; i--) { + fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]); + } + } + @Override protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { + if (mChildFrom == null) { + mChildFrom = new int[mChildFromNames.length]; + initFromColumns(cursor, mChildFromNames, mChildFrom); + } + bindView(view, context, cursor, mChildFrom, mChildTo); } @Override protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { + if (mGroupFrom == null) { + mGroupFrom = new int[mGroupFromNames.length]; + initFromColumns(cursor, mGroupFromNames, mGroupFrom); + } + bindView(view, context, cursor, mGroupFrom, mGroupTo); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index dd59d6327663..dab1dba811cb 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -22,6 +22,8 @@ #include <android_runtime/AndroidRuntime.h> #include <android/native_activity.h> +#include <surfaceflinger/Surface.h> +#include <ui/egl/android_natives.h> #include <ui/InputTransport.h> #include <utils/PollLoop.h> @@ -29,6 +31,7 @@ #include "android_os_MessageQueue.h" #include "android_view_InputChannel.h" #include "android_view_KeyEvent.h" +#include "android_view_Surface.h" namespace android { @@ -37,8 +40,16 @@ static struct { jclass clazz; jmethodID dispatchUnhandledKeyEvent; + jmethodID setWindowFlags; + jmethodID setWindowFormat; } gNativeActivityClassInfo; +// ------------------------------------------------------------------------ + +/* + * Specialized input queue that allows unhandled key events to be dispatched + * back to the native activity's Java framework code. + */ struct MyInputQueue : AInputQueue { explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite) : AInputQueue(channel), mWorkWrite(workWrite) { @@ -74,13 +85,18 @@ struct MyInputQueue : AInputQueue { Vector<KeyEvent*> mPendingKeys; }; +// ------------------------------------------------------------------------ + +/* + * Native state for interacting with the NativeActivity class. + */ struct NativeCode { NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { memset(&activity, sizeof(activity), 0); memset(&callbacks, sizeof(callbacks), 0); dlhandle = _dlhandle; createActivityFunc = _createFunc; - surface = NULL; + nativeWindow = NULL; inputChannel = NULL; nativeInputQueue = NULL; mainWorkRead = mainWorkWrite = -1; @@ -104,18 +120,18 @@ struct NativeCode { if (mainWorkRead >= 0) close(mainWorkRead); if (mainWorkWrite >= 0) close(mainWorkWrite); if (dlhandle != NULL) { - dlclose(dlhandle); + // for now don't unload... we probably should clean this + // up and only keep one open dlhandle per proc, since there + // is really no benefit to unloading the code. + //dlclose(dlhandle); } } void setSurface(jobject _surface) { - if (surface != NULL) { - activity.env->DeleteGlobalRef(surface); - } if (_surface != NULL) { - surface = activity.env->NewGlobalRef(_surface); + nativeWindow = android_Surface_getNativeWindow(activity.env, _surface); } else { - surface = NULL; + nativeWindow = NULL; } } @@ -150,7 +166,7 @@ struct NativeCode { void* dlhandle; ANativeActivity_createFunc* createActivityFunc; - jobject surface; + sp<ANativeWindow> nativeWindow; jobject inputChannel; struct MyInputQueue* nativeInputQueue; @@ -160,6 +176,11 @@ struct NativeCode { sp<PollLoop> pollLoop; }; +// ------------------------------------------------------------------------ + +/* + * Callback for handling native events on the application's main thread. + */ static bool mainWorkCallback(int fd, int events, void* data) { NativeCode* code = (NativeCode*)data; if ((events & POLLIN) != 0) { @@ -180,6 +201,8 @@ static bool mainWorkCallback(int fd, int events, void* data) { return true; } +// ------------------------------------------------------------------------ + static jint loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue) { @@ -323,9 +346,9 @@ onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface if (handle != 0) { NativeCode* code = (NativeCode*)handle; code->setSurface(surface); - if (code->callbacks.onSurfaceCreated != NULL) { - code->callbacks.onSurfaceCreated(&code->activity, - (ASurfaceHolder*)code->surface); + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { + code->callbacks.onNativeWindowCreated(&code->activity, + code->nativeWindow.get()); } } } @@ -336,9 +359,17 @@ onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface { if (handle != 0) { NativeCode* code = (NativeCode*)handle; - if (code->surface != NULL && code->callbacks.onSurfaceChanged != NULL) { - code->callbacks.onSurfaceChanged(&code->activity, - (ASurfaceHolder*)code->surface, format, width, height); + sp<ANativeWindow> oldNativeWindow = code->nativeWindow; + code->setSurface(surface); + if (oldNativeWindow != code->nativeWindow) { + if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { + code->callbacks.onNativeWindowDestroyed(&code->activity, + oldNativeWindow.get()); + } + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { + code->callbacks.onNativeWindowCreated(&code->activity, + code->nativeWindow.get()); + } } } } @@ -348,9 +379,9 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa { if (handle != 0) { NativeCode* code = (NativeCode*)handle; - if (code->surface != NULL && code->callbacks.onSurfaceDestroyed != NULL) { - code->callbacks.onSurfaceDestroyed(&code->activity, - (ASurfaceHolder*)code->surface); + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { + code->callbacks.onNativeWindowDestroyed(&code->activity, + code->nativeWindow.get()); } code->setSurface(NULL); } @@ -398,9 +429,9 @@ static const JNINativeMethod g_methods[] = { { "onStopNative", "(I)V", (void*)onStop_native }, { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, - { "onSurfaceCreatedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceCreated_native }, - { "onSurfaceChangedNative", "(ILandroid/view/SurfaceHolder;III)V", (void*)onSurfaceChanged_native }, - { "onSurfaceDestroyedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceDestroyed_native }, + { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, + { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, + { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, }; @@ -421,11 +452,18 @@ int register_android_app_NativeActivity(JNIEnv* env) //LOGD("register_android_app_NativeActivity"); FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName); - + GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, gNativeActivityClassInfo.clazz, "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V"); - + + GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags, + gNativeActivityClassInfo.clazz, + "setWindowFlags", "(II)V"); + GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, + gNativeActivityClassInfo.clazz, + "setWindowFormat", "(I)V"); + return AndroidRuntime::registerNativeMethods( env, kNativeActivityPathName, g_methods, NELEM(g_methods)); diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index cba6ed15e553..89ad53418a24 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -195,13 +195,22 @@ static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas OpenGLRenderer* renderer, SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, - SkMatrix* matrix, SkPaint* paint, - jint bitmapDensity, jint canvasDensity, jint screenDensity) { + SkPaint* paint, jint bitmapDensity, jint canvasDensity, jint screenDensity) { if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) { renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, - dstLeft, dstTop, dstRight, dstBottom, matrix, paint); + dstLeft, dstTop, dstRight, dstBottom, paint); } else { + // TODO: implement + } +} +static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint, + jint bitmapDensity, jint canvasDensity,jint screenDensity) { + if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) { + renderer->drawBitmap(bitmap, matrix, paint); + } else { + // TODO: implement } } @@ -249,7 +258,8 @@ static JNINativeMethod gMethods[] = { { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix }, { "nDrawBitmap", "(IIFFIIII)V", (void*) android_view_GLES20Canvas_drawBitmap }, - { "nDrawBitmap", "(IIFFFFFFFFIIIII)V", (void*) android_view_GLES20Canvas_drawBitmapRect }, + { "nDrawBitmap", "(IIFFFFFFFFIIII)V", (void*) android_view_GLES20Canvas_drawBitmapRect }, + { "nDrawBitmap", "(IIIIIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix }, { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index cef5c107ec86..a82abc93eacf 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -33,6 +33,7 @@ #include "jni.h" #include <android_runtime/AndroidRuntime.h> +#include "android_view_Surface.h" #include <utils/misc.h> @@ -179,7 +180,7 @@ static sp<Surface> getSurface(JNIEnv* env, jobject clazz) return result; } -EGLNativeWindowType android_Surface_getEGLNativeWindow( +sp<ANativeWindow> android_Surface_getNativeWindow( JNIEnv* env, jobject clazz) { return getSurface(env, clazz).get(); } diff --git a/core/jni/android_view_Surface.h b/core/jni/android_view_Surface.h new file mode 100644 index 000000000000..c37932e74c4f --- /dev/null +++ b/core/jni/android_view_Surface.h @@ -0,0 +1,31 @@ +/* + * 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_VIEW_SURFACE_H +#define _ANDROID_VIEW_SURFACE_H + +#include <android/native_window.h> + +#include "jni.h" + +namespace android { + +extern sp<ANativeWindow> android_Surface_getNativeWindow( + JNIEnv* env, jobject clazz); + +} // namespace android + +#endif // _ANDROID_VIEW_SURFACE_H diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index d5cde48b8538..866c038eac43 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -25,10 +25,9 @@ #include <SkBitmap.h> #include <SkPixelRef.h> -namespace android { +#include "android_view_Surface.h" -extern EGLNativeWindowType android_Surface_getEGLNativeWindow( - JNIEnv* env, jobject clazz); +namespace android { static jclass gDisplay_class; static jclass gContext_class; @@ -325,7 +324,7 @@ static jint jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject disp } EGLDisplay dpy = getDisplay(_env, display); EGLContext cnf = getConfig(_env, config); - EGLNativeWindowType window = 0; + sp<ANativeWindow> window; if (native_window == NULL) { not_valid_surface: doThrow(_env, "java/lang/IllegalArgumentException", @@ -333,12 +332,12 @@ not_valid_surface: return 0; } - window = android_Surface_getEGLNativeWindow(_env, native_window); + window = android_Surface_getNativeWindow(_env, native_window); if (window == NULL) goto not_valid_surface; jint* base = beginNativeAttribList(_env, attrib_list); - EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window, base); + EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base); endNativeAttributeList(_env, attrib_list, base); return (jint)sur; } diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java index c86bdfa57812..921ac8a09976 100644 --- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java +++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java @@ -1103,29 +1103,6 @@ public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceT } @SmallTest - public void testLruCachingOfSqliteCompiledSqlObjs() { - mDatabase.execSQL("CREATE TABLE test (i int, j int);"); - mDatabase.execSQL("insert into test values(1,1);"); - // set cache size - int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; - mDatabase.setMaxSqlCacheSize(N); - - // do N+1 queries - and when the 0th entry is removed from LRU cache due to the - // insertion of (N+1)th entry, make sure 0th entry is closed - ArrayList<SQLiteStatement> stmtObjs = new ArrayList<SQLiteStatement>(); - for (int i = 0; i < N+1; i++) { - SQLiteStatement c = mDatabase.compileStatement("select * from test where i = " + i); - c.close(); - stmtObjs.add(i, c); - } - - assertEquals(0, stmtObjs.get(0).getUniqueId()); - for (int i = 1; i < N+1; i++) { - assertTrue(stmtObjs.get(i).getUniqueId() > 0); - } - } - - @SmallTest public void testSetMaxCahesize() { mDatabase.execSQL("CREATE TABLE test (i int, j int);"); mDatabase.execSQL("insert into test values(1,1);"); diff --git a/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java b/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java deleted file mode 100644 index 19c7bcb03cc9..000000000000 --- a/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java +++ /dev/null @@ -1,211 +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 android.database.sqlite; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import junit.framework.TestCase; -import android.test.suitebuilder.annotation.MediumTest; - -/** - * Tests for the most commonly used methods of sql like creating a connection, - * inserting, selecting, updating. - */ -public abstract class AbstractJDBCDriverTest extends TestCase { - - @MediumTest - public void testJDBCDriver() throws Exception { - Connection firstConnection = null; - Connection secondConnection = null; - File dbFile = getDbFile(); - String connectionURL = getConnectionURL(); - Statement firstStmt = null; - Statement secondStmt = null; - try { - Class.forName(getJDBCDriverClassName()); - firstConnection = DriverManager.getConnection(connectionURL); - secondConnection = DriverManager.getConnection(connectionURL); - - String[] ones = {"hello!", "goodbye"}; - short[] twos = {10, 20}; - String[] onesUpdated = new String[ones.length]; - for (int i = 0; i < ones.length; i++) { - onesUpdated[i] = ones[i] + twos[i]; - } - firstStmt = firstConnection.createStatement(); - firstStmt.execute("create table tbl1(one varchar(10), two smallint)"); - secondStmt = secondConnection.createStatement(); - - autoCommitInsertSelectTest(firstStmt, ones, twos); - updateSelectCommitSelectTest(firstStmt, secondStmt, ones, onesUpdated, twos); - updateSelectRollbackSelectTest(firstStmt, secondStmt, onesUpdated, ones, twos); - } finally { - closeConnections(firstConnection, secondConnection, dbFile, firstStmt, secondStmt); - } - } - - protected abstract String getJDBCDriverClassName(); - protected abstract String getConnectionURL(); - protected abstract File getDbFile(); - - private void closeConnections(Connection firstConnection, Connection secondConnection, - File dbFile, Statement firstStmt, Statement secondStmt) { - String failText = null; - try { - if (firstStmt != null) { - firstStmt.execute("drop table tbl1"); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (firstStmt != null) { - firstStmt.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (firstConnection != null) { - firstConnection.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (secondStmt != null) { - secondStmt.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (secondConnection != null) { - secondConnection.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - dbFile.delete(); - assertNull(failText, failText); - } - - /** - * Inserts the values from 'ones' with the values from 'twos' into 'tbl1' - * @param stmt the statement to use for the inserts. - * @param ones the string values to insert into tbl1. - * @param twos the corresponding numerical values to insert into tbl1. - * @throws SQLException in case of a problem during insert. - */ - private void autoCommitInsertSelectTest(Statement stmt, String[] ones, - short[] twos) throws SQLException { - for (int i = 0; i < ones.length; i++) { - stmt.execute("insert into tbl1 values('" + ones[i] + "'," + twos[i] - + ")"); - } - assertAllFromTbl1(stmt, ones, twos); - } - - /** - * Asserts that all values that where added to tbl1 are actually in tbl1. - * @param stmt the statement to use for the select. - * @param ones the string values that where added. - * @param twos the numerical values that where added. - * @throws SQLException in case of a problem during select. - */ - private void assertAllFromTbl1(Statement stmt, String[] ones, short[] twos) - throws SQLException { - ResultSet rs = stmt.executeQuery("select * from tbl1"); - int i = 0; - for (; rs.next(); i++) { - assertTrue(i < ones.length); - assertEquals(ones[i], rs.getString("one")); - assertEquals(twos[i], rs.getShort("two")); - } - assertEquals(i, ones.length); - } - - /** - * Tests the results of an update followed bz a select on a diffrent statement. - * After that the first statement commits its update. and now the second - * statement should also be able to see the changed values in a select. - * @param firstStmt the statement to use for the update and commit. - * @param secondStmt the statement that should be used to check if the commit works - * @param ones the original string values. - * @param onesUpdated the updated string values. - * @param twos the numerical values. - * @throws SQLException in case of a problem during any of the executed commands. - */ - private void updateSelectCommitSelectTest(Statement firstStmt, - Statement secondStmt, String[] ones, String[] onesUpdated, - short[] twos) throws SQLException { - firstStmt.getConnection().setAutoCommit(false); - try { - updateOnes(firstStmt, onesUpdated, twos); - assertAllFromTbl1(secondStmt, ones, twos); - firstStmt.getConnection().commit(); - assertAllFromTbl1(secondStmt, onesUpdated, twos); - } finally { - firstStmt.getConnection().setAutoCommit(true); - } - } - - /** - * Tests if an update followed by a select works. After that a rollback will - * be made and again a select should show that the rollback worked. - * @param firstStmt the statement to use for the update and the rollback - * @param secondStmt the statement to use for checking if the rollback worked as intended. - * @param ones the original string values. - * @param onesUpdated the updated string values. - * @param twos the nomerical values. - * @throws SQLException in case of a problem during any command. - */ - private void updateSelectRollbackSelectTest(Statement firstStmt, - Statement secondStmt, String[] ones, String[] onesUpdated, - short[] twos) throws SQLException { - firstStmt.getConnection().setAutoCommit(false); - try { - updateOnes(firstStmt, onesUpdated, twos); - assertAllFromTbl1(secondStmt, ones, twos); - firstStmt.getConnection().rollback(); - assertAllFromTbl1(secondStmt, ones, twos); - } finally { - firstStmt.getConnection().setAutoCommit(true); - } - } - - /** - * updates the sring values. the original values are stored in 'ones' - * and the updated values in 'ones_updated' - * @param stmt the statement to use for the update. - * @param onesUpdated the new string values. - * @param twos the numerical values. - * @throws SQLException in case of a problem during update. - */ - private void updateOnes(Statement stmt, String[] onesUpdated, short[] twos) - throws SQLException { - for (int i = 0; i < onesUpdated.length; i++) { - stmt.execute("UPDATE tbl1 SET one = '" + onesUpdated[i] - + "' WHERE two = " + twos[i]); - } - } -} diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java index b098b5cc0927..eac9eea28038 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java @@ -18,12 +18,16 @@ package android.database.sqlite; import android.content.Context; import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.Suppress; import android.util.Log; import java.io.File; +import java.util.ArrayList; public class SQLiteDatabaseTest extends AndroidTestCase { private static final String TAG = "DatabaseGeneralTest"; @@ -125,6 +129,7 @@ public class SQLiteDatabaseTest extends AndroidTestCase { * </ul> */ @LargeTest + @Suppress // run this test only if you need to collect the numbers from this test public void testConcurrencyEffectsOfConnPool() throws Exception { // run the test with sqlite WAL enable runConnectionPoolTest(true); @@ -271,4 +276,37 @@ public class SQLiteDatabaseTest extends AndroidTestCase { setCounts(readerNum, numReads); } } + + @SmallTest + public void testLruCachingOfSqliteCompiledSqlObjs() { + mDatabase.execSQL("CREATE TABLE test (i int, j int);"); + mDatabase.execSQL("insert into test values(1,1);"); + // set cache size + int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; + mDatabase.setMaxSqlCacheSize(N); + + // do N+1 queries - and when the 0th entry is removed from LRU cache due to the + // insertion of (N+1)th entry, make sure 0th entry is closed + ArrayList<Integer> stmtObjs = new ArrayList<Integer>(); + ArrayList<String> sqlStrings = new ArrayList<String>(); + SQLiteStatement stmt0 = null; + for (int i = 0; i < N+1; i++) { + String s = "select * from test where i = " + i; + sqlStrings.add(s); + SQLiteStatement c = mDatabase.compileStatement(s); + stmtObjs.add(i, c.getUniqueId()); + if (i == 0) { + // save thie SQLiteStatement obj. we want to make sure it is thrown out of + // the cache and its handle is 0'ed. + stmt0 = c; + } + c.close(); + } + // is 0'th entry out of the cache? + assertEquals(0, stmt0.getUniqueId()); + for (int i = 1; i < N+1; i++) { + SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i)); + assertTrue(stmtObjs.contains(compSql.nStatement)); + } + } } diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java deleted file mode 100644 index 8e677a5b8702..000000000000 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java +++ /dev/null @@ -1,137 +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 android.database.sqlite; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Statement; -import android.test.suitebuilder.annotation.MediumTest; - -/** - * Minimal test for JDBC driver - */ -public class SQLiteJDBCDriverTest extends AbstractJDBCDriverTest { - - private File dbFile; - - @Override - protected void setUp() throws Exception { - super.setUp(); - dbFile = File.createTempFile("sqliteTestDB", null); - } - - @Override - protected void tearDown() throws Exception { - if(dbFile != null) { - dbFile.delete(); - } - super.tearDown(); - } - - @Override - protected String getConnectionURL() { - return "jdbc:sqlite:/" + dbFile; - } - - @Override - protected File getDbFile() { - return dbFile; - } - - @Override - protected String getJDBCDriverClassName() { - return "SQLite.JDBCDriver"; - } - - // Regression test for (Noser) #255: PreparedStatement.executeUpdate results - // in VM crashing with SIGABRT. - @MediumTest - public void test_connection3() throws Exception { - PreparedStatement prst = null; - Statement st = null; - Connection conn = null; - try { - Class.forName("SQLite.JDBCDriver").newInstance(); - if (dbFile.exists()) { - dbFile.delete(); - } - conn = DriverManager.getConnection("jdbc:sqlite:/" - + dbFile.getPath()); - assertNotNull(conn); - - // create table - st = conn.createStatement(); - String sql = "CREATE TABLE zoo (ZID INTEGER NOT NULL, family VARCHAR (20) NOT NULL, name VARCHAR (20) NOT NULL, PRIMARY KEY(ZID) )"; - st.executeUpdate(sql); - - String update = "update zoo set family = ? where name = ?;"; - prst = conn.prepareStatement(update); - prst.setString(1, "cat"); - prst.setString(2, "Yasha"); - // st = conn.createStatement(); - // st.execute("select * from zoo where family = 'cat'"); - // ResultSet rs = st.getResultSet(); - // assertEquals(0, getCount(rs)); - prst.executeUpdate(); - // st.execute("select * from zoo where family = 'cat'"); - // ResultSet rs1 = st.getResultSet(); - // assertEquals(1, getCount(rs1)); - try { - prst = conn.prepareStatement(""); - prst.execute(); - fail("SQLException is not thrown"); - } catch (SQLException e) { - // expected - } - - try { - conn.prepareStatement(null); - fail("NPE is not thrown"); - } catch (Exception e) { - // expected - } - try { - st = conn.createStatement(); - st.execute("drop table if exists zoo"); - - } catch (SQLException e) { - fail("Couldn't drop table: " + e.getMessage()); - } finally { - try { - st.close(); - conn.close(); - } catch (SQLException ee) { - } - } - } finally { - try { - if (prst != null) { - prst.close(); - } - if (st != null) { - st.close(); - } - } catch (SQLException ee) { - } - } - - } - -} diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd index cf2a45716d34..66379a3945d2 100644 --- a/docs/html/guide/developing/eclipse-adt.jd +++ b/docs/html/guide/developing/eclipse-adt.jd @@ -527,7 +527,7 @@ Marking a project as an Android library project. </p> <p>A library project's manifest file must declare all of the shared components that it includes, just as would a standard Android application. For more information, see the documentation for <a -href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <p>For example, the <a href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a> @@ -613,7 +613,8 @@ like this: </p> ... </manifest></pre> -<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +<p>For more information about the manifest file, see the documentation for <a +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <h3 id="considerations">Development considerations</h3> diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd index e8a6fb6494f8..1d67aa90ede4 100644 --- a/docs/html/guide/developing/other-ide.jd +++ b/docs/html/guide/developing/other-ide.jd @@ -687,7 +687,7 @@ so that other applications can use it, you can do so by adding a the <p>A library project's manifest file must declare all of the shared components that it includes, just as would a standard Android application. For more information, see the documentation for <a -href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <p>For example, the <a href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a> @@ -799,7 +799,8 @@ like this: </p> ... </manifest></pre> -<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +<p>For more information about the manifest file, see the documentation for <a +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <h3 id="depAppBuild">Building a dependent application</h3> diff --git a/docs/html/guide/samples/index.jd b/docs/html/guide/samples/index.jd index 2f3ac5ebb6bc..bd9ea52bc6ff 100644 --- a/docs/html/guide/samples/index.jd +++ b/docs/html/guide/samples/index.jd @@ -3,99 +3,13 @@ page.title=Sample Code @jd:body -<p>Sometimes, the best way to learn how things are done is to look at some code. -Here, you can browse the source of some sample Android applications that are included -in the Android SDK.</p> +<script type="text/javascript"> + window.location = toRoot + "resources/samples/index.html"; +</script> -<p>Each version of the Android platform available for the SDK includes a full set of sample -applications (which may vary between different versions of the platform). -You can find the samples in your SDK at:</p> +<p><strong>This document has moved. Please go to <a +href="http://developer.android.com/resources/samples/index.html">List of Sample +Apps</a>.</strong></p> -<p style="margin-left:2em"> -<code><em><sdk></em>/platforms/android-<em><version></em>/samples/</code> -</p> - -<p>You can easily create new Android projects with these samples, modify them -if you'd like, then run them on an emulator or device. For example, to create -a project for the API Demos app from Eclipse, -start a new Android Project, select "Create project from existing source", then select -{@code ApiDemos} in the {@code samples/} directory. To create the API Demos project -using the {@code android} tool, execute:</p> -<pre> -android update project -s -n API Demos -t <em><target_ID></em> -p <em><path-to-platform></em>/samples/ApiDemos/ -</pre> - -<p>The pages below provide an overview of each sample application (available with most -platforms) and allow you to view the source files in your browser. </p> - -<div class="special"> - <p>Some of the samples in this listing are not yet available in the - SDK. While we work to update the SDK, you can - <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP - archive.</p> -</div> - -<dl> - - <dt><a href="{@docRoot}resources/samples/ApiDemos/index.html">API Demos</a></dt> - <dd>A variety of small applications that demonstrate an extensive collection of - framework topics.</dd> - - <dt><a href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a></dt> - <dd>An simple example that illustrates a few different ways for an application to - implement support for the Android data backup and restore mechanism.</dd> - - <dt><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></dt> - <dd>An application for two-way text messaging over Bluetooth.</dd> - - <dt><a href="{@docRoot}resources/samples/ContactManager/index.html">Contact Manager</a></dt> - <dd>An application that demonstrates how to query the system contacts provider - using the <code>ContactsContract</code> API, as - well as insert contacts into a specific account.</dd> - - <dt><a href="{@docRoot}resources/samples/Home/index.html">Home</a></dt> - <dd>A home screen replacement application.</dd> - - <dt><a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a></dt> - <dd>JetBoy is a game that demonstrates the SONiVOX JET interactive music technology, - with {@link android.media.JetPlayer}.</dd> - - <dt><a href="{@docRoot}resources/samples/LunarLander/index.html">Lunar Lander</a></dt> - <dd>A classic Lunar Lander game.</dd> - - <dt><a href="{@docRoot}resources/samples/MultiResolution/index.html">Multiple Resolutions</a></dt> - <dd>A sample application that shows how to use resource directory qualifiers to - provide different resources for different screen configurations.</dd> - - <dt><a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a></dt> - <dd>An application for saving notes. Similar (but not identical) to the - <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd> - - <dt><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a></dt> - <dd>A sample application that demonstrates Android's search framework, - including how to provide search suggestions for Quick Search Box.</dd> - - <dt><a href="{@docRoot}resources/samples/Snake/index.html">Snake</a></dt> - <dd>An implementation of the classic game "Snake."</dd> - - <dt><a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard</a></dt> - <dd>An example of writing an input method for a software keyboard.</dd> - - <dt><a href=""{@docRoot}resources/samples/Wiktionary/index.html">Wiktionary</a></dt> - <dd>An example of creating interactive widgets for display on the Android - home screen.</dd> - - <dt><a href="{@docRoot}resources/samples/WiktionarySimple/index.html">Wiktionary (Simplified)</a></dt> - <dd>A simple Android home screen widgets example.</dd> - -</dl> - - -<div class="special"> -<p>For more sample applications, check out -<a href="http://code.google.com/p/apps-for-android/">apps-for-android</a>, a -collection of open source applications that demonstrate various Android APIs. -</p> -</div> diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd index aeefca8f9553..6c02031fd4f5 100644 --- a/docs/html/guide/topics/data/backup.jd +++ b/docs/html/guide/topics/data/backup.jd @@ -240,7 +240,7 @@ Backup Service Key is ignored.</p> <h2 id="BackupAgent">Extending BackupAgent</h2> <p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class -directly, but should instead <a href="BackupAgentHelper">extend BackupAgentHelper</a> to take +directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take advantage of the built-in helper classes that automatically backup and restore your files. However, you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p> <ul> @@ -262,7 +262,7 @@ create your table and insert the data during a restore operation.</li> <p>If you don't need to perform any of the tasks above and want to back up complete files from {@link android.content.SharedPreferences} or <a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you -should skip to <a href="BackupAgentHelper">Extending BackupAgentHelper</a>.</p> +should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p> diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index cac85e835fe0..7e2f8a0cea04 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -761,7 +761,7 @@ Android runs your application, it will crash if you do not provide default resou cannot use the resources named with the new qualifier. For example, if your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> is set to 4, and you qualify all of your drawable resources using <a -href="NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API +href="#NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API Level 8), then an API Level 4 device cannot access your drawable resources and will crash. In this case, you probably want {@code notnight} to be your default resources, so you should exclude that qualifier so your drawable resources are in either {@code drawable/} or {@code drawable-night/}.</p> diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd index 5e751057e17d..6cb7228016b5 100644 --- a/docs/html/resources/dashboard/platform-versions.jd +++ b/docs/html/resources/dashboard/platform-versions.jd @@ -43,29 +43,73 @@ the development of your application features for the devices currently in the hands of users. For information about how to target your application to devices based on platform version, see <a href="{@docRoot}guide/appendix/api-levels.html">API Levels</a>.</p> -<p class="note"><strong>Note:</strong> This data is based on the number -of Android devices that have accessed Android Market within a 14-day period -ending on the data collection date noted below.</p> + +<h3 id="Current">Current Distribution</h3> + +<p>The following pie chart and table is based on the number of Android devices that have accessed +Android Market within a 14-day period ending on the data collection date noted below.</p> <div class="dashboard-panel"> -<img alt="" width="460" height="250" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.1,24.6,25.0,0.1,0.3,50.0&chl= -Android%201.1|Android%201.5|Android%201.6|Android%202.0|Android%202.0.1|Android%202.1&chco=c4df9b, -6fad0c" /> +<img alt="" height="250" width="460" +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.3,21.3,23.5,53.1,1.8&chl=Other*| +Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" /> <table> <tr> - <th>Android Platform</th> - <th>Percent of Devices</th> + <th>Platform</th> + <th>API Level</th> + <th>Distribution</th> </tr> -<tr><td>Android 1.1</td><td>0.1%</td></tr> -<tr><td>Android 1.5</td><td>24.6%</td></tr> -<tr><td>Android 1.6</td><td>25.0%</td></tr> -<tr><td>Android 2.0</td><td>0.1%</td></tr> -<tr><td>Android 2.0.1</td><td>0.3%</td></tr> -<tr><td>Android 2.1</td><td>50.0%</td></tr> +<tr><td>Android 1.5</td><td>3</td><td>21.3%</td></tr> +<tr><td>Android 1.6</td><td>4</td><td>23.5%</td></tr> +<tr><td>Android 2.1</td><td>7</td><td>53.1%</td></tr> +<tr><td>Android 2.2</td><td>8</td><td>1.8%</td></tr> </table> -<p><em>Data collected during two weeks ending on June 16, 2010</em></p> -</div> + +<p><em>Data collected during two weeks ending on July 1, 2010</em></p> +<p style="font-size:.9em">* <em>Other: 0.3% of devices running obsolete versions</em></p> + +</div><!-- end dashboard-panel --> + + +<h3 id="Historical">Historical Distribution</h3> + +<p>The following stacked line graph provides a history of the relative number of +active Android devices running different versions of the Android platform. It also provides a +valuable perspective of how many devices your application is compatible with, based on the +platform version.</p> + +<p>Notice that the platform versions are stacked on top of each other with the oldest active +version at the top. This format indicates the total percent of active devices that are compatible +with a given version of Android. For example, if you develop your application for +the version that is at the very top of the chart, then your application is +compatible with 100% of active devices (and all future versions), because all Android APIs are +forward compatible. Or, if you develop your application for a version lower on the chart, +then it is currently compatible with the percentage of devices indicated on the y-axis, where the +line for that version meets the y-axis on the right.</p> + +<p>Each dataset in the timeline is based on the number of Android devices that accessed +Android Market within a 14-day period ending on the date indicated on the x-axis.</p> + +<div class="dashboard-panel"> + +<img alt="" height="265" width="700" style="padding:5px;background:#fff" +src="http://chart.apis.google.com/chart?&cht=lc&chs=700x265&chxt=x,y,r&chxr=0,0,10%7C1,0,100%7C2,0, +100&chxl=0%3A%7C2010/02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15% +7C2010/07/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25% +7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10&chxtc=0,5&chd=t:99.0,99.2,99.4,99.5,99.6,99.6,99.6,99.7,100.6 +,101.1,99.9%7C63.4,62.5,61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6%7C22.6,23.2,24.3,25.4,29.4,30.2 +,32.7,35.3,46.2,51.3,55.1%7C0.0,0.0,0.0,0.0,4.0,28.3,32.0,34.9,45.9,51.0,54.9%7C0.0,0.0,0.0,0.0,0.0, +0.0,0.0,0.0,0.8,1.2,1.8&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5%7Cb,c3df9b,0,1,0%7CtAndroid%201.6, +638d23,1,0,15,,t::-5%7Cb,b0db6e,1,2,0%7CtAndroid%202.0.1,496c13,2,0,15,,t::-5%7Cb,9ddb3d,2,3,0% +7CtAndroid%202.1,2f4708,3,5,15,,t::-5%7Cb,89cf19,3,4,0%7CB,6fad0c,4,5,0&chg=9,25&chdl=Android%201.5% +20(API%20Level%203)%7CAndroid%201.6%20(API%20Level%204)%7CAndroid%202.0.1%20(API%20Level%206)% +7CAndroid%202.1%20(API%20Level%207)%7CAndroid%202.2%20(API%20Level %208)&chco=add274, +9ad145,84c323,6ba213,507d08" /> + +<p><em>Last historical dataset collected during two weeks ending on July 1, 2010</em></p> + + +</div><!-- end dashboard-panel --> diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd index f8130ea50e9d..89fdd2d30352 100644 --- a/docs/html/resources/dashboard/screens.jd +++ b/docs/html/resources/dashboard/screens.jd @@ -49,7 +49,7 @@ ending on the data collection date noted below.</p> <div class="dashboard-panel"> <img alt="" width="460" height="250" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,57.8,41.0&chl=Small%20/%20ldpi| +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.4,54.5,44.1&chl=Small%20/%20ldpi| Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> <table> @@ -60,14 +60,14 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> <th scope="col">High Density</th> </tr> <tr><th scope="row">Small</th> -<td class='cent hi'>1.1%</td> +<td class='cent hi'>1.4%</td> <td></td> <td></td> </tr> <tr><th scope="row">Normal</th> <td></td> -<td class='cent hi'>57.8%</td> -<td class='cent hi'>41.0%</td> +<td class='cent hi'>54.5%</td> +<td class='cent hi'>44.1%</td> </tr> <tr><th scope="row">Large</th> <td></td> @@ -76,6 +76,6 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> </tr> </table> -<p><em>Data collected during two weeks ending on June 16, 2010</em></p> +<p><em>Data collected during two weeks ending on July 1, 2010</em></p> </div> diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd index c3ba49566e70..77f7e43783b6 100644 --- a/docs/html/sdk/older_releases.jd +++ b/docs/html/sdk/older_releases.jd @@ -47,7 +47,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.6_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.6_r1.zip">android-sdk- windows-1 .6_r1.zip</a> </td> <td>260529085 bytes</td> @@ -57,7 +57,7 @@ windows-1 .6_r1.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk- mac_x86-1 .6_r1.zip</a> </td> <td>247412515 bytes</td> @@ -67,7 +67,7 @@ mac_x86-1 .6_r1.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.6_r1.tgz">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.6_r1.tgz">android- sdk- linux_x86-1.6_r1.tgz</a> </td> <td>238224860 bytes</td> @@ -92,7 +92,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r3.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r3.zip">android-sdk- windows-1 .5_r3.zip</a> </td> <td>191477853 bytes</td> @@ -102,7 +102,7 @@ windows-1 .5_r3.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk- mac_x86-1 .5_r3.zip</a> </td> <td>183024673 bytes</td> @@ -112,7 +112,7 @@ mac_x86-1 .5_r3.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r3.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r3.zip">android- sdk- linux_x86-1.5_r3.zip</a> </td> <td>178117561 bytes</td> @@ -137,7 +137,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.1_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.1_r1.zip">android-sdk- windows-1 .1_r1.zip</a> </td> @@ -148,7 +148,7 @@ windows-1 <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk- mac_x86-1 .1_r1.zip</a> </td> @@ -159,7 +159,7 @@ mac_x86-1 <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.1_r1.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.1_r1.zip">android- sdk- linux_x86-1.1_r1.zip</a> </td> @@ -185,7 +185,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r2.zip">android-sdk- windows-1 .0_r2.zip</a> </td> @@ -196,7 +196,7 @@ windows-1 <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk- mac_x86-1 .0_r2.zip</a> </td> @@ -207,7 +207,7 @@ mac_x86-1 <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r2.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r2.zip">android- sdk- linux_x86-1.0_r2.zip</a> </td> @@ -241,7 +241,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r2.zip">android-sdk- windows-1 .5_r2.zip</a> </td> <td>178346828 bytes</td> @@ -251,7 +251,7 @@ windows-1 .5_r2.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk- mac_x86-1 .5_r2.zip</a> </td> <td>169945128 bytes</td> @@ -261,7 +261,7 @@ mac_x86-1 .5_r2.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r2.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r2.zip">android- sdk- linux_x86-1.5_r2.zip</a> </td> <td>165035130 bytes</td> @@ -286,7 +286,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r1.zip">android-sdk- windows-1 .5_r1.zip</a> </td> <td>176263368 bytes</td> @@ -296,7 +296,7 @@ windows-1 .5_r1.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk- mac_x86-1 .5_r1.zip</a> </td> <td>167848675 bytes</td> @@ -306,7 +306,7 @@ mac_x86-1 .5_r1.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r1.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r1.zip">android- sdk- linux_x86-1.5_r1.zip</a> </td> <td>162938845 bytes</td> @@ -331,7 +331,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r1.zip">android-sdk- windows-1 .0_r1.zip</a> </td> <td>89.7 MB bytes</td> @@ -341,7 +341,7 @@ windows-1 .0_r1.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk- mac_x86-1 .0_r1.zip</a> </td> <td>87.5 MB bytes</td> @@ -351,7 +351,7 @@ mac_x86-1 .0_r1.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r1.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r1.zip">android- sdk- linux_x86-1.0_r1.zip</a> </td> <td>87.8 MB bytes</td> diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java index fb36f1faa401..3b3711bb320f 100644 --- a/graphics/java/android/renderscript/FileA3D.java +++ b/graphics/java/android/renderscript/FileA3D.java @@ -36,7 +36,6 @@ public class FileA3D extends BaseObj { UNKNOWN, MESH, - SIMPLE_MESH, TYPE, ELEMENT, ALLOCATION, @@ -89,10 +88,7 @@ public class FileA3D extends BaseObj { switch (mClassID) { case MESH: - mLoadedObj = null; - break; - case SIMPLE_MESH: - mLoadedObj = new SimpleMesh(objectID, mRS); + mLoadedObj = new Mesh(objectID, mRS); break; case TYPE: mLoadedObj = new Type(objectID, mRS); diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java new file mode 100644 index 000000000000..5a538787d601 --- /dev/null +++ b/graphics/java/android/renderscript/Mesh.java @@ -0,0 +1,447 @@ +/* + * 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 android.renderscript; + +import java.util.Vector; + +import android.util.Config; +import android.util.Log; + +/** + * @hide + * + **/ +public class Mesh extends BaseObj { + + Allocation[] mVertexBuffers; + Allocation[] mIndexBuffers; + Primitive[] mPrimitives; + + Mesh(int id, RenderScript rs) { + super(rs); + mID = id; + } + + public int getVertexAllocationCount() { + if(mVertexBuffers == null) { + return 0; + } + return mVertexBuffers.length; + } + public Allocation getVertexAllocation(int slot) { + return mVertexBuffers[slot]; + } + + public int getPrimitiveCount() { + if(mIndexBuffers == null) { + return 0; + } + return mIndexBuffers.length; + } + public Allocation getIndexAllocation(int slot) { + return mIndexBuffers[slot]; + } + public Primitive getPrimitive(int slot) { + return mPrimitives[slot]; + } + + public static class Builder { + RenderScript mRS; + + class Entry { + Type t; + Element e; + int size; + Primitive prim; + } + + int mVertexTypeCount; + Entry[] mVertexTypes; + Vector mIndexTypes; + + public Builder(RenderScript rs) { + mRS = rs; + mVertexTypeCount = 0; + mVertexTypes = new Entry[16]; + mIndexTypes = new Vector(); + } + + public int addVertexType(Type t) throws IllegalStateException { + if (mVertexTypeCount >= mVertexTypes.length) { + throw new IllegalStateException("Max vertex types exceeded."); + } + + int addedIndex = mVertexTypeCount; + mVertexTypes[mVertexTypeCount] = new Entry(); + mVertexTypes[mVertexTypeCount].t = t; + mVertexTypes[mVertexTypeCount].e = null; + mVertexTypeCount++; + return addedIndex; + } + + public int addVertexType(Element e, int size) throws IllegalStateException { + if (mVertexTypeCount >= mVertexTypes.length) { + throw new IllegalStateException("Max vertex types exceeded."); + } + + int addedIndex = mVertexTypeCount; + mVertexTypes[mVertexTypeCount] = new Entry(); + mVertexTypes[mVertexTypeCount].t = null; + mVertexTypes[mVertexTypeCount].e = e; + mVertexTypes[mVertexTypeCount].size = size; + mVertexTypeCount++; + return addedIndex; + } + + public int addIndexType(Type t, Primitive p) { + int addedIndex = mIndexTypes.size(); + Entry indexType = new Entry(); + indexType.t = t; + indexType.e = null; + indexType.size = 0; + indexType.prim = p; + mIndexTypes.addElement(indexType); + return addedIndex; + } + + public int addIndexType(Primitive p) { + int addedIndex = mIndexTypes.size(); + Entry indexType = new Entry(); + indexType.t = null; + indexType.e = null; + indexType.size = 0; + indexType.prim = p; + mIndexTypes.addElement(indexType); + return addedIndex; + } + + public int addIndexType(Element e, int size, Primitive p) { + int addedIndex = mIndexTypes.size(); + Entry indexType = new Entry(); + indexType.t = null; + indexType.e = e; + indexType.size = size; + indexType.prim = p; + mIndexTypes.addElement(indexType); + return addedIndex; + } + + Type newType(Element e, int size) { + Type.Builder tb = new Type.Builder(mRS, e); + tb.add(Dimension.X, size); + return tb.create(); + } + + static synchronized Mesh internalCreate(RenderScript rs, Builder b) { + + int id = rs.nMeshCreate(b.mVertexTypeCount, b.mIndexTypes.size()); + Mesh newMesh = new Mesh(id, rs); + newMesh.mIndexBuffers = new Allocation[b.mIndexTypes.size()]; + newMesh.mPrimitives = new Primitive[b.mIndexTypes.size()]; + newMesh.mVertexBuffers = new Allocation[b.mVertexTypeCount]; + + for(int ct = 0; ct < b.mIndexTypes.size(); ct ++) { + Allocation alloc = null; + Entry entry = (Entry)b.mIndexTypes.elementAt(ct); + if (entry.t != null) { + alloc = Allocation.createTyped(rs, entry.t); + } + else if(entry.e != null) { + alloc = Allocation.createSized(rs, entry.e, entry.size); + } + int allocID = (alloc == null) ? 0 : alloc.getID(); + rs.nMeshBindIndex(id, allocID, entry.prim.mID, ct); + newMesh.mIndexBuffers[ct] = alloc; + newMesh.mPrimitives[ct] = entry.prim; + } + + for(int ct = 0; ct < b.mVertexTypeCount; ct ++) { + Allocation alloc = null; + Entry entry = b.mVertexTypes[ct]; + if (entry.t != null) { + alloc = Allocation.createTyped(rs, entry.t); + } else if(entry.e != null) { + alloc = Allocation.createSized(rs, entry.e, entry.size); + } + rs.nMeshBindVertex(id, alloc.getID(), ct); + newMesh.mVertexBuffers[ct] = alloc; + } + + return newMesh; + } + + public Mesh create() { + mRS.validate(); + Mesh sm = internalCreate(mRS, this); + return sm; + } + } + + public static class AllocationBuilder { + RenderScript mRS; + + class Entry { + Allocation a; + Primitive prim; + } + + int mVertexTypeCount; + Entry[] mVertexTypes; + + Vector mIndexTypes; + + public AllocationBuilder(RenderScript rs) { + mRS = rs; + mVertexTypeCount = 0; + mVertexTypes = new Entry[16]; + mIndexTypes = new Vector(); + } + + public int addVertexAllocation(Allocation a) throws IllegalStateException { + if (mVertexTypeCount >= mVertexTypes.length) { + throw new IllegalStateException("Max vertex types exceeded."); + } + + int addedIndex = mVertexTypeCount; + mVertexTypes[mVertexTypeCount] = new Entry(); + mVertexTypes[mVertexTypeCount].a = a; + mVertexTypeCount++; + return addedIndex; + } + + public int addIndexAllocation(Allocation a, Primitive p) { + int addedIndex = mIndexTypes.size(); + Entry indexType = new Entry(); + indexType.a = a; + indexType.prim = p; + mIndexTypes.addElement(indexType); + return addedIndex; + } + + public int addIndexType(Primitive p) { + int addedIndex = mIndexTypes.size(); + Entry indexType = new Entry(); + indexType.a = null; + indexType.prim = p; + mIndexTypes.addElement(indexType); + return addedIndex; + } + + static synchronized Mesh internalCreate(RenderScript rs, AllocationBuilder b) { + + int id = rs.nMeshCreate(b.mVertexTypeCount, b.mIndexTypes.size()); + Mesh newMesh = new Mesh(id, rs); + newMesh.mIndexBuffers = new Allocation[b.mIndexTypes.size()]; + newMesh.mPrimitives = new Primitive[b.mIndexTypes.size()]; + newMesh.mVertexBuffers = new Allocation[b.mVertexTypeCount]; + + for(int ct = 0; ct < b.mIndexTypes.size(); ct ++) { + Entry entry = (Entry)b.mIndexTypes.elementAt(ct); + int allocID = (entry.a == null) ? 0 : entry.a.getID(); + rs.nMeshBindIndex(id, allocID, entry.prim.mID, ct); + newMesh.mIndexBuffers[ct] = entry.a; + newMesh.mPrimitives[ct] = entry.prim; + } + + for(int ct = 0; ct < b.mVertexTypeCount; ct ++) { + Entry entry = b.mVertexTypes[ct]; + rs.nMeshBindVertex(id, entry.a.mID, ct); + newMesh.mVertexBuffers[ct] = entry.a; + } + + return newMesh; + } + + public Mesh create() { + mRS.validate(); + Mesh sm = internalCreate(mRS, this); + return sm; + } + } + + + public static class TriangleMeshBuilder { + float mVtxData[]; + int mVtxCount; + short mIndexData[]; + int mIndexCount; + RenderScript mRS; + Element mElement; + + float mNX = 0; + float mNY = 0; + float mNZ = -1; + float mS0 = 0; + float mT0 = 0; + float mR = 1; + float mG = 1; + float mB = 1; + float mA = 1; + + int mVtxSize; + int mFlags; + + public static final int COLOR = 0x0001; + public static final int NORMAL = 0x0002; + public static final int TEXTURE_0 = 0x0100; + + public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) { + mRS = rs; + mVtxCount = 0; + mIndexCount = 0; + mVtxData = new float[128]; + mIndexData = new short[128]; + mVtxSize = vtxSize; + mFlags = flags; + + if (vtxSize < 2 || vtxSize > 3) { + throw new IllegalArgumentException("Vertex size out of range."); + } + } + + private void makeSpace(int count) { + if ((mVtxCount + count) >= mVtxData.length) { + float t[] = new float[mVtxData.length * 2]; + System.arraycopy(mVtxData, 0, t, 0, mVtxData.length); + mVtxData = t; + } + } + + private void latch() { + if ((mFlags & COLOR) != 0) { + makeSpace(4); + mVtxData[mVtxCount++] = mR; + mVtxData[mVtxCount++] = mG; + mVtxData[mVtxCount++] = mB; + mVtxData[mVtxCount++] = mA; + } + if ((mFlags & TEXTURE_0) != 0) { + makeSpace(2); + mVtxData[mVtxCount++] = mS0; + mVtxData[mVtxCount++] = mT0; + } + if ((mFlags & NORMAL) != 0) { + makeSpace(3); + mVtxData[mVtxCount++] = mNX; + mVtxData[mVtxCount++] = mNY; + mVtxData[mVtxCount++] = mNZ; + } + } + + public void addVertex(float x, float y) { + if (mVtxSize != 2) { + throw new IllegalStateException("add mistmatch with declared components."); + } + makeSpace(2); + mVtxData[mVtxCount++] = x; + mVtxData[mVtxCount++] = y; + latch(); + } + + public void addVertex(float x, float y, float z) { + if (mVtxSize != 3) { + throw new IllegalStateException("add mistmatch with declared components."); + } + makeSpace(3); + mVtxData[mVtxCount++] = x; + mVtxData[mVtxCount++] = y; + mVtxData[mVtxCount++] = z; + latch(); + } + + public void setTexture(float s, float t) { + if ((mFlags & TEXTURE_0) == 0) { + throw new IllegalStateException("add mistmatch with declared components."); + } + mS0 = s; + mT0 = t; + } + + public void setNormal(float x, float y, float z) { + if ((mFlags & NORMAL) == 0) { + throw new IllegalStateException("add mistmatch with declared components."); + } + mNX = x; + mNY = y; + mNZ = z; + } + + public void setColor(float r, float g, float b, float a) { + if ((mFlags & COLOR) == 0) { + throw new IllegalStateException("add mistmatch with declared components."); + } + mR = r; + mG = g; + mB = b; + mA = a; + } + + public void addTriangle(int idx1, int idx2, int idx3) { + if((idx1 >= mVtxCount) || (idx1 < 0) || + (idx2 >= mVtxCount) || (idx2 < 0) || + (idx3 >= mVtxCount) || (idx3 < 0)) { + throw new IllegalStateException("Index provided greater than vertex count."); + } + if ((mIndexCount + 3) >= mIndexData.length) { + short t[] = new short[mIndexData.length * 2]; + System.arraycopy(mIndexData, 0, t, 0, mIndexData.length); + mIndexData = t; + } + mIndexData[mIndexCount++] = (short)idx1; + mIndexData[mIndexCount++] = (short)idx2; + mIndexData[mIndexCount++] = (short)idx3; + } + + public Mesh create(boolean uploadToBufferObject) { + Element.Builder b = new Element.Builder(mRS); + int floatCount = mVtxSize; + b.add(Element.createVector(mRS, + Element.DataType.FLOAT_32, + mVtxSize), "position"); + if ((mFlags & COLOR) != 0) { + floatCount += 4; + b.add(Element.F32_4(mRS), "color"); + } + if ((mFlags & TEXTURE_0) != 0) { + floatCount += 2; + b.add(Element.F32_2(mRS), "texture0"); + } + if ((mFlags & NORMAL) != 0) { + floatCount += 3; + b.add(Element.F32_3(mRS), "normal"); + } + mElement = b.create(); + + Builder smb = new Builder(mRS); + smb.addVertexType(mElement, mVtxCount / floatCount); + smb.addIndexType(Element.U16(mRS), mIndexCount, Primitive.TRIANGLE); + + Mesh sm = smb.create(); + + sm.getVertexAllocation(0).data(mVtxData); + if(uploadToBufferObject) { + sm.getVertexAllocation(0).uploadToBufferObject(); + } + + sm.getIndexAllocation(0).data(mIndexData); + sm.getIndexAllocation(0).uploadToBufferObject(); + + return sm; + } + } +} + diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index b2e5b02cadec..c7e8ca5929bc 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -186,6 +186,10 @@ public class RenderScript { native void nLightSetColor(int l, float r, float g, float b); native void nLightSetPosition(int l, float x, float y, float z); + native int nMeshCreate(int vtxCount, int indexCount); + native void nMeshBindVertex(int id, int alloc, int slot); + native void nMeshBindIndex(int id, int alloc, int prim, int slot); + native int nSimpleMeshCreate(int batchID, int idxID, int[] vtxID, int prim); native void nSimpleMeshBindVertex(int id, int alloc, int slot); native void nSimpleMeshBindIndex(int id, int alloc); diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index c61a21595084..a6d2489c2944 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -1351,6 +1351,33 @@ nLightSetPosition(JNIEnv *_env, jobject _this, jint light, float x, float y, flo // --------------------------------------------------------------------------- static jint +nMeshCreate(JNIEnv *_env, jobject _this, jint vtxCount, jint idxCount) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshCreate, con(%p), vtxCount(%i), idxCount(%i)", con, vtxCount, idxCount); + int id = (int)rsMeshCreate(con, vtxCount, idxCount); + return id; +} + +static void +nMeshBindVertex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint slot) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshBindVertex, con(%p), Mesh(%p), Alloc(%p), slot(%i)", con, (RsMesh)s, (RsAllocation)alloc, slot); + rsMeshBindVertex(con, (RsMesh)s, (RsAllocation)alloc, slot); +} + +static void +nMeshBindIndex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint primID, jint slot) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nMeshBindIndex, con(%p), Mesh(%p), Alloc(%p)", con, (RsMesh)s, (RsAllocation)alloc); + rsMeshBindIndex(con, (RsMesh)s, (RsAllocation)alloc, primID, slot); +} + +// --------------------------------------------------------------------------- + +static jint nSimpleMeshCreate(JNIEnv *_env, jobject _this, jint batchID, jint indexID, jintArray vtxIDs, jint primID) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); @@ -1367,16 +1394,16 @@ static void nSimpleMeshBindVertex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint slot) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nSimpleMeshBindVertex, con(%p), SimpleMesh(%p), Alloc(%p), slot(%i)", con, (RsSimpleMesh)s, (RsAllocation)alloc, slot); - rsSimpleMeshBindVertex(con, (RsSimpleMesh)s, (RsAllocation)alloc, slot); + LOG_API("nSimpleMeshBindVertex, con(%p), Mesh(%p), Alloc(%p), slot(%i)", con, (RsMesh)s, (RsAllocation)alloc, slot); + rsSimpleMeshBindVertex(con, (RsMesh)s, (RsAllocation)alloc, slot); } static void nSimpleMeshBindIndex(JNIEnv *_env, jobject _this, jint s, jint alloc) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nSimpleMeshBindIndex, con(%p), SimpleMesh(%p), Alloc(%p)", con, (RsSimpleMesh)s, (RsAllocation)alloc); - rsSimpleMeshBindIndex(con, (RsSimpleMesh)s, (RsAllocation)alloc); + LOG_API("nSimpleMeshBindIndex, con(%p), Mesh(%p), Alloc(%p)", con, (RsMesh)s, (RsAllocation)alloc); + rsSimpleMeshBindIndex(con, (RsMesh)s, (RsAllocation)alloc); } // --------------------------------------------------------------------------- @@ -1513,6 +1540,10 @@ static JNINativeMethod methods[] = { {"nSimpleMeshBindVertex", "(III)V", (void*)nSimpleMeshBindVertex }, {"nSimpleMeshBindIndex", "(II)V", (void*)nSimpleMeshBindIndex }, +{"nMeshCreate", "(II)I", (void*)nMeshCreate }, +{"nMeshBindVertex", "(III)V", (void*)nMeshBindVertex }, +{"nMeshBindIndex", "(IIII)V", (void*)nMeshBindIndex }, + }; static int registerFuncs(JNIEnv *_env) diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h index 3192d03a0643..fd30ba58d4e9 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -35,6 +35,10 @@ public: static CameraSource *Create(); static CameraSource *CreateFromCamera(const sp<Camera> &camera); + void enableTimeLapseMode( + int64_t timeBetweenTimeLapseFrameCaptureUs, int32_t videoFrameRate); + void disableTimeLapseMode(); + virtual ~CameraSource(); virtual status_t start(MetaData *params = NULL); @@ -71,6 +75,14 @@ private: bool mCollectStats; bool mStarted; + // Time between capture of two frames during time lapse recording + // Negative value indicates that timelapse is disabled. + int64_t mTimeBetweenTimeLapseFrameCaptureUs; + // Time between two frames in final video (1/frameRate) + int64_t mTimeBetweenTimeLapseVideoFramesUs; + // Real timestamp of the last encoded time lapse frame + int64_t mLastTimeLapseFrameRealTimestampUs; + CameraSource(const sp<Camera> &camera); void dataCallbackTimestamp( diff --git a/include/media/stagefright/foundation/ALooper.h b/include/media/stagefright/foundation/ALooper.h index 69ad837413be..194f1fc92529 100644 --- a/include/media/stagefright/foundation/ALooper.h +++ b/include/media/stagefright/foundation/ALooper.h @@ -39,7 +39,10 @@ struct ALooper : public RefBase { handler_id registerHandler(const sp<AHandler> &handler); void unregisterHandler(handler_id handlerID); - status_t start(bool runOnCallingThread = false); + status_t start( + bool runOnCallingThread = false, + bool canCallJava = false); + status_t stop(); static int64_t GetNowUs(); diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h index 139c6205529f..c674cbaf3471 100644 --- a/include/media/stagefright/foundation/AMessage.h +++ b/include/media/stagefright/foundation/AMessage.h @@ -60,6 +60,8 @@ struct AMessage : public RefBase { sp<AMessage> dup() const; + AString debugString(int32_t indent = 0) const; + protected: virtual ~AMessage(); diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h index 4a3ca77571a5..56693dac736e 100644 --- a/libs/hwui/GenerationCache.h +++ b/libs/hwui/GenerationCache.h @@ -33,9 +33,13 @@ public: template<typename K, typename V> class GenerationCache { public: - GenerationCache(unsigned int maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { }; + GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { }; ~GenerationCache() { clear(); }; + enum Capacity { + kUnlimitedCapacity, + }; + void setOnEntryRemovedListener(OnEntryRemoved<K*, V*>* listener); void clear(); @@ -44,12 +48,11 @@ public: V* get(K* key); void put(K* key, V* value); V* remove(K* key); + void removeOldest(); - unsigned int size() const; + uint32_t size() const; private: - void removeOldest(); - template<typename EntryKey, typename EntryValue> struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > { Entry() { } @@ -69,7 +72,7 @@ private: void attachToCache(sp<Entry<K*, V*> > entry); void detachFromCache(sp<Entry<K*, V*> > entry); - unsigned int mMaxCapacity; + uint32_t mMaxCapacity; OnEntryRemoved<K*, V*>* mListener; @@ -80,7 +83,7 @@ private: }; // class GenerationCache template<typename K, typename V> -unsigned int GenerationCache<K, V>::size() const { +uint32_t GenerationCache<K, V>::size() const { return mCache.size(); } @@ -124,7 +127,7 @@ V* GenerationCache<K, V>::get(K* key) { template<typename K, typename V> void GenerationCache<K, V>::put(K* key, V* value) { - if (mCache.size() >= mMaxCapacity) { + if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { removeOldest(); } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 091abb058c9e..9df1c6765c50 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -22,6 +22,7 @@ #include <SkCanvas.h> +#include <cutils/properties.h> #include <utils/Log.h> #include "OpenGLRenderer.h" @@ -33,7 +34,15 @@ namespace uirenderer { // Defines /////////////////////////////////////////////////////////////////////////////// -#define MAX_TEXTURE_COUNT 128 +// These properties are defined in mega-bytes +#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size" +#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size" + +// Converts a number of mega-bytes into bytes +#define MB(s) s * 1024 * 1024 + +#define DEFAULT_TEXTURE_CACHE_SIZE MB(20) +#define DEFAULT_LAYER_CACHE_SIZE MB(10) #define SV(x, y) { { x, y } } #define FV(x, y, u, v) { { x, y }, { u, v } } @@ -83,9 +92,14 @@ static const Blender gBlends[] = { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -OpenGLRenderer::OpenGLRenderer(): mTextureCache(MAX_TEXTURE_COUNT) { +OpenGLRenderer::OpenGLRenderer(): mTextureCache(DEFAULT_TEXTURE_CACHE_SIZE) { LOGD("Create OpenGLRenderer"); + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { + mTextureCache.setMaxSize(MB(atoi(property))); + } + mDrawColorShader = new DrawColorProgram; mDrawTextureShader = new DrawTextureProgram; @@ -111,6 +125,7 @@ void OpenGLRenderer::setViewport(int width, int height) { mWidth = width; mHeight = height; + mFirstSnapshot.height = height; } void OpenGLRenderer::prepare() { @@ -170,10 +185,15 @@ int OpenGLRenderer::saveSnapshot() { bool OpenGLRenderer::restoreSnapshot() { bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; + bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; sp<Snapshot> current = mSnapshot; sp<Snapshot> previous = mSnapshot->previous; + if (restoreOrtho) { + memcpy(mOrthoMatrix, current->orthoMatrix, sizeof(mOrthoMatrix)); + } + if (restoreLayer) { composeLayer(current, previous); } @@ -197,21 +217,11 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // The texture is currently as big as the window but drawn with // a quad of the appropriate size const Rect& layer = current->layer; - Rect texCoords(current->layer); - mSnapshot->transform.mapRect(texCoords); - - const float u1 = texCoords.left / float(mWidth); - const float v1 = (mHeight - texCoords.top) / float(mHeight); - const float u2 = texCoords.right / float(mWidth); - const float v2 = (mHeight - texCoords.bottom) / float(mHeight); - - resetDrawTextureTexCoords(u1, v1, u2, v2); drawTextureRect(layer.left, layer.top, layer.right, layer.bottom, current->texture, current->alpha, current->mode, true, true); - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); - + // TODO Don't delete these things, but cache them glDeleteFramebuffers(1, ¤t->fbo); glDeleteTextures(1, ¤t->texture); } @@ -268,14 +278,10 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time - // TODO ***** IMPORTANT ***** - // Creating a texture-backed FBO works only if the texture is the same size - // as the original rendering buffer (in this case, mWidth and mHeight.) - // This is expensive and wasteful and must be fixed. - // TODO Additionally we should use an FBO cache + // TODO Use an FBO cache - const GLsizei width = mWidth; //right - left; - const GLsizei height = mHeight; //bottom - right; + const GLsizei width = right - left; + const GLsizei height = bottom - top; const GLint format = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) ? GL_RGBA : GL_RGB; glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); @@ -287,7 +293,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - LOGD("Framebuffer incomplete %d", status); + LOGD("Framebuffer incomplete (GL error code 0x%x)", status); glDeleteFramebuffers(1, &snapshot->fbo); glDeleteTextures(1, &snapshot->texture); @@ -295,11 +301,34 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return false; } + // Clear the FBO + glDisable(GL_SCISSOR_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_SCISSOR_TEST); + snapshot->flags |= Snapshot::kFlagIsLayer; snapshot->mode = mode; snapshot->alpha = alpha / 255.0f; snapshot->layer.set(left, top, right, bottom); + // Creates a new snapshot to draw into the FBO + saveSnapshot(); + // TODO: This doesn't preserve other transformations (check Skia first) + mSnapshot->transform.loadTranslate(-left, -top, 0.0f); + mSnapshot->clipRect.set(left, top, right, bottom); + mSnapshot->height = bottom - top; + setScissorFromClip(); + + mSnapshot->flags = Snapshot::kFlagDirtyTransform | Snapshot::kFlagDirtyOrtho | + Snapshot::kFlagClipSet; + memcpy(mSnapshot->orthoMatrix, mOrthoMatrix, sizeof(mOrthoMatrix)); + + // Change the ortho projection + mat4 ortho; + ortho.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f); + ortho.copyTo(mOrthoMatrix); + return true; } @@ -343,7 +372,7 @@ void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { void OpenGLRenderer::setScissorFromClip() { const Rect& clip = mSnapshot->getMappedClip(); - glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); } const Rect& OpenGLRenderer::getClipBounds() { @@ -381,7 +410,7 @@ bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom) /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) { - Texture* texture = mTextureCache.get(bitmap); + const Texture* texture = mTextureCache.get(bitmap); int alpha; SkXfermode::Mode mode; @@ -391,11 +420,26 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const S alpha / 255.0f, mode, texture->blend, true); } +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) { + Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); + const mat4 transform(*matrix); + transform.mapRect(r); + + const Texture* texture = mTextureCache.get(bitmap); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + drawTextureRect(r.left, r.top, r.right, r.bottom, texture->id, + alpha / 255.0f, mode, texture->blend, true); +} + void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkMatrix* matrix, const SkPaint* paint) { - Texture* texture = mTextureCache.get(bitmap); + const SkPaint* paint) { + const Texture* texture = mTextureCache.get(bitmap); int alpha; SkXfermode::Mode mode; @@ -411,8 +455,6 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, resetDrawTextureTexCoords(u1, v1, u2, v2); - // TODO: implement Matrix - drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f, mode, texture->blend, true); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index a698e7979d93..bd5f84fbf83b 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -103,9 +103,9 @@ public: bool clipRect(float left, float top, float right, float bottom); void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint); void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkMatrix* matrix, const SkPaint* paint); + float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint); void drawColor(int color, SkXfermode::Mode mode); void drawRect(float left, float top, float right, float bottom, const SkPaint* paint); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index ca91b3437af6..d1809f35e1e0 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -51,6 +51,7 @@ public: * snapshot. */ Snapshot(const sp<Snapshot> s): + height(s->height), transform(s->transform), clipRect(s->clipRect), flags(kFlagDirtyTransform), @@ -80,6 +81,10 @@ public: * a new layer. */ kFlagIsLayer = 0x4, + /** + * Indicates that this snapshot has changed the ortho matrix. + */ + kFlagDirtyOrtho = 0x8, }; /** @@ -95,6 +100,11 @@ public: } /** + * Height of the framebuffer the snapshot is rendering into. + */ + int height; + + /** * Local transformation. Holds the current translation, scale and * rotation values. */ @@ -141,6 +151,11 @@ public: */ SkXfermode::Mode mode; + /** + * Contains the previous ortho matrix. + */ + float orthoMatrix[16]; + private: // Clipping rectangle mapped with the transform Rect mappedClip; diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 6802c59f973f..d37013d683ba 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -41,11 +41,11 @@ struct Texture { /** * Width of the backing bitmap. */ - unsigned int width; + uint32_t width; /** * Height of the backing bitmap. */ - unsigned int height; + uint32_t height; }; // struct Texture }; // namespace uirenderer diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 7b8b31398188..93ee138a373c 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "OpenGLRenderer" + #include <GLES2/gl2.h> #include "TextureCache.h" @@ -21,7 +23,13 @@ namespace android { namespace uirenderer { -TextureCache::TextureCache(unsigned int maxEntries): mCache(maxEntries) { +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +TextureCache::TextureCache(uint32_t maxByteSize): + mCache(GenerationCache<SkBitmap, Texture>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { mCache.setOnEntryRemovedListener(this); } @@ -29,28 +37,71 @@ TextureCache::~TextureCache() { mCache.clear(); } -void TextureCache::operator()(SkBitmap* key, Texture* value) { - LOGD("Entry removed"); - if (value) { - glDeleteTextures(1, &value->id); - delete value; +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t TextureCache::getSize() { + return mSize; +} + +uint32_t TextureCache::getMaxSize() { + return mMaxSize; +} + +void TextureCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void TextureCache::operator()(SkBitmap* bitmap, Texture* texture) { + if (bitmap) { + const uint32_t size = bitmap->rowBytes() * bitmap->height(); + mSize -= size; + } + + if (texture) { + glDeleteTextures(1, &texture->id); + delete texture; } } +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + Texture* TextureCache::get(SkBitmap* bitmap) { Texture* texture = mCache.get(bitmap); if (!texture) { + const uint32_t size = bitmap->rowBytes() * bitmap->height(); + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + texture = new Texture; generateTexture(bitmap, texture, false); - mCache.put(bitmap, texture); + + if (size < mMaxSize) { + mSize += size; + mCache.put(bitmap, texture); + } } else if (bitmap->getGenerationID() != texture->generation) { generateTexture(bitmap, texture, true); } return texture; } -Texture* TextureCache::remove(SkBitmap* bitmap) { - return mCache.remove(bitmap); +void TextureCache::remove(SkBitmap* bitmap) { + mCache.remove(bitmap); } void TextureCache::clear() { diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index bf680616c4f0..250efc56f60f 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -25,21 +25,63 @@ namespace android { namespace uirenderer { +/** + * A simple LRU texture cache. The cache has a maximum size expressed in bytes. + * Any texture added to the cache causing the cache to grow beyond the maximum + * allowed size will also cause the oldest texture to be kicked out. + */ class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> { public: - TextureCache(unsigned int maxEntries); + TextureCache(uint32_t maxByteSize); ~TextureCache(); - void operator()(SkBitmap* key, Texture* value); + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(SkBitmap* bitmap, Texture* texture); + /** + * Returns the texture associated with the specified bitmap. If the texture + * cannot be found in the cache, a new texture is generated. + */ Texture* get(SkBitmap* bitmap); - Texture* remove(SkBitmap* bitmap); + /** + * Removes the texture associated with the specified bitmap. Returns NULL + * if the texture cannot be found. Upon remove the texture is freed. + */ + void remove(SkBitmap* bitmap); + /** + * Clears the cache. This causes all textures to be deleted. + */ void clear(); + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + private: + /** + * Generates the texture from a bitmap into the specified texture structure. + * + * @param regenerate If true, the bitmap data is reuploaded into the texture, but + * no new texture is generated. + */ void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); GenerationCache<SkBitmap, Texture> mCache; + + uint32_t mSize; + uint32_t mMaxSize; }; // class TextureCache }; // namespace uirenderer diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 7afc4f24c59c..37c418bec7e9 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -102,7 +102,6 @@ LOCAL_SRC_FILES:= \ rsScriptC_LibGL.cpp \ rsShaderCache.cpp \ rsSignal.cpp \ - rsSimpleMesh.cpp \ rsStream.cpp \ rsThreadIO.cpp \ rsType.cpp \ diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index 14283183e970..8e6b5c67e3d8 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -38,7 +38,7 @@ typedef void * RsFile; typedef void * RsFont; typedef void * RsSampler; typedef void * RsScript; -typedef void * RsSimpleMesh; +typedef void * RsMesh; typedef void * RsType; typedef void * RsLight; typedef void * RsObjectBase; @@ -229,7 +229,6 @@ enum RsAnimationEdge { enum RsA3DClassID { RS_A3D_CLASS_ID_UNKNOWN, RS_A3D_CLASS_ID_MESH, - RS_A3D_CLASS_ID_SIMPLE_MESH, RS_A3D_CLASS_ID_TYPE, RS_A3D_CLASS_ID_ELEMENT, RS_A3D_CLASS_ID_ALLOCATION, diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h index 144e5392a1d0..92259041b73f 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/RenderScriptEnv.h @@ -9,7 +9,7 @@ typedef void * RsDevice; typedef void * RsElement; typedef void * RsSampler; typedef void * RsScript; -typedef void * RsSimpleMesh; +typedef void * RsMesh; typedef void * RsType; typedef void * RsProgramFragment; typedef void * RsProgramStore; diff --git a/libs/rs/java/Fountain/res/raw/fountain.rs b/libs/rs/java/Fountain/res/raw/fountain.rs index ef12d9accb8d..c8c10cd0388f 100644 --- a/libs/rs/java/Fountain/res/raw/fountain.rs +++ b/libs/rs/java/Fountain/res/raw/fountain.rs @@ -8,7 +8,6 @@ #include "../../../../scriptc/rs_graphics.rsh" static int newPart = 0; -static float4 partColor; rs_mesh partMesh; typedef struct __attribute__((packed, aligned(4))) Point { @@ -36,20 +35,21 @@ int root() { p++; } - rsgDrawSimpleMesh(partMesh); + rsgDrawMesh(partMesh); return 1; } -void addParticles(int rate, float x, float y, bool newColor) +static float4 partColor[10]; +void addParticles(int rate, float x, float y, int index, bool newColor) { if (newColor) { - partColor.x = rsRand(0.5f, 1.0f); - partColor.y = rsRand(1.0f); - partColor.z = rsRand(1.0f); + partColor[index].x = rsRand(0.5f, 1.0f); + partColor[index].y = rsRand(1.0f); + partColor[index].z = rsRand(1.0f); } - float rMax = ((float)rate) * 0.005f; + float rMax = ((float)rate) * 0.02f; int size = rsAllocationGetDimX(rsGetAllocation(point)); - uchar4 c = rsPackColorTo8888(partColor); + uchar4 c = rsPackColorTo8888(partColor[index]); Point_t * np = &point[newPart]; float2 p = {x, y}; diff --git a/libs/rs/java/Fountain/res/raw/fountain_bc.bc b/libs/rs/java/Fountain/res/raw/fountain_bc.bc Binary files differindex ac6b7d4e5b0d..2c8ce8b9c6fb 100644 --- a/libs/rs/java/Fountain/res/raw/fountain_bc.bc +++ b/libs/rs/java/Fountain/res/raw/fountain_bc.bc diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java index ffe2fad97c3a..cfe8e27aeecb 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java @@ -36,11 +36,10 @@ public class FountainRS { ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT); - SimpleMesh.Builder smb = new SimpleMesh.Builder(mRS); - int vtxSlot = smb.addVertexType(points.getType()); - smb.setPrimitive(Primitive.POINT); - SimpleMesh sm = smb.create(); - sm.bindVertexAllocation(points.getAllocation(), vtxSlot); + Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS); + smb.addVertexAllocation(points.getAllocation()); + smb.addIndexType(Primitive.POINT); + Mesh sm = smb.create(); mScript = new ScriptC_Fountain(mRS, mRes, R.raw.fountain_bc, true); mScript.set_partMesh(sm); @@ -48,13 +47,20 @@ public class FountainRS { mRS.contextBindRootScript(mScript); } - boolean holdingColor = false; - public void newTouchPosition(int x, int y, int rate) { + boolean holdingColor[] = new boolean[10]; + public void newTouchPosition(float x, float y, float pressure, int id) { + if (id > holdingColor.length) { + return; + } + int rate = (int)(pressure * pressure * 500.f); + if(rate > 500) { + rate = 500; + } if (rate > 0) { - mScript.invoke_addParticles(rate, x, y, !holdingColor); - holdingColor = true; + mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]); + holdingColor[id] = true; } else { - holdingColor = false; + holdingColor[id] = false; } } diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java index dfd6a49d09a1..6082c1e878a7 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java @@ -73,15 +73,27 @@ public class FountainView extends RSSurfaceView { { int act = ev.getAction(); if (act == ev.ACTION_UP) { - mRender.newTouchPosition(0, 0, 0); + mRender.newTouchPosition(0, 0, 0, 0); return false; } - float rate = (ev.getPressure() * 50.f); - rate *= rate; - if(rate > 2000.f) { - rate = 2000.f; + + int count = ev.getHistorySize(); + int pcount = ev.getPointerCount(); + + for (int p=0; p < pcount; p++) { + int id = ev.getPointerId(p); + mRender.newTouchPosition(ev.getX(p), + ev.getY(p), + ev.getPressure(p), + id); + + for (int i=0; i < count; i++) { + mRender.newTouchPosition(ev.getHistoricalX(p, i), + ev.getHistoricalY(p, i), + ev.getHistoricalPressure(p, i), + id); + } } - mRender.newTouchPosition((int)ev.getX(), (int)ev.getY(), (int)rate); return true; } } diff --git a/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java index a4c598be46e1..0ec00096f525 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java @@ -27,13 +27,13 @@ public class ScriptC_Fountain extends ScriptC { } private final static int mExportVarIdx_partMesh = 0; - private SimpleMesh mExportVar_partMesh; - public void set_partMesh(SimpleMesh v) { + private Mesh mExportVar_partMesh; + public void set_partMesh(Mesh v) { mExportVar_partMesh = v; setVar(mExportVarIdx_partMesh, (v == null) ? 0 : v.getID()); } - public SimpleMesh get_partMesh() { + public Mesh get_partMesh() { return mExportVar_partMesh; } @@ -50,11 +50,12 @@ public class ScriptC_Fountain extends ScriptC { } private final static int mExportFuncIdx_addParticles = 0; - public void invoke_addParticles(int rate, float x, float y, boolean newColor) { - FieldPacker addParticles_fp = new FieldPacker(16); + public void invoke_addParticles(int rate, float x, float y, int index, boolean newColor) { + FieldPacker addParticles_fp = new FieldPacker(20); addParticles_fp.addI32(rate); addParticles_fp.addF32(x); addParticles_fp.addF32(y); + addParticles_fp.addI32(index); addParticles_fp.addBoolean(newColor); addParticles_fp.skip(3); invoke(mExportFuncIdx_addParticles, addParticles_fp); diff --git a/libs/rs/java/ModelViewer/res/raw/modelviewer.rs b/libs/rs/java/ModelViewer/res/raw/modelviewer.rs index 91194e827912..559bf4856472 100644 --- a/libs/rs/java/ModelViewer/res/raw/modelviewer.rs +++ b/libs/rs/java/ModelViewer/res/raw/modelviewer.rs @@ -62,7 +62,7 @@ int root(int launchID) { rsMatrixRotate(&matrix, gRotate, 0.0f, 1.0f, 0.0f); rsgProgramVertexLoadModelMatrix(&matrix); - rsgDrawSimpleMesh(gTestMesh); + rsgDrawMesh(gTestMesh); color(0.3f, 0.3f, 0.3f, 1.0f); rsgDrawText("Renderscript model test", 30, 695); diff --git a/libs/rs/java/ModelViewer/res/raw/modelviewer_bc.bc b/libs/rs/java/ModelViewer/res/raw/modelviewer_bc.bc Binary files differindex a64e7258b50b..fb8502860f72 100644 --- a/libs/rs/java/ModelViewer/res/raw/modelviewer_bc.bc +++ b/libs/rs/java/ModelViewer/res/raw/modelviewer_bc.bc diff --git a/libs/rs/java/ModelViewer/res/raw/robot.a3d b/libs/rs/java/ModelViewer/res/raw/robot.a3d Binary files differindex c0c66aed5e56..430fe950ffd6 100644 --- a/libs/rs/java/ModelViewer/res/raw/robot.a3d +++ b/libs/rs/java/ModelViewer/res/raw/robot.a3d diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java index b6485dcdfbb5..37eb9c18fa19 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java @@ -55,7 +55,7 @@ public class ModelViewerRS { private Allocation mGridImage; private Allocation mAllocPV; - private SimpleMesh mMesh; + private Mesh mMesh; private Font mItalic; private Allocation mTextAlloc; @@ -149,15 +149,15 @@ public class ModelViewerRS { FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot); FileA3D.IndexEntry entry = model.getIndexEntry(0); - if(entry == null || entry.getClassID() != FileA3D.ClassID.SIMPLE_MESH) { + if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) { Log.e("rs", "could not load model"); } else { - mMesh = (SimpleMesh)entry.getObject(); + mMesh = (Mesh)entry.getObject(); mScript.set_gTestMesh(mMesh); } - mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 10); + mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 8); mScript.set_gItalic(mItalic); initTextAllocation(); diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java index d3a2a292e7d6..06c10ab22efb 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java @@ -60,13 +60,13 @@ public class ScriptC_Modelviewer extends ScriptC { } private final static int mExportVarIdx_gTestMesh = 3; - private SimpleMesh mExportVar_gTestMesh; - public void set_gTestMesh(SimpleMesh v) { + private Mesh mExportVar_gTestMesh; + public void set_gTestMesh(Mesh v) { mExportVar_gTestMesh = v; setVar(mExportVarIdx_gTestMesh, (v == null) ? 0 : v.getID()); } - public SimpleMesh get_gTestMesh() { + public Mesh get_gTestMesh() { return mExportVar_gTestMesh; } diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 3694b6590f7e..172ba661df17 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -478,8 +478,34 @@ FontCreateFromFile { ret RsFont } +MeshCreate { + ret RsMesh + param uint32_t vtxCount + param uint32_t idxCount + } + +MeshBindIndex { + param RsMesh mesh + param RsAllocation idx + param uint32_t primType + param uint32_t slot + } + +MeshBindPrimitive { + param RsMesh mesh + param RsAllocation prim + param uint32_t primType + param uint32_t slot + } + +MeshBindVertex { + param RsMesh mesh + param RsAllocation vtx + param uint32_t slot + } + SimpleMeshCreate { - ret RsSimpleMesh + ret RsMesh param RsAllocation prim param RsAllocation index param RsAllocation *vtx @@ -489,17 +515,17 @@ SimpleMeshCreate { SimpleMeshBindIndex { - param RsSimpleMesh mesh + param RsMesh mesh param RsAllocation idx } SimpleMeshBindPrimitive { - param RsSimpleMesh mesh + param RsMesh mesh param RsAllocation prim } SimpleMeshBindVertex { - param RsSimpleMesh mesh + param RsMesh mesh param RsAllocation vtx param uint32_t slot } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 73f478adb357..06433a17f149 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -24,7 +24,6 @@ #include "rsType.h" #include "rsMatrix.h" #include "rsAllocation.h" -#include "rsSimpleMesh.h" #include "rsMesh.h" #include "rsDevice.h" #include "rsScriptC.h" diff --git a/libs/rs/rsContextHostStub.h b/libs/rs/rsContextHostStub.h index be1fff6dd071..c437606cdb48 100644 --- a/libs/rs/rsContextHostStub.h +++ b/libs/rs/rsContextHostStub.h @@ -24,7 +24,6 @@ #include "rsType.h" #include "rsMatrix.h" #include "rsAllocation.h" -#include "rsSimpleMesh.h" #include "rsMesh.h" //#include "rsDevice.h" #include "rsScriptC.h" diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp index 4fac421e4da0..5709f2a8e79d 100644 --- a/libs/rs/rsFileA3D.cpp +++ b/libs/rs/rsFileA3D.cpp @@ -258,9 +258,6 @@ ObjectBase *FileA3D::initializeFromEntry(size_t index) { case RS_A3D_CLASS_ID_MESH: entry->mRsObj = Mesh::createFromStream(mRSC, mReadStream); break; - case RS_A3D_CLASS_ID_SIMPLE_MESH: - entry->mRsObj = SimpleMesh::createFromStream(mRSC, mReadStream); - break; case RS_A3D_CLASS_ID_TYPE: entry->mRsObj = Type::createFromStream(mRSC, mReadStream); break; diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp index bd9cd275999c..4f90f36b1c40 100644 --- a/libs/rs/rsMesh.cpp +++ b/libs/rs/rsMesh.cpp @@ -35,43 +35,138 @@ Mesh::Mesh(Context *rsc) : ObjectBase(rsc) { mAllocFile = __FILE__; mAllocLine = __LINE__; - mVerticies = NULL; - mVerticiesCount = 0; mPrimitives = NULL; mPrimitivesCount = 0; + mVertexBuffers = NULL; + mVertexTypes = NULL; + mVertexBufferCount = 0; } Mesh::~Mesh() { + if(mVertexTypes) { + delete[] mVertexTypes; + } + + if(mVertexBuffers) { + delete[] mVertexBuffers; + } + + if(mPrimitives) { + for(uint32_t i = 0; i < mPrimitivesCount; i ++) { + delete mPrimitives[i]; + } + delete[] mPrimitives; + } } -void Mesh::serialize(OStream *stream) const +void Mesh::render(Context *rsc) const { - // Need to identify ourselves - stream->addU32((uint32_t)getClassId()); + for(uint32_t ct = 0; ct < mPrimitivesCount; ct ++) { + renderPrimitive(rsc, ct); + } +} - String8 name(getName()); - stream->addString(&name); +void Mesh::renderPrimitive(Context *rsc, uint32_t primIndex) const { + if (primIndex >= mPrimitivesCount) { + LOGE("Invalid primitive index"); + return; + } - stream->addU32(mVerticiesCount); + Primitive_t *prim = mPrimitives[primIndex]; - for(uint32_t vCount = 0; vCount < mVerticiesCount; vCount ++) { - Verticies_t *verts = mVerticies[vCount]; + if (prim->mIndexBuffer.get()) { + renderPrimitiveRange(rsc, primIndex, 0, prim->mIndexBuffer->getType()->getDimX()); + return; + } - stream->addU32(verts->mAllocationCount); + if (prim->mPrimitiveBuffer.get()) { + renderPrimitiveRange(rsc, primIndex, 0, prim->mPrimitiveBuffer->getType()->getDimX()); + return; + } + + renderPrimitiveRange(rsc, primIndex, 0, mVertexBuffers[0]->getType()->getDimX()); +} - for (uint32_t aCount = 0; aCount < verts->mAllocationCount; aCount++) { - verts->mAllocations[aCount]->serialize(stream); +void Mesh::renderPrimitiveRange(Context *rsc, uint32_t primIndex, uint32_t start, uint32_t len) const +{ + if (len < 1 || primIndex >= mPrimitivesCount) { + return; + } + + rsc->checkError("Mesh::renderPrimitiveRange 1"); + VertexArray va; + for (uint32_t ct=0; ct < mVertexBufferCount; ct++) { + mVertexBuffers[ct]->uploadCheck(rsc); + if (mVertexBuffers[ct]->getIsBufferObject()) { + va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID()); + } else { + va.setActiveBuffer(mVertexBuffers[ct]->getPtr()); } - stream->addU32(verts->mVertexDataSize); + mVertexBuffers[ct]->getType()->enableGLVertexBuffer(&va); + } + va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); + + rsc->checkError("Mesh::renderPrimitiveRange 2"); + Primitive_t *prim = mPrimitives[primIndex]; + if (prim->mIndexBuffer.get()) { + prim->mIndexBuffer->uploadCheck(rsc); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prim->mIndexBuffer->getBufferObjectID()); + glDrawElements(prim->mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2)); + } else { + glDrawArrays(prim->mGLPrimitive, start, len); + } + + rsc->checkError("Mesh::renderPrimitiveRange"); +} - stream->addU32(verts->mOffsetCoord); - stream->addU32(verts->mOffsetTex); - stream->addU32(verts->mOffsetNorm); - stream->addU32(verts->mSizeCoord); - stream->addU32(verts->mSizeTex); - stream->addU32(verts->mSizeNorm ); +void Mesh::uploadAll(Context *rsc) +{ + for (uint32_t ct = 0; ct < mVertexBufferCount; ct ++) { + if (mVertexBuffers[ct].get()) { + mVertexBuffers[ct]->deferedUploadToBufferObject(rsc); + } + } + + for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) { + if (mPrimitives[ct]->mIndexBuffer.get()) { + mPrimitives[ct]->mIndexBuffer->deferedUploadToBufferObject(rsc); + } + if (mPrimitives[ct]->mPrimitiveBuffer.get()) { + mPrimitives[ct]->mPrimitiveBuffer->deferedUploadToBufferObject(rsc); + } + } + + rsc->checkError("Mesh::uploadAll"); +} + +void Mesh::updateGLPrimitives() +{ + for(uint32_t i = 0; i < mPrimitivesCount; i ++) { + switch(mPrimitives[i]->mPrimitive) { + case RS_PRIMITIVE_POINT: mPrimitives[i]->mGLPrimitive = GL_POINTS; break; + case RS_PRIMITIVE_LINE: mPrimitives[i]->mGLPrimitive = GL_LINES; break; + case RS_PRIMITIVE_LINE_STRIP: mPrimitives[i]->mGLPrimitive = GL_LINE_STRIP; break; + case RS_PRIMITIVE_TRIANGLE: mPrimitives[i]->mGLPrimitive = GL_TRIANGLES; break; + case RS_PRIMITIVE_TRIANGLE_STRIP: mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_STRIP; break; + case RS_PRIMITIVE_TRIANGLE_FAN: mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_FAN; break; + } + } +} + +void Mesh::serialize(OStream *stream) const +{ + // Need to identify ourselves + stream->addU32((uint32_t)getClassId()); + + String8 name(getName()); + stream->addString(&name); + + // Store number of vertex streams + stream->addU32(mVertexBufferCount); + for(uint32_t vCount = 0; vCount < mVertexBufferCount; vCount ++) { + mVertexBuffers[vCount]->serialize(stream); } stream->addU32(mPrimitivesCount); @@ -79,27 +174,22 @@ void Mesh::serialize(OStream *stream) const for (uint32_t pCount = 0; pCount < mPrimitivesCount; pCount ++) { Primitive_t * prim = mPrimitives[pCount]; - stream->addU8((uint8_t)prim->mType); + stream->addU8((uint8_t)prim->mPrimitive); - // We store the index to the vertices - // So iterate over our vertices to find which one we point to - uint32_t vertexIndex = 0; - for(uint32_t vCount = 0; vCount < mVerticiesCount; vCount ++) { - if(prim->mVerticies == mVerticies[vCount]) { - vertexIndex = vCount; - break; - } + if(prim->mIndexBuffer.get()) { + stream->addU32(1); + prim->mIndexBuffer->serialize(stream); } - stream->addU32(vertexIndex); - - stream->addU32(prim->mIndexCount); - for (uint32_t ct = 0; ct < prim->mIndexCount; ct++) { - stream->addU16(prim->mIndicies[ct]); + else { + stream->addU32(0); } - stream->addU32(prim->mRestartCounts); - for (uint32_t ct = 0; ct < prim->mRestartCounts; ct++) { - stream->addU16(prim->mRestarts[ct]); + if(prim->mPrimitiveBuffer.get()) { + stream->addU32(1); + prim->mPrimitiveBuffer->serialize(stream); + } + else { + stream->addU32(0); } } } @@ -119,86 +209,46 @@ Mesh *Mesh::createFromStream(Context *rsc, IStream *stream) stream->loadString(&name); mesh->setName(name.string(), name.size()); - mesh->mVerticiesCount = stream->loadU32(); - if(mesh->mVerticiesCount) { - mesh->mVerticies = new Verticies_t *[mesh->mVerticiesCount]; - } - else { - mesh->mVerticies = NULL; - } - - for(uint32_t vCount = 0; vCount < mesh->mVerticiesCount; vCount ++) { - Verticies_t *verts = new Verticies_t(); - // Store our vertices one the mesh - mesh->mVerticies[vCount] = verts; - - verts->mAllocationCount = stream->loadU32(); - verts->mAllocations = new Allocation *[verts->mAllocationCount]; + mesh->mVertexBufferCount = stream->loadU32(); + if(mesh->mVertexBufferCount) { + mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexBufferCount]; - LOGE("processChunk_Verticies count %i", verts->mAllocationCount); - for (uint32_t aCount = 0; aCount < verts->mAllocationCount; aCount++) { - verts->mAllocations[aCount] = Allocation::createFromStream(rsc, stream); + for(uint32_t vCount = 0; vCount < mesh->mVertexBufferCount; vCount ++) { + Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream); + mesh->mVertexBuffers[vCount].set(vertexAlloc); } - verts->mVertexDataSize = stream->loadU32(); - - verts->mOffsetCoord = stream->loadU32(); - verts->mOffsetTex = stream->loadU32(); - verts->mOffsetNorm = stream->loadU32(); - - verts->mSizeCoord = stream->loadU32(); - verts->mSizeTex = stream->loadU32(); - verts->mSizeNorm = stream->loadU32(); } mesh->mPrimitivesCount = stream->loadU32(); if(mesh->mPrimitivesCount) { mesh->mPrimitives = new Primitive_t *[mesh->mPrimitivesCount]; - } - else { - mesh->mPrimitives = NULL; - } - // load all primitives - for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { - Primitive_t * prim = new Primitive_t; - mesh->mPrimitives[pCount] = prim; + // load all primitives + for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { + Primitive_t * prim = new Primitive_t; + mesh->mPrimitives[pCount] = prim; - prim->mType = (RsPrimitive)stream->loadU8(); - - // We store the index to the vertices - uint32_t vertexIndex = stream->loadU32(); - if(vertexIndex < mesh->mVerticiesCount) { - prim->mVerticies = mesh->mVerticies[vertexIndex]; - } - else { - prim->mVerticies = NULL; - } + prim->mPrimitive = (RsPrimitive)stream->loadU8(); - prim->mIndexCount = stream->loadU32(); - if(prim->mIndexCount){ - prim->mIndicies = new uint16_t[prim->mIndexCount]; - for (uint32_t ct = 0; ct < prim->mIndexCount; ct++) { - prim->mIndicies[ct] = stream->loadU16(); + // Check to see if the index buffer was stored + uint32_t isIndexPresent = stream->loadU32(); + if(isIndexPresent) { + Allocation *indexAlloc = Allocation::createFromStream(rsc, stream); + prim->mIndexBuffer.set(indexAlloc); } - } - else { - prim->mIndicies = NULL; - } - - prim->mRestartCounts = stream->loadU32(); - if (prim->mRestartCounts) { - prim->mRestarts = new uint16_t[prim->mRestartCounts]; - for (uint32_t ct = 0; ct < prim->mRestartCounts; ct++) { - prim->mRestarts[ct] = stream->loadU16(); + // Check to see if the primitive buffer was stored + uint32_t isPrimitivePresent = stream->loadU32(); + if(isPrimitivePresent) { + Allocation *primitiveAlloc = Allocation::createFromStream(rsc, stream); + prim->mPrimitiveBuffer.set(primitiveAlloc); } } - else { - prim->mRestarts = NULL; - } - } + mesh->updateGLPrimitives(); + mesh->uploadAll(rsc); + return mesh; } @@ -211,3 +261,103 @@ MeshContext::~MeshContext() { } +namespace android { +namespace renderscript { + +RsMesh rsi_MeshCreate(Context *rsc, uint32_t vtxCount, uint32_t idxCount) +{ + Mesh *sm = new Mesh(rsc); + sm->incUserRef(); + + sm->mPrimitivesCount = idxCount; + sm->mPrimitives = new Mesh::Primitive_t *[sm->mPrimitivesCount]; + for(uint32_t ct = 0; ct < idxCount; ct ++) { + sm->mPrimitives[ct] = new Mesh::Primitive_t; + } + + sm->mVertexBufferCount = vtxCount; + sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount]; + sm->mVertexTypes = new ObjectBaseRef<const Type>[vtxCount]; + + return sm; +} + +void rsi_MeshBindVertex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t slot) +{ + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(slot < sm->mVertexBufferCount); + + sm->mVertexBuffers[slot].set((Allocation *)va); +} + +void rsi_MeshBindIndex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t primType, uint32_t slot) +{ + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(slot < sm->mPrimitivesCount); + + sm->mPrimitives[slot]->mIndexBuffer.set((Allocation *)va); + sm->mPrimitives[slot]->mPrimitive = (RsPrimitive)primType; + sm->updateGLPrimitives(); +} + +void rsi_MeshBindPrimitive(Context *rsc, RsMesh mv, RsAllocation va, uint32_t primType, uint32_t slot) +{ + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(slot < sm->mPrimitivesCount); + + sm->mPrimitives[slot]->mPrimitiveBuffer.set((Allocation *)va); + sm->mPrimitives[slot]->mPrimitive = (RsPrimitive)primType; + sm->updateGLPrimitives(); +} + + +// Route all the simple mesh through mesh + +RsMesh rsi_SimpleMeshCreate(Context *rsc, RsType prim, RsType idx, RsType *vtx, uint32_t vtxCount, uint32_t primType) +{ + Mesh *sm = new Mesh(rsc); + sm->incUserRef(); + + sm->mPrimitivesCount = 1; + sm->mPrimitives = new Mesh::Primitive_t *[sm->mPrimitivesCount]; + sm->mPrimitives[0] = new Mesh::Primitive_t; + + sm->mPrimitives[0]->mIndexType.set((const Type *)idx); + sm->mPrimitives[0]->mPrimitiveType.set((const Type *)prim); + sm->mPrimitives[0]->mPrimitive = (RsPrimitive)primType; + sm->updateGLPrimitives(); + + sm->mVertexBufferCount = vtxCount; + sm->mVertexTypes = new ObjectBaseRef<const Type>[vtxCount]; + sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount]; + for (uint32_t ct=0; ct < vtxCount; ct++) { + sm->mVertexTypes[ct].set((const Type *)vtx[ct]); + } + + return sm; +} + +void rsi_SimpleMeshBindVertex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t slot) +{ + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(slot < sm->mVertexBufferCount); + + sm->mVertexBuffers[slot].set((Allocation *)va); +} + +void rsi_SimpleMeshBindIndex(Context *rsc, RsMesh mv, RsAllocation va) +{ + Mesh *sm = static_cast<Mesh *>(mv); + sm->mPrimitives[0]->mIndexBuffer.set((Allocation *)va); +} + +void rsi_SimpleMeshBindPrimitive(Context *rsc, RsMesh mv, RsAllocation va) +{ + Mesh *sm = static_cast<Mesh *>(mv); + sm->mPrimitives[0]->mPrimitiveBuffer.set((Allocation *)va); +} + + + + +}} diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h index 8c7e8a4427c6..66174b725581 100644 --- a/libs/rs/rsMesh.h +++ b/libs/rs/rsMesh.h @@ -32,43 +32,35 @@ public: Mesh(Context *); ~Mesh(); - struct Verticies_t - { - Allocation ** mAllocations; - uint32_t mAllocationCount; - - size_t mVertexDataSize; - - size_t mOffsetCoord; - size_t mOffsetTex; - size_t mOffsetNorm; - - size_t mSizeCoord; - size_t mSizeTex; - size_t mSizeNorm; - - uint32_t mBufferObject; - }; - + // Contains vertex data + // Position, normal, texcoord, etc could either be strided in one allocation + // of provided separetely in multiple ones + ObjectBaseRef<Allocation> *mVertexBuffers; + ObjectBaseRef<const Type> *mVertexTypes; + uint32_t mVertexBufferCount; + + // Either mIndexBuffer, mPrimitiveBuffer or both could have a NULL reference + // If both are null, mPrimitive only would be used to render the mesh struct Primitive_t { - RsPrimitive mType; - Verticies_t *mVerticies; - - uint32_t mIndexCount; - uint16_t *mIndicies; + ObjectBaseRef<Allocation> mIndexBuffer; + ObjectBaseRef<Allocation> mPrimitiveBuffer; + ObjectBaseRef<const Type> mIndexType; + ObjectBaseRef<const Type> mPrimitiveType; - uint32_t mRestartCounts; - uint16_t *mRestarts; + RsPrimitive mPrimitive; + uint32_t mGLPrimitive; }; - Verticies_t ** mVerticies; - uint32_t mVerticiesCount; - Primitive_t ** mPrimitives; uint32_t mPrimitivesCount; - void analyzeElement(); + void render(Context *) const; + void renderPrimitive(Context *, uint32_t primIndex) const; + void renderPrimitiveRange(Context *, uint32_t primIndex, uint32_t start, uint32_t len) const; + void uploadAll(Context *); + void updateGLPrimitives(); + virtual void serialize(OStream *stream) const; virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; } static Mesh *createFromStream(Context *rsc, IStream *stream); @@ -90,3 +82,4 @@ public: #endif //ANDROID_RS_TRIANGLE_MESH_H + diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp index dbd398e34c42..7185009b7a34 100644 --- a/libs/rs/rsScriptC_LibGL.cpp +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -43,10 +43,10 @@ using namespace android::renderscript; // IO routines ////////////////////////////////////////////////////////////////////////////// -static void SC_updateSimpleMesh(RsSimpleMesh mesh) +static void SC_updateSimpleMesh(RsMesh mesh) { GET_TLS(); - SimpleMesh *sm = static_cast<SimpleMesh *>(mesh); + Mesh *sm = static_cast<Mesh *>(mesh); sm->uploadAll(rsc); } @@ -220,24 +220,54 @@ static void SC_drawRect(float x1, float y1, x1, y1, z); } -static void SC_drawSimpleMesh(RsSimpleMesh vsm) +static void SC_drawSimpleMesh(RsMesh vsm) { GET_TLS(); - SimpleMesh *sm = static_cast<SimpleMesh *>(vsm); + Mesh *sm = static_cast<Mesh *>(vsm); + if (!rsc->setupCheck()) { + return; + } + sm->renderPrimitive(rsc, 0); +} + +static void SC_drawSimpleMeshRange(RsMesh vsm, uint32_t start, uint32_t len) +{ + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); + if (!rsc->setupCheck()) { + return; + } + sm->renderPrimitiveRange(rsc, 0, start, len); +} + +static void SC_drawMesh(RsMesh vsm) +{ + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); if (!rsc->setupCheck()) { return; } sm->render(rsc); } -static void SC_drawSimpleMeshRange(RsSimpleMesh vsm, uint32_t start, uint32_t len) +static void SC_drawMeshPrimitive(RsMesh vsm, uint32_t primIndex) { GET_TLS(); - SimpleMesh *sm = static_cast<SimpleMesh *>(vsm); + Mesh *sm = static_cast<Mesh *>(vsm); if (!rsc->setupCheck()) { return; } - sm->renderRange(rsc, start, len); + sm->renderPrimitive(rsc, primIndex); +} + +static void SC_drawMeshPrimitiveRange(RsMesh vsm, uint32_t primIndex, uint32_t start, uint32_t len) +{ + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); + if (!rsc->setupCheck()) { + return; + } + sm->renderPrimitiveRange(rsc, primIndex, start, len); } @@ -375,6 +405,10 @@ static ScriptCState::SymbolTable_t gSyms[] = { { "_Z17rsgDrawSimpleMesh7rs_mesh", (void *)&SC_drawSimpleMesh }, { "_Z17rsgDrawSimpleMesh7rs_meshii", (void *)&SC_drawSimpleMeshRange }, + { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh }, + { "_Z11rsgDrawMesh7rs_meshi", (void *)&SC_drawMeshPrimitive }, + { "_Z11rsgDrawMesh7rs_meshiii", (void *)&SC_drawMeshPrimitiveRange }, + { "rsgClearColor", (void *)&SC_ClearColor }, { "rsgClearDepth", (void *)&SC_ClearDepth }, diff --git a/libs/rs/rsSimpleMesh.cpp b/libs/rs/rsSimpleMesh.cpp deleted file mode 100644 index e5c2eb5afa19..000000000000 --- a/libs/rs/rsSimpleMesh.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2009 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_RS_BUILD_FOR_HOST -#include "rsContext.h" - -#include <GLES/gl.h> -#include <GLES2/gl2.h> -#include <GLES/glext.h> -#else -#include "rsContextHostStub.h" - -#include <OpenGL/gl.h> -#include <OpenGl/glext.h> -#endif - -using namespace android; -using namespace android::renderscript; - - - - -SimpleMesh::SimpleMesh(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; -} - -SimpleMesh::~SimpleMesh() -{ - delete[] mVertexTypes; - delete[] mVertexBuffers; -} - -void SimpleMesh::render(Context *rsc) const -{ - if (mPrimitiveType.get()) { - renderRange(rsc, 0, mPrimitiveType->getDimX()); - return; - } - - if (mIndexType.get()) { - renderRange(rsc, 0, mIndexType->getDimX()); - return; - } - - renderRange(rsc, 0, mVertexTypes[0]->getDimX()); -} - -void SimpleMesh::renderRange(Context *rsc, uint32_t start, uint32_t len) const -{ - if (len < 1) { - return; - } - - rsc->checkError("SimpleMesh::renderRange 1"); - VertexArray va; - for (uint32_t ct=0; ct < mVertexTypeCount; ct++) { - mVertexBuffers[ct]->uploadCheck(rsc); - if (mVertexBuffers[ct]->getIsBufferObject()) { - va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID()); - } else { - va.setActiveBuffer(mVertexBuffers[ct]->getPtr()); - } - mVertexTypes[ct]->enableGLVertexBuffer(&va); - } - va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); - - rsc->checkError("SimpleMesh::renderRange 2"); - if (mIndexType.get()) { - mIndexBuffer->uploadCheck(rsc); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID()); - glDrawElements(mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2)); - } else { - glDrawArrays(mGLPrimitive, start, len); - } - - rsc->checkError("SimpleMesh::renderRange"); -} - -void SimpleMesh::uploadAll(Context *rsc) -{ - for (uint32_t ct=0; ct < mVertexTypeCount; ct++) { - if (mVertexBuffers[ct].get()) { - mVertexBuffers[ct]->deferedUploadToBufferObject(rsc); - } - } - if (mIndexBuffer.get()) { - mIndexBuffer->deferedUploadToBufferObject(rsc); - } - if (mPrimitiveBuffer.get()) { - mPrimitiveBuffer->deferedUploadToBufferObject(rsc); - } - rsc->checkError("SimpleMesh::uploadAll"); -} - -void SimpleMesh::updateGLPrimitive() -{ - switch(mPrimitive) { - case RS_PRIMITIVE_POINT: mGLPrimitive = GL_POINTS; break; - case RS_PRIMITIVE_LINE: mGLPrimitive = GL_LINES; break; - case RS_PRIMITIVE_LINE_STRIP: mGLPrimitive = GL_LINE_STRIP; break; - case RS_PRIMITIVE_TRIANGLE: mGLPrimitive = GL_TRIANGLES; break; - case RS_PRIMITIVE_TRIANGLE_STRIP: mGLPrimitive = GL_TRIANGLE_STRIP; break; - case RS_PRIMITIVE_TRIANGLE_FAN: mGLPrimitive = GL_TRIANGLE_FAN; break; - } -} - -void SimpleMesh::serialize(OStream *stream) const -{ - // Need to identify ourselves - stream->addU32((uint32_t)getClassId()); - - String8 name(getName()); - stream->addString(&name); - - // Add primitive type - stream->addU8((uint8_t)mPrimitive); - - // And now serialize the allocations - mIndexBuffer->serialize(stream); - - // We need to indicate if the primitive buffer is present - if(mPrimitiveBuffer.get() != NULL) { - // Write if the primitive buffer is present - stream->addU32(1); - mPrimitiveBuffer->serialize(stream); - } - else { - // No buffer present, will need this when we read - stream->addU32(0); - } - - // Store number of vertex streams - stream->addU32(mVertexTypeCount); - for(uint32_t vCount = 0; vCount < mVertexTypeCount; vCount ++) { - mVertexBuffers[vCount]->serialize(stream); - } -} - -SimpleMesh *SimpleMesh::createFromStream(Context *rsc, IStream *stream) -{ - // First make sure we are reading the correct object - RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); - if(classID != RS_A3D_CLASS_ID_SIMPLE_MESH) { - LOGE("simple mesh loading skipped due to invalid class id"); - return NULL; - } - - SimpleMesh * mesh = new SimpleMesh(rsc); - - String8 name; - stream->loadString(&name); - mesh->setName(name.string(), name.size()); - - mesh->mPrimitive = (RsPrimitive)stream->loadU8(); - mesh->updateGLPrimitive(); - - Allocation *indexAlloc = Allocation::createFromStream(rsc, stream); - const Type *indexType = indexAlloc->getType(); - mesh->mIndexBuffer.set(indexAlloc); - mesh->mIndexType.set(indexType); - - bool isPrimitivePresent = stream->loadU32() != 0; - if(isPrimitivePresent) { - mesh->mPrimitiveBuffer.set(Allocation::createFromStream(rsc, stream)); - mesh->mPrimitiveType.set(mesh->mPrimitiveBuffer->getType()); - } - - mesh->mVertexTypeCount = stream->loadU32(); - if(mesh->mVertexTypeCount) { - mesh->mVertexTypes = new ObjectBaseRef<const Type>[mesh->mVertexTypeCount]; - mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexTypeCount]; - - for(uint32_t vCount = 0; vCount < mesh->mVertexTypeCount; vCount ++) { - Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream); - const Type *vertexType = vertexAlloc->getType(); - mesh->mVertexBuffers[vCount].set(vertexAlloc); - mesh->mVertexTypes[vCount].set(vertexType); - } - } - - mesh->uploadAll(rsc); - - return mesh; -} - - -SimpleMeshContext::SimpleMeshContext() -{ -} - -SimpleMeshContext::~SimpleMeshContext() -{ -} - - -namespace android { -namespace renderscript { - - -RsSimpleMesh rsi_SimpleMeshCreate(Context *rsc, RsType prim, RsType idx, RsType *vtx, uint32_t vtxCount, uint32_t primType) -{ - SimpleMesh *sm = new SimpleMesh(rsc); - sm->incUserRef(); - - sm->mIndexType.set((const Type *)idx); - sm->mPrimitiveType.set((const Type *)prim); - - sm->mVertexTypeCount = vtxCount; - sm->mVertexTypes = new ObjectBaseRef<const Type>[vtxCount]; - sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount]; - for (uint32_t ct=0; ct < vtxCount; ct++) { - sm->mVertexTypes[ct].set((const Type *)vtx[ct]); - } - - sm->mPrimitive = (RsPrimitive)primType; - sm->updateGLPrimitive(); - return sm; -} - -void rsi_SimpleMeshBindVertex(Context *rsc, RsSimpleMesh mv, RsAllocation va, uint32_t slot) -{ - SimpleMesh *sm = static_cast<SimpleMesh *>(mv); - rsAssert(slot < sm->mVertexTypeCount); - - sm->mVertexBuffers[slot].set((Allocation *)va); -} - -void rsi_SimpleMeshBindIndex(Context *rsc, RsSimpleMesh mv, RsAllocation va) -{ - SimpleMesh *sm = static_cast<SimpleMesh *>(mv); - sm->mIndexBuffer.set((Allocation *)va); -} - -void rsi_SimpleMeshBindPrimitive(Context *rsc, RsSimpleMesh mv, RsAllocation va) -{ - SimpleMesh *sm = static_cast<SimpleMesh *>(mv); - sm->mPrimitiveBuffer.set((Allocation *)va); -} - - - - -}} - diff --git a/libs/rs/rsSimpleMesh.h b/libs/rs/rsSimpleMesh.h deleted file mode 100644 index 362c7fbac2d4..000000000000 --- a/libs/rs/rsSimpleMesh.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2009 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_RS_SIMPLE_MESH_H -#define ANDROID_RS_SIMPLE_MESH_H - - -#include "RenderScript.h" - -// --------------------------------------------------------------------------- -namespace android { -namespace renderscript { - - -// An element is a group of Components that occupies one cell in a structure. -class SimpleMesh : public ObjectBase -{ -public: - SimpleMesh(Context *); - ~SimpleMesh(); - - ObjectBaseRef<const Type> mIndexType; - ObjectBaseRef<const Type> mPrimitiveType; - ObjectBaseRef<const Type> *mVertexTypes; - uint32_t mVertexTypeCount; - - ObjectBaseRef<Allocation> mIndexBuffer; - ObjectBaseRef<Allocation> mPrimitiveBuffer; - ObjectBaseRef<Allocation> *mVertexBuffers; - - RsPrimitive mPrimitive; - uint32_t mGLPrimitive; - - - void render(Context *) const; - void renderRange(Context *, uint32_t start, uint32_t len) const; - void uploadAll(Context *); - void updateGLPrimitive(); - - virtual void serialize(OStream *stream) const; - virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_SIMPLE_MESH; } - static SimpleMesh *createFromStream(Context *rsc, IStream *stream); - -protected: -}; - -class SimpleMeshContext -{ -public: - SimpleMeshContext(); - ~SimpleMeshContext(); - - -}; - - -} -} -#endif //ANDROID_RS_SIMPLE_MESH_H - diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh index fba3d6d0acd0..1f1f1ac02a5e 100644 --- a/libs/rs/scriptc/rs_graphics.rsh +++ b/libs/rs/scriptc/rs_graphics.rsh @@ -32,6 +32,10 @@ extern void rsgDrawSpriteScreenspace(float x, float y, float z, float w, float h extern void __attribute__((overloadable)) rsgDrawSimpleMesh(rs_mesh ism); extern void __attribute__((overloadable)) rsgDrawSimpleMesh(rs_mesh ism, int start, int len); +extern void __attribute__((overloadable)) rsgDrawMesh(rs_mesh ism); +extern void __attribute__((overloadable)) rsgDrawMesh(rs_mesh ism, int primitiveIndex); +extern void __attribute__((overloadable)) rsgDrawMesh(rs_mesh ism, int primitiveIndex, int start, int len); + extern void rsgClearColor(float, float, float, float); extern void rsgClearDepth(float); diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp index fa192563cb4b..65260325b21f 100644 --- a/libs/surfaceflinger/TextureManager.cpp +++ b/libs/surfaceflinger/TextureManager.cpp @@ -87,7 +87,7 @@ status_t TextureManager::initTexture(Image* pImage, int32_t format) GLenum target = GL_TEXTURE_2D; #if defined(GL_OES_texture_external) if (GLExtensions::getInstance().haveTextureExternal()) { - if (format && isSupportedYuvFormat(format)) { + if (format && isYuvFormat(format)) { target = GL_TEXTURE_EXTERNAL_OES; pImage->target = Texture::TEXTURE_EXTERNAL; } @@ -105,23 +105,32 @@ status_t TextureManager::initTexture(Image* pImage, int32_t format) bool TextureManager::isSupportedYuvFormat(int format) { - return isYuvFormat(format); + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YV16: + return true; + } + return false; } bool TextureManager::isYuvFormat(int format) { switch (format) { - case HAL_PIXEL_FORMAT_NV16: - case HAL_PIXEL_FORMAT_NV21: - case HAL_PIXEL_FORMAT_IYUV: - case HAL_PIXEL_FORMAT_YUV9: - case HAL_PIXEL_FORMAT_YUY2: - case HAL_PIXEL_FORMAT_UYVY: - case HAL_PIXEL_FORMAT_NV12: - case HAL_PIXEL_FORMAT_NV61: - case HAL_PIXEL_FORMAT_NV12_ADRENO_TILED: + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YV16: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: return true; } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + return false; } @@ -255,7 +264,7 @@ status_t TextureManager::loadTexture(Texture* texture, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->potWidth, texture->potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if (isYuvFormat(t.format)) { + } else if (isSupportedYuvFormat(t.format)) { // just show the Y plane of YUV buffers glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, @@ -283,7 +292,7 @@ status_t TextureManager::loadTexture(Texture* texture, 0, bounds.top, t.width, bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, t.data + bounds.top*t.stride*4); - } else if (isYuvFormat(t.format)) { + } else if (isSupportedYuvFormat(t.format)) { // just show the Y plane of YUV buffers glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.top, t.width, bounds.height(), diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index c9a5950d1532..b205418e0f70 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -59,16 +59,13 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) // YUV format from the HAL are handled here switch (format) { case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCrCb_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_P: case HAL_PIXEL_FORMAT_YCbCr_422_I: - case HAL_PIXEL_FORMAT_CbYCrY_422_I: + case HAL_PIXEL_FORMAT_YV16: info->bitsPerPixel = 16; goto done; - case HAL_PIXEL_FORMAT_YCbCr_420_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: - case HAL_PIXEL_FORMAT_YCbCr_420_P: + case HAL_PIXEL_FORMAT_YV12: info->bitsPerPixel = 12; done: info->format = format; diff --git a/media/jni/android_media_MtpServer.cpp b/media/jni/android_media_MtpServer.cpp index 355a5eb4b140..e6a3835a9e4d 100644 --- a/media/jni/android_media_MtpServer.cpp +++ b/media/jni/android_media_MtpServer.cpp @@ -27,6 +27,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "private/android_filesystem_config.h" #include "MtpServer.h" @@ -65,7 +66,7 @@ public: return false; } - MtpServer* server = new MtpServer(fd, mDatabasePath); + MtpServer* server = new MtpServer(fd, mDatabasePath, AID_SDCARD_RW, 0664, 0775); server->addStorage(mStoragePath); // temporary diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 8bc6e9ac0e50..3619013fa4f7 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -69,6 +69,7 @@ status_t StagefrightRecorder::setAudioSource(audio_source as) { LOGV("setAudioSource: %d", as); if (as < AUDIO_SOURCE_DEFAULT || as >= AUDIO_SOURCE_LIST_END) { + LOGE("Invalid audio source: %d", as); return BAD_VALUE; } @@ -85,6 +86,7 @@ status_t StagefrightRecorder::setVideoSource(video_source vs) { LOGV("setVideoSource: %d", vs); if (vs < VIDEO_SOURCE_DEFAULT || vs >= VIDEO_SOURCE_LIST_END) { + LOGE("Invalid video source: %d", vs); return BAD_VALUE; } @@ -101,6 +103,7 @@ status_t StagefrightRecorder::setOutputFormat(output_format of) { LOGV("setOutputFormat: %d", of); if (of < OUTPUT_FORMAT_DEFAULT || of >= OUTPUT_FORMAT_LIST_END) { + LOGE("Invalid output format: %d", of); return BAD_VALUE; } @@ -117,6 +120,7 @@ status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) { LOGV("setAudioEncoder: %d", ae); if (ae < AUDIO_ENCODER_DEFAULT || ae >= AUDIO_ENCODER_LIST_END) { + LOGE("Invalid audio encoder: %d", ae); return BAD_VALUE; } @@ -133,6 +137,7 @@ status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) { LOGV("setVideoEncoder: %d", ve); if (ve < VIDEO_ENCODER_DEFAULT || ve >= VIDEO_ENCODER_LIST_END) { + LOGE("Invalid video encoder: %d", ve); return BAD_VALUE; } @@ -176,7 +181,7 @@ status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) { LOGV("setCamera"); if (camera == 0) { LOGE("camera is NULL"); - return UNKNOWN_ERROR; + return BAD_VALUE; } int64_t token = IPCThreadState::self()->clearCallingIdentity(); @@ -185,7 +190,7 @@ status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) { if (mCamera == 0) { LOGE("Unable to connect to camera"); IPCThreadState::self()->restoreCallingIdentity(token); - return UNKNOWN_ERROR; + return -EBUSY; } LOGV("Connected to camera"); @@ -206,11 +211,11 @@ status_t StagefrightRecorder::setPreviewSurface(const sp<ISurface> &surface) { } status_t StagefrightRecorder::setOutputFile(const char *path) { - LOGE("setOutputFile(const char*) should not be called"); + LOGE("setOutputFile(const char*) must not be called"); // We don't actually support this at all, as the media_server process // no longer has permissions to create files. - return UNKNOWN_ERROR; + return -EPERM; } status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { @@ -219,6 +224,11 @@ status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t leng CHECK_EQ(offset, 0); CHECK_EQ(length, 0); + if (fd < 0) { + LOGE("Invalid file descriptor: %d", fd); + return -EBADF; + } + if (mOutputFd >= 0) { ::close(mOutputFd); } @@ -294,6 +304,7 @@ status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) { LOGV("setParamAudioNumberOfChannels: %d", channels); if (channels <= 0 || channels >= 3) { LOGE("Invalid number of audio channels: %d", channels); + return BAD_VALUE; } // Additional check on the number of channels will be performed later. @@ -331,21 +342,23 @@ status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { return OK; } -status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit, - bool limit_is_duration) { - LOGV("setParamMaxDurationOrFileSize: limit (%lld) for %s", - limit, limit_is_duration?"duration":"size"); - if (limit_is_duration) { // limit is in ms - if (limit <= 1000) { // XXX: 1 second - LOGE("Max file duration is too short: %lld us", limit); - } - mMaxFileDurationUs = limit * 1000LL; - } else { - if (limit <= 1024) { // XXX: 1 kB - LOGE("Max file size is too small: %lld bytes", limit); - } - mMaxFileSizeBytes = limit; +status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { + LOGV("setParamMaxFileDurationUs: %lld us", timeUs); + if (timeUs <= 1000000LL) { // XXX: 1 second + LOGE("Max file duration is too short: %lld us", timeUs); + return BAD_VALUE; } + mMaxFileDurationUs = timeUs; + return OK; +} + +status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) { + LOGV("setParamMaxFileSizeBytes: %lld bytes", bytes); + if (bytes <= 1024) { // XXX: 1 kB + LOGE("Max file size is too small: %lld bytes", bytes); + return BAD_VALUE; + } + mMaxFileSizeBytes = bytes; return OK; } @@ -370,7 +383,7 @@ status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { // If interval < 0, only the first frame is I frame, and rest are all P frames // If interval == 0, all frames are encoded as I frames. No P frames -// If interval > 0, it is the time spacing between 2 neighboring I frames +// If interval > 0, it is the time spacing (seconds) between 2 neighboring I frames status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) { LOGV("setParamVideoIFramesInterval: %d seconds", interval); mIFramesInterval = interval; @@ -396,6 +409,7 @@ status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) { status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) { LOGV("setParamTrackFrameStatus: %d", nFrames); if (nFrames <= 0) { + LOGE("Invalid number of frames to track: %d", nFrames); return BAD_VALUE; } mTrackEveryNumberOfFrames = nFrames; @@ -405,6 +419,7 @@ status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) { status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { LOGV("setParamTrackTimeStatus: %lld", timeDurationUs); if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms? + LOGE("Tracking time duration too short: %lld us", timeDurationUs); return BAD_VALUE; } mTrackEveryTimeDurationUs = timeDurationUs; @@ -417,14 +432,12 @@ status_t StagefrightRecorder::setParameter( if (key == "max-duration") { int64_t max_duration_ms; if (safe_strtoi64(value.string(), &max_duration_ms)) { - return setParamMaxDurationOrFileSize( - max_duration_ms, true /* limit_is_duration */); + return setParamMaxFileDurationUs(1000LL * max_duration_ms); } } else if (key == "max-filesize") { int64_t max_filesize_bytes; if (safe_strtoi64(value.string(), &max_filesize_bytes)) { - return setParamMaxDurationOrFileSize( - max_filesize_bytes, false /* limit is filesize */); + return setParamMaxFileSizeBytes(max_filesize_bytes); } } else if (key == "interleave-duration-us") { int32_t durationUs; @@ -528,7 +541,10 @@ status_t StagefrightRecorder::prepare() { } status_t StagefrightRecorder::start() { + CHECK(mOutputFd >= 0); + if (mWriter != NULL) { + LOGE("File writer is not avaialble"); return UNKNOWN_ERROR; } @@ -547,6 +563,7 @@ status_t StagefrightRecorder::start() { return startAACRecording(); default: + LOGE("Unsupported output file format: %d", mOutputFormat); return UNKNOWN_ERROR; } } @@ -610,7 +627,6 @@ status_t StagefrightRecorder::startAACRecording() { CHECK(mAudioEncoder == AUDIO_ENCODER_AAC); CHECK(mAudioSource != AUDIO_SOURCE_LIST_END); - CHECK(mOutputFd >= 0); CHECK(0 == "AACWriter is not implemented yet"); @@ -626,34 +642,34 @@ status_t StagefrightRecorder::startAMRRecording() { mAudioEncoder != AUDIO_ENCODER_AMR_NB) { LOGE("Invalid encoder %d used for AMRNB recording", mAudioEncoder); - return UNKNOWN_ERROR; + return BAD_VALUE; } if (mSampleRate != 8000) { LOGE("Invalid sampling rate %d used for AMRNB recording", mSampleRate); - return UNKNOWN_ERROR; + return BAD_VALUE; } } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) { LOGE("Invlaid encoder %d used for AMRWB recording", mAudioEncoder); - return UNKNOWN_ERROR; + return BAD_VALUE; } if (mSampleRate != 16000) { LOGE("Invalid sample rate %d used for AMRWB recording", mSampleRate); - return UNKNOWN_ERROR; + return BAD_VALUE; } } if (mAudioChannels != 1) { LOGE("Invalid number of audio channels %d used for amr recording", mAudioChannels); - return UNKNOWN_ERROR; + return BAD_VALUE; } if (mAudioSource >= AUDIO_SOURCE_LIST_END) { LOGE("Invalid audio source: %d", mAudioSource); - return UNKNOWN_ERROR; + return BAD_VALUE; } sp<MediaSource> audioEncoder = createAudioSource(); @@ -662,7 +678,6 @@ status_t StagefrightRecorder::startAMRRecording() { return UNKNOWN_ERROR; } - CHECK(mOutputFd >= 0); mWriter = new AMRWriter(dup(mOutputFd)); mWriter->addSource(audioEncoder); @@ -729,6 +744,54 @@ void StagefrightRecorder::clipVideoFrameWidth() { } } +status_t StagefrightRecorder::setupCameraSource() { + clipVideoBitRate(); + clipVideoFrameRate(); + clipVideoFrameWidth(); + clipVideoFrameHeight(); + + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + if (mCamera == 0) { + mCamera = Camera::connect(mCameraId); + if (mCamera == 0) { + LOGE("Camera connection could not be established."); + return -EBUSY; + } + mFlags &= ~FLAGS_HOT_CAMERA; + mCamera->lock(); + } + + // Set the actual video recording frame size + CameraParameters params(mCamera->getParameters()); + params.setPreviewSize(mVideoWidth, mVideoHeight); + params.setPreviewFrameRate(mFrameRate); + String8 s = params.flatten(); + CHECK_EQ(OK, mCamera->setParameters(s)); + CameraParameters newCameraParams(mCamera->getParameters()); + + // Check on video frame size + int frameWidth = 0, frameHeight = 0; + newCameraParams.getPreviewSize(&frameWidth, &frameHeight); + if (frameWidth < 0 || frameWidth != mVideoWidth || + frameHeight < 0 || frameHeight != mVideoHeight) { + LOGE("Failed to set the video frame size to %dx%d", + mVideoWidth, mVideoHeight); + IPCThreadState::self()->restoreCallingIdentity(token); + return UNKNOWN_ERROR; + } + + // Check on video frame rate + int frameRate = newCameraParams.getPreviewFrameRate(); + if (frameRate < 0 || (frameRate - mFrameRate) != 0) { + LOGE("Failed to set frame rate to %d fps. The actual " + "frame rate is %d", mFrameRate, frameRate); + } + + CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface)); + IPCThreadState::self()->restoreCallingIdentity(token); + return OK; +} + void StagefrightRecorder::clipVideoFrameHeight() { LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder); int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( @@ -746,146 +809,113 @@ void StagefrightRecorder::clipVideoFrameHeight() { } } -status_t StagefrightRecorder::startMPEG4Recording() { - mWriter = new MPEG4Writer(dup(mOutputFd)); - int32_t totalBitRate = 0; - - // Add audio source first if it exists - if (mAudioSource != AUDIO_SOURCE_LIST_END) { - sp<MediaSource> audioEncoder; - switch(mAudioEncoder) { - case AUDIO_ENCODER_AMR_NB: - case AUDIO_ENCODER_AMR_WB: - case AUDIO_ENCODER_AAC: - audioEncoder = createAudioSource(); - break; - default: - LOGE("Unsupported audio encoder: %d", mAudioEncoder); - return UNKNOWN_ERROR; - } +status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) { + status_t err = setupCameraSource(); + if (err != OK) return err; - if (audioEncoder == NULL) { - return UNKNOWN_ERROR; - } - totalBitRate += mAudioBitRate; - mWriter->addSource(audioEncoder); + sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera); + CHECK(cameraSource != NULL); + if(mCaptureTimeLapse) { + cameraSource->enableTimeLapseMode(1E6, mFrameRate); } - if (mVideoSource == VIDEO_SOURCE_DEFAULT - || mVideoSource == VIDEO_SOURCE_CAMERA) { - - clipVideoBitRate(); - clipVideoFrameRate(); - clipVideoFrameWidth(); - clipVideoFrameHeight(); - - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - if (mCamera == 0) { - mCamera = Camera::connect(mCameraId); - if (mCamera == 0) { - LOGE("Camera connection could not be established."); - return -EBUSY; - } - mFlags &= ~FLAGS_HOT_CAMERA; - mCamera->lock(); - } + sp<MetaData> enc_meta = new MetaData; + enc_meta->setInt32(kKeyBitRate, mVideoBitRate); + enc_meta->setInt32(kKeySampleRate, mFrameRate); - // Set the actual video recording frame size - CameraParameters params(mCamera->getParameters()); - params.setPreviewSize(mVideoWidth, mVideoHeight); - params.setPreviewFrameRate(mFrameRate); - String8 s = params.flatten(); - CHECK_EQ(OK, mCamera->setParameters(s)); - CameraParameters newCameraParams(mCamera->getParameters()); - - // Check on video frame size - int frameWidth = 0, frameHeight = 0; - newCameraParams.getPreviewSize(&frameWidth, &frameHeight); - if (frameWidth < 0 || frameWidth != mVideoWidth || - frameHeight < 0 || frameHeight != mVideoHeight) { - LOGE("Failed to set the video frame size to %dx%d", - mVideoWidth, mVideoHeight); - IPCThreadState::self()->restoreCallingIdentity(token); - return UNKNOWN_ERROR; - } - - // Check on video frame rate - int frameRate = newCameraParams.getPreviewFrameRate(); - if (frameRate < 0 || (frameRate - mFrameRate) != 0) { - LOGE("Failed to set frame rate to %d fps. The actual " - "frame rate is %d", mFrameRate, frameRate); - } - - CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface)); - IPCThreadState::self()->restoreCallingIdentity(token); + switch (mVideoEncoder) { + case VIDEO_ENCODER_H263: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); + break; - sp<CameraSource> cameraSource = - CameraSource::CreateFromCamera(mCamera); + case VIDEO_ENCODER_MPEG_4_SP: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + break; - CHECK(cameraSource != NULL); + case VIDEO_ENCODER_H264: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + break; - sp<MetaData> enc_meta = new MetaData; - enc_meta->setInt32(kKeyBitRate, mVideoBitRate); - enc_meta->setInt32(kKeySampleRate, mFrameRate); + default: + CHECK(!"Should not be here, unsupported video encoding."); + break; + } - switch (mVideoEncoder) { - case VIDEO_ENCODER_H263: - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); - break; + sp<MetaData> meta = cameraSource->getFormat(); - case VIDEO_ENCODER_MPEG_4_SP: - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); - break; + int32_t width, height, stride, sliceHeight; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); - case VIDEO_ENCODER_H264: - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); - break; + enc_meta->setInt32(kKeyWidth, width); + enc_meta->setInt32(kKeyHeight, height); + enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval); + enc_meta->setInt32(kKeyStride, stride); + enc_meta->setInt32(kKeySliceHeight, sliceHeight); - default: - CHECK(!"Should not be here, unsupported video encoding."); - break; - } + OMXClient client; + CHECK_EQ(client.connect(), OK); - sp<MetaData> meta = cameraSource->getFormat(); + sp<MediaSource> encoder = OMXCodec::Create( + client.interface(), enc_meta, + true /* createEncoder */, cameraSource); + if (encoder == NULL) { + return UNKNOWN_ERROR; + } - int32_t width, height, stride, sliceHeight; - CHECK(meta->findInt32(kKeyWidth, &width)); - CHECK(meta->findInt32(kKeyHeight, &height)); - CHECK(meta->findInt32(kKeyStride, &stride)); - CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + writer->addSource(encoder); + return OK; +} - enc_meta->setInt32(kKeyWidth, width); - enc_meta->setInt32(kKeyHeight, height); - enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval); - enc_meta->setInt32(kKeyStride, stride); - enc_meta->setInt32(kKeySliceHeight, sliceHeight); +status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { + sp<MediaSource> audioEncoder; + switch(mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_AMR_WB: + case AUDIO_ENCODER_AAC: + audioEncoder = createAudioSource(); + break; + default: + LOGE("Unsupported audio encoder: %d", mAudioEncoder); + return UNKNOWN_ERROR; + } - OMXClient client; - CHECK_EQ(client.connect(), OK); + if (audioEncoder == NULL) { + return UNKNOWN_ERROR; + } + writer->addSource(audioEncoder); + return OK; +} - sp<MediaSource> encoder = - OMXCodec::Create( - client.interface(), enc_meta, - true /* createEncoder */, cameraSource); +status_t StagefrightRecorder::startMPEG4Recording() { + int32_t totalBitRate = 0; + status_t err = OK; + sp<MediaWriter> writer = new MPEG4Writer(dup(mOutputFd)); - CHECK(mOutputFd >= 0); + // Add audio source first if it exists + if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_LIST_END)) { + err = setupAudioEncoder(writer); + if (err != OK) return err; + totalBitRate += mAudioBitRate; + } + if (mVideoSource == VIDEO_SOURCE_DEFAULT + || mVideoSource == VIDEO_SOURCE_CAMERA) { + err = setupVideoEncoder(writer); + if (err != OK) return err; totalBitRate += mVideoBitRate; - mWriter->addSource(encoder); } - { - // MPEGWriter specific handling - MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); - writer->setInterleaveDuration(mInterleaveDurationUs); - } + reinterpret_cast<MPEG4Writer *>(writer.get())-> + setInterleaveDuration(mInterleaveDurationUs); if (mMaxFileDurationUs != 0) { - mWriter->setMaxFileDuration(mMaxFileDurationUs); + writer->setMaxFileDuration(mMaxFileDurationUs); } if (mMaxFileSizeBytes != 0) { - mWriter->setMaxFileSize(mMaxFileSizeBytes); + writer->setMaxFileSize(mMaxFileSizeBytes); } - mWriter->setListener(mListener); sp<MetaData> meta = new MetaData; meta->setInt64(kKeyTime, systemTime() / 1000); meta->setInt32(kKeyFileType, mOutputFormat); @@ -897,8 +927,9 @@ status_t StagefrightRecorder::startMPEG4Recording() { if (mTrackEveryTimeDurationUs > 0) { meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs); } - mWriter->start(meta.get()); - return OK; + writer->setListener(mListener); + mWriter = writer; + return mWriter->start(meta.get()); } status_t StagefrightRecorder::pause() { @@ -914,7 +945,7 @@ status_t StagefrightRecorder::stop() { LOGV("stop"); if (mWriter != NULL) { mWriter->stop(); - mWriter = NULL; + mWriter.clear(); } if (mCamera != 0) { @@ -925,7 +956,7 @@ status_t StagefrightRecorder::stop() { mCamera->stopPreview(); } mCamera->unlock(); - mCamera = NULL; + mCamera.clear(); IPCThreadState::self()->restoreCallingIdentity(token); mFlags = 0; } @@ -966,6 +997,7 @@ status_t StagefrightRecorder::reset() { mCameraId = 0; mTrackEveryNumberOfFrames = 0; mTrackEveryTimeDurationUs = 0; + mCaptureTimeLapse = false; mEncoderProfiles = MediaProfiles::getInstance(); mOutputFd = -1; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index f4488b641c3f..dcb474798dd9 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -87,6 +87,8 @@ private: int32_t mTrackEveryNumberOfFrames; int64_t mTrackEveryTimeDurationUs; + bool mCaptureTimeLapse; + String8 mParams; int mOutputFd; int32_t mFlags; @@ -97,6 +99,11 @@ private: status_t startAMRRecording(); status_t startAACRecording(); sp<MediaSource> createAudioSource(); + status_t setupCameraSource(); + status_t setupAudioEncoder(const sp<MediaWriter>& writer); + status_t setupVideoEncoder(const sp<MediaWriter>& writer); + + // Encoding parameter handling utilities status_t setParameter(const String8 &key, const String8 &value); status_t setParamAudioEncodingBitRate(int32_t bitRate); status_t setParamAudioNumberOfChannels(int32_t channles); @@ -108,7 +115,8 @@ private: status_t setParamTrackFrameStatus(int32_t nFrames); status_t setParamInterleaveDuration(int32_t durationUs); status_t setParam64BitFileOffset(bool use64BitFileOffset); - status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration); + status_t setParamMaxFileDurationUs(int64_t timeUs); + status_t setParamMaxFileSizeBytes(int64_t bytes); void clipVideoBitRate(); void clipVideoFrameRate(); void clipVideoFrameWidth(); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 6f4c980960e9..bb53d97b3b4f 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -116,6 +116,19 @@ CameraSource *CameraSource::CreateFromCamera(const sp<Camera> &camera) { return new CameraSource(camera); } +void CameraSource::enableTimeLapseMode( + int64_t timeBetweenTimeLapseFrameCaptureUs, int32_t videoFrameRate) { + LOGV("starting time lapse mode"); + mTimeBetweenTimeLapseFrameCaptureUs = timeBetweenTimeLapseFrameCaptureUs; + mTimeBetweenTimeLapseVideoFramesUs = (1E6/videoFrameRate); +} + +void CameraSource::disableTimeLapseMode() { + LOGV("stopping time lapse mode"); + mTimeBetweenTimeLapseFrameCaptureUs = -1; + mTimeBetweenTimeLapseVideoFramesUs = 0; +} + CameraSource::CameraSource(const sp<Camera> &camera) : mCamera(camera), mFirstFrameTimeUs(0), @@ -126,7 +139,10 @@ CameraSource::CameraSource(const sp<Camera> &camera) mNumGlitches(0), mGlitchDurationThresholdUs(200000), mCollectStats(false), - mStarted(false) { + mStarted(false), + mTimeBetweenTimeLapseFrameCaptureUs(-1), + mTimeBetweenTimeLapseVideoFramesUs(0), + mLastTimeLapseFrameRealTimestampUs(0) { int64_t token = IPCThreadState::self()->clearCallingIdentity(); String8 s = mCamera->getParameters(); @@ -316,6 +332,35 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, ++mNumGlitches; } + // time lapse + if(mTimeBetweenTimeLapseFrameCaptureUs >= 0) { + if(mLastTimeLapseFrameRealTimestampUs == 0) { + // First time lapse frame. Initialize mLastTimeLapseFrameRealTimestampUs + // to current time (timestampUs) and save frame data. + LOGV("dataCallbackTimestamp timelapse: initial frame"); + + mLastTimeLapseFrameRealTimestampUs = timestampUs; + } else if (timestampUs < + (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) { + // Skip all frames from last encoded frame until + // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed. + // Tell the camera to release its recording frame and return. + LOGV("dataCallbackTimestamp timelapse: skipping intermediate frame"); + + releaseOneRecordingFrame(data); + return; + } else { + // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time: + // - Reset mLastTimeLapseFrameRealTimestampUs to current time. + // - Artificially modify timestampUs to be one frame time (1/framerate) ahead + // of the last encoded frame's time stamp. + LOGV("dataCallbackTimestamp timelapse: got timelapse frame"); + + mLastTimeLapseFrameRealTimestampUs = timestampUs; + timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs; + } + } + mLastFrameTimestampUs = timestampUs; if (mNumFramesReceived == 0) { mFirstFrameTimeUs = timestampUs; diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp index 831fa2a2ba3b..cd4f34992f0d 100644 --- a/media/libstagefright/foundation/ALooper.cpp +++ b/media/libstagefright/foundation/ALooper.cpp @@ -31,8 +31,9 @@ namespace android { ALooperRoster gLooperRoster; struct ALooper::LooperThread : public Thread { - LooperThread(ALooper *looper) - : mLooper(looper) { + LooperThread(ALooper *looper, bool canCallJava) + : Thread(canCallJava), + mLooper(looper) { } virtual bool threadLoop() { @@ -72,7 +73,7 @@ void ALooper::unregisterHandler(handler_id handlerID) { gLooperRoster.unregisterHandler(handlerID); } -status_t ALooper::start(bool runOnCallingThread) { +status_t ALooper::start(bool runOnCallingThread, bool canCallJava) { if (runOnCallingThread) { { Mutex::Autolock autoLock(mLock); @@ -96,7 +97,7 @@ status_t ALooper::start(bool runOnCallingThread) { return INVALID_OPERATION; } - mThread = new LooperThread(this); + mThread = new LooperThread(this, canCallJava); status_t err = mThread->run("ALooper"); if (err != OK) { diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index dfd1ae335098..26c6d42ca606 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -16,6 +16,8 @@ #include "AMessage.h" +#include <ctype.h> + #include "AAtomizer.h" #include "ADebug.h" #include "ALooperRoster.h" @@ -238,4 +240,105 @@ sp<AMessage> AMessage::dup() const { return msg; } +static void appendIndent(AString *s, int32_t indent) { + static const char kWhitespace[] = + " " + " "; + + CHECK_LT((size_t)indent, sizeof(kWhitespace)); + + s->append(kWhitespace, indent); +} + +static bool isFourcc(uint32_t what) { + return isprint(what & 0xff) + && isprint((what >> 8) & 0xff) + && isprint((what >> 16) & 0xff) + && isprint((what >> 24) & 0xff); +} + +AString AMessage::debugString(int32_t indent) const { + AString s = "AMessage(what = "; + + AString tmp; + if (isFourcc(mWhat)) { + tmp = StringPrintf( + "'%c%c%c%c'", + (char)(mWhat >> 24), + (char)((mWhat >> 16) & 0xff), + (char)((mWhat >> 8) & 0xff), + (char)(mWhat & 0xff)); + } else { + tmp = StringPrintf("0x%08x", mWhat); + } + s.append(tmp); + + if (mTarget != 0) { + tmp = StringPrintf(", target = %d", mTarget); + s.append(tmp); + } + s.append(") = {\n"); + + for (size_t i = 0; i < mNumItems; ++i) { + const Item &item = mItems[i]; + + switch (item.mType) { + case kTypeInt32: + tmp = StringPrintf( + "int32_t %s = %d", item.mName, item.u.int32Value); + break; + case kTypeInt64: + tmp = StringPrintf( + "int64_t %s = %lld", item.mName, item.u.int64Value); + break; + case kTypeSize: + tmp = StringPrintf( + "size_t %s = %d", item.mName, item.u.sizeValue); + break; + case kTypeFloat: + tmp = StringPrintf( + "float %s = %f", item.mName, item.u.floatValue); + break; + case kTypeDouble: + tmp = StringPrintf( + "double %s = %f", item.mName, item.u.doubleValue); + break; + case kTypePointer: + tmp = StringPrintf( + "void *%s = %p", item.mName, item.u.ptrValue); + break; + case kTypeString: + tmp = StringPrintf( + "string %s = \"%s\"", + item.mName, + item.u.stringValue->c_str()); + break; + case kTypeObject: + tmp = StringPrintf( + "RefBase *%s = %p", item.mName, item.u.refValue); + break; + case kTypeMessage: + tmp = StringPrintf( + "AMessage %s = %s", + item.mName, + static_cast<AMessage *>( + item.u.refValue)->debugString( + indent + strlen(item.mName) + 14).c_str()); + break; + default: + TRESPASS(); + } + + appendIndent(&s, indent); + s.append(" "); + s.append(tmp); + s.append("\n"); + } + + appendIndent(&s, indent); + s.append("}"); + + return s; +} + } // namespace android diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk index 3c9909b46a6f..13a643054cc2 100644 --- a/media/mtp/Android.mk +++ b/media/mtp/Android.mk @@ -32,6 +32,7 @@ LOCAL_SRC_FILES:= \ MtpRequestPacket.cpp \ MtpResponsePacket.cpp \ MtpServer.cpp \ + MtpSqliteDatabase.cpp \ MtpStorageInfo.cpp \ MtpStringBuffer.cpp \ MtpStorage.cpp \ @@ -70,18 +71,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := scantest LOCAL_SRC_FILES:= \ scantest.cpp \ - MtpMediaScanner.cpp \ - MtpDatabase.cpp \ - MtpDataPacket.cpp \ - MtpPacket.cpp \ - MtpStringBuffer.cpp \ - MtpUtils.cpp \ - SqliteDatabase.cpp \ - SqliteStatement.cpp \ -#LOCAL_STATIC_LIBRARIES := libusbhost -#LOCAL_LDLIBS := -lpthread +LOCAL_STATIC_LIBRARIES := libmtp LOCAL_C_INCLUDES := external/sqlite/dist LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright libmedia diff --git a/media/mtp/MtpDatabase.cpp b/media/mtp/MtpDatabase.cpp index 0f9b898448ec..eabd9937311f 100644 --- a/media/mtp/MtpDatabase.cpp +++ b/media/mtp/MtpDatabase.cpp @@ -18,196 +18,14 @@ #include "MtpDebug.h" #include "MtpDatabase.h" -#include "MtpDataPacket.h" -#include "MtpUtils.h" -#include "SqliteDatabase.h" -#include "SqliteStatement.h" - -#include <stdio.h> -#include <stdlib.h> -#include <sqlite3.h> +#include "MtpTypes.h" +#include "mtp.h" namespace android { -#define FILE_ID_COLUMN 1 -#define FILE_PATH_COLUMN 2 -#define FILE_FORMAT_COLUMN 3 -#define FILE_PARENT_COLUMN 4 -#define FILE_STORAGE_COLUMN 5 -#define FILE_SIZE_COLUMN 6 -#define FILE_MODIFIED_COLUMN 7 - -#define AUDIO_ID_COLUMN 1 -#define AUDIO_TITLE_COLUMN 2 -#define AUDIO_ARTIST_COLUMN 3 -#define AUDIO_ALBUM_COLUMN 4 -#define AUDIO_ALBUM_ARTIST_COLUMN 5 -#define AUDIO_GENRE_COLUMN 6 -#define AUDIO_COMPOSER_COLUMN 7 -#define AUDIO_TRACK_NUMBER_COLUMN 8 -#define AUDIO_YEAR_COLUMN 9 -#define AUDIO_DURATION_COLUMN 10 -#define AUDIO_USE_COUNT_COLUMN 11 -#define AUDIO_SAMPLE_RATE_COLUMN 12 -#define AUDIO_NUM_CHANNELS_COLUMN 13 -#define AUDIO_AUDIO_WAVE_CODEC_COLUMN 14 -#define AUDIO_AUDIO_BIT_RATE_COLUMN 15 - -#define FILE_TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \ - "_id INTEGER PRIMARY KEY," \ - "path TEXT," \ - "format INTEGER," \ - "parent INTEGER," \ - "storage INTEGER," \ - "size INTEGER," \ - "date_modified INTEGER" \ - ");" - -#define AUDIO_TABLE_CREATE "CREATE TABLE IF NOT EXISTS audio (" \ - "id INTEGER PRIMARY KEY," \ - "title TEXT," \ - "artist TEXT," \ - "album TEXT," \ - "album_artist TEXT," \ - "genre TEXT," \ - "composer TEXT," \ - "track_number INTEGER," \ - "year INTEGER," \ - "duration INTEGER," \ - "use_count INTEGER," \ - "sample_rate INTEGER," \ - "num_channels INTEGER," \ - "audio_wave_codec TEXT," \ - "audio_bit_rate INTEGER" \ - ");" - -#define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);" - -#define FILE_ID_QUERY "SELECT _id,format FROM files WHERE path = ?;" -#define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?" - -#define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_modified FROM files WHERE _id = ?;" -#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?);" -#define FILE_DELETE "DELETE FROM files WHERE _id = ?;" - -#define AUDIO_INSERT "INSERT INTO audio VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);" -#define AUDIO_DELETE "DELETE FROM audio WHERE id = ?;" - -struct PropertyTableEntry { - MtpObjectProperty property; - int type; - const char* columnName; -}; - -static const PropertyTableEntry kPropertyTable[] = { - { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" }, - { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" }, - { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" }, - { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" }, - { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" }, - { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" }, -}; - -static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) { - int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]); - const PropertyTableEntry* entry = kPropertyTable; - for (int i = 0; i < count; i++, entry++) { - if (entry->property == property) { - type = entry->type; - columnName = entry->columnName; - return true; - } - } - return false; -} - - - -MtpDatabase::MtpDatabase() - : mFileIdQuery(NULL), - mFilePathQuery(NULL), - mObjectInfoQuery(NULL), - mFileInserter(NULL), - mFileDeleter(NULL), - mAudioInserter(NULL), - mAudioDeleter(NULL) -{ -} - MtpDatabase::~MtpDatabase() { } -bool MtpDatabase::open(const char* path, bool create) { - if (!SqliteDatabase::open(path, create)) - return false; - - // create tables and indices if necessary - if (!exec(FILE_TABLE_CREATE)) { - LOGE("could not create file table"); - return false; - } - if (!exec(PATH_INDEX_CREATE)) { - LOGE("could not path index on file table"); - return false; - } - if (!exec(AUDIO_TABLE_CREATE)) { - LOGE("could not create file table"); - return false; - } - - if (!mFileIdQuery) { - mFileIdQuery = new SqliteStatement(this); - if (!mFileIdQuery->prepare(FILE_ID_QUERY)) { - LOGE("could not compile FILE_ID_QUERY"); - exit(-1); - } - } - if (!mFilePathQuery) { - mFilePathQuery = new SqliteStatement(this); - if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) { - LOGE("could not compile FILE_PATH_QUERY"); - exit(-1); - } - } - if (!mObjectInfoQuery) { - mObjectInfoQuery = new SqliteStatement(this); - if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) { - LOGE("could not compile GET_OBJECT_INFO_QUERY"); - exit(-1); - } - } - if (!mFileInserter) { - mFileInserter = new SqliteStatement(this); - if (!mFileInserter->prepare(FILE_INSERT)) { - LOGE("could not compile FILE_INSERT\n"); - exit(-1); - } - } - if (!mFileDeleter) { - mFileDeleter = new SqliteStatement(this); - if (!mFileDeleter->prepare(FILE_DELETE)) { - LOGE("could not compile FILE_DELETE\n"); - exit(-1); - } - } - if (!mAudioInserter) { - mAudioInserter = new SqliteStatement(this); - if (!mAudioInserter->prepare(AUDIO_INSERT)) { - LOGE("could not compile AUDIO_INSERT\n"); - exit(-1); - } - } - if (!mAudioDeleter) { - mAudioDeleter = new SqliteStatement(this); - if (!mAudioDeleter->prepare(AUDIO_DELETE)) { - LOGE("could not compile AUDIO_DELETE\n"); - exit(-1); - } - } - - return true; -} - uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) { switch (format) { case MTP_FORMAT_AIFF: @@ -260,322 +78,4 @@ uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) { } } -MtpObjectHandle MtpDatabase::getObjectHandle(const char* path) { - mFileIdQuery->reset(); - mFileIdQuery->bind(1, path); - if (mFileIdQuery->step()) { - int row = mFileIdQuery->getColumnInt(0); - if (row > 0) { - MtpObjectFormat format = mFileIdQuery->getColumnInt(1); - row |= getTableForFile(format); - return row; - } - } - - return 0; -} - -MtpObjectHandle MtpDatabase::addFile(const char* path, - MtpObjectFormat format, - MtpObjectHandle parent, - MtpStorageID storage, - uint64_t size, - time_t modified) { - mFileInserter->bind(FILE_PATH_COLUMN, path); - mFileInserter->bind(FILE_FORMAT_COLUMN, format); - mFileInserter->bind(FILE_PARENT_COLUMN, parent); - mFileInserter->bind(FILE_STORAGE_COLUMN, storage); - mFileInserter->bind(FILE_SIZE_COLUMN, size); - mFileInserter->bind(FILE_MODIFIED_COLUMN, modified); - mFileInserter->step(); - mFileInserter->reset(); - int result = lastInsertedRow(); - return (result <= 0 ? kInvalidObjectHandle : result); -} - -MtpObjectHandle MtpDatabase::addAudioFile(MtpObjectHandle handle) { - mAudioInserter->bind(AUDIO_ID_COLUMN, handle); - mAudioInserter->step(); - mAudioInserter->reset(); - int result = lastInsertedRow(); - handle |= kObjectHandleTableAudio; - return (result > 0 ? handle : kInvalidObjectHandle); -} - -MtpObjectHandle MtpDatabase::addAudioFile(MtpObjectHandle handle, - const char* title, - const char* artist, - const char* album, - const char* albumArtist, - const char* genre, - const char* composer, - const char* mimeType, - int track, - int year, - int duration) { - mAudioInserter->bind(AUDIO_ID_COLUMN, handle); - if (title) mAudioInserter->bind(AUDIO_TITLE_COLUMN, title); - if (artist) mAudioInserter->bind(AUDIO_ARTIST_COLUMN, artist); - if (album) mAudioInserter->bind(AUDIO_ALBUM_COLUMN, album); - if (albumArtist) mAudioInserter->bind(AUDIO_ALBUM_ARTIST_COLUMN, albumArtist); - if (genre) mAudioInserter->bind(AUDIO_GENRE_COLUMN, genre); - if (composer) mAudioInserter->bind(AUDIO_COMPOSER_COLUMN, composer); - if (track) mAudioInserter->bind(AUDIO_TRACK_NUMBER_COLUMN, track); - if (year) mAudioInserter->bind(AUDIO_YEAR_COLUMN, year); - if (duration) mAudioInserter->bind(AUDIO_DURATION_COLUMN, duration); - mAudioInserter->step(); - mAudioInserter->reset(); - int result = lastInsertedRow(); - if (result <= 0) - return kInvalidObjectHandle; - result |= kObjectHandleTableAudio; - return result; -} - -MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID, - MtpObjectFormat format, - MtpObjectHandle parent) { - bool whereStorage = (storageID != 0xFFFFFFFF); - bool whereFormat = (format != 0); - bool whereParent = (parent != 0); - char intBuffer[20]; - - MtpString query("SELECT _id,format FROM files"); - if (whereStorage || whereFormat || whereParent) - query += " WHERE"; - if (whereStorage) { - snprintf(intBuffer, sizeof(intBuffer), "%d", storageID); - query += " storage = "; - query += intBuffer; - } - if (whereFormat) { - snprintf(intBuffer, sizeof(intBuffer), "%d", format); - if (whereStorage) - query += " AND"; - query += " format = "; - query += intBuffer; - } - if (whereParent) { - if (parent != MTP_PARENT_ROOT) - parent &= kObjectHandleIndexMask; - snprintf(intBuffer, sizeof(intBuffer), "%d", parent); - if (whereStorage || whereFormat) - query += " AND"; - query += " parent = "; - query += intBuffer; - } - query += ";"; - - SqliteStatement stmt(this); - LOGV("%s", (const char *)query); - stmt.prepare(query); - - MtpObjectHandleList* list = new MtpObjectHandleList(); - while (!stmt.isDone()) { - if (stmt.step()) { - int index = stmt.getColumnInt(0); - LOGV("stmt.getColumnInt returned %d", index); - if (index > 0) { - MtpObjectFormat format = stmt.getColumnInt(1); - index |= getTableForFile(format); - list->push(index); - } - } - } - LOGV("list size: %d", list->size()); - return list; -} - - -MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle, - MtpObjectProperty property, - MtpDataPacket& packet) { - int type; - const char* columnName; - char intBuffer[20]; - - if (handle != MTP_PARENT_ROOT) - handle &= kObjectHandleIndexMask; - - if (!getPropertyInfo(property, type, columnName)) - return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; - snprintf(intBuffer, sizeof(intBuffer), "%d", handle); - - MtpString query("SELECT "); - query += columnName; - query += " FROM files WHERE _id = "; - query += intBuffer; - query += ";"; - - SqliteStatement stmt(this); - LOGV("%s", (const char *)query); - stmt.prepare(query); - - if (!stmt.step()) - return MTP_RESPONSE_INVALID_OBJECT_HANDLE; - - switch (type) { - case MTP_TYPE_INT8: - packet.putInt8(stmt.getColumnInt(0)); - break; - case MTP_TYPE_UINT8: - packet.putUInt8(stmt.getColumnInt(0)); - break; - case MTP_TYPE_INT16: - packet.putInt16(stmt.getColumnInt(0)); - break; - case MTP_TYPE_UINT16: - packet.putUInt16(stmt.getColumnInt(0)); - break; - case MTP_TYPE_INT32: - packet.putInt32(stmt.getColumnInt(0)); - break; - case MTP_TYPE_UINT32: - packet.putUInt32(stmt.getColumnInt(0)); - break; - case MTP_TYPE_INT64: - packet.putInt64(stmt.getColumnInt64(0)); - break; - case MTP_TYPE_UINT64: - packet.putUInt64(stmt.getColumnInt64(0)); - break; - case MTP_TYPE_STR: - packet.putString(stmt.getColumnString(0)); - break; - default: - LOGE("unsupported object type\n"); - return MTP_RESPONSE_INVALID_OBJECT_HANDLE; - } - return MTP_RESPONSE_OK; -} - -MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle, - MtpDataPacket& packet) { - char date[20]; - - if (handle != MTP_PARENT_ROOT) - handle &= kObjectHandleIndexMask; - - mObjectInfoQuery->reset(); - mObjectInfoQuery->bind(1, handle); - if (!mObjectInfoQuery->step()) - return MTP_RESPONSE_INVALID_OBJECT_HANDLE; - - MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0); - MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1); - MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2); - // extract name from path. do we want a separate database entry for this? - const char* name = mObjectInfoQuery->getColumnString(3); - const char* lastSlash = strrchr(name, '/'); - if (lastSlash) - name = lastSlash + 1; - int64_t size = mObjectInfoQuery->getColumnInt64(4); - time_t modified = mObjectInfoQuery->getColumnInt(5); - int associationType = (format == MTP_FORMAT_ASSOCIATION ? - MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : - MTP_ASSOCIATION_TYPE_UNDEFINED); - - LOGV("storageID: %d, format: %d, parent: %d", storageID, format, parent); - - packet.putUInt32(storageID); - packet.putUInt16(format); - packet.putUInt16(0); // protection status - packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size)); - packet.putUInt16(0); // thumb format - packet.putUInt32(0); // thumb compressed size - packet.putUInt32(0); // thumb pix width - packet.putUInt32(0); // thumb pix height - packet.putUInt32(0); // image pix width - packet.putUInt32(0); // image pix height - packet.putUInt32(0); // image bit depth - packet.putUInt32(parent); - packet.putUInt16(associationType); - packet.putUInt32(0); // association desc - packet.putUInt32(0); // sequence number - packet.putString(name); // file name - packet.putEmptyString(); - formatDateTime(modified, date, sizeof(date)); - packet.putString(date); // date modified - packet.putEmptyString(); // keywords - - return MTP_RESPONSE_OK; -} - -bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle, - MtpString& filePath, - int64_t& fileLength) { - if (handle != MTP_PARENT_ROOT) - handle &= kObjectHandleIndexMask; - mFilePathQuery->reset(); - mFilePathQuery->bind(1, handle); - if (!mFilePathQuery->step()) - return false; - - const char* path = mFilePathQuery->getColumnString(0); - if (!path) - return false; - filePath = path; - fileLength = mFilePathQuery->getColumnInt64(1); - return true; -} - -bool MtpDatabase::deleteFile(MtpObjectHandle handle) { - uint32_t table = handle & kObjectHandleTableMask; - handle &= kObjectHandleIndexMask; - mFileDeleter->bind(1, handle); - mFileDeleter->step(); - mFileDeleter->reset(); - if (table == kObjectHandleTableAudio) { - mAudioDeleter->bind(1, handle); - mAudioDeleter->step(); - mAudioDeleter->reset(); - } - - return true; -} - -MtpObjectHandle* MtpDatabase::getFileList(int& outCount) { - MtpObjectHandle* result = NULL; - int count = 0; - SqliteStatement stmt(this); - stmt.prepare("SELECT count(*) FROM files;"); - - MtpObjectHandleList* list = new MtpObjectHandleList(); - if (stmt.step()) - count = stmt.getColumnInt(0); - - if (count > 0) { - result = new MtpObjectHandle[count]; - memset(result, 0, count * sizeof(*result)); - SqliteStatement stmt2(this); - stmt2.prepare("SELECT _id,format FROM files;"); - - for (int i = 0; i < count; i++) { - if (!stmt2.step()) { - LOGW("getFileList ended early"); - count = i; - break; - } - MtpObjectHandle handle = stmt2.getColumnInt(0); - MtpObjectFormat format = stmt2.getColumnInt(1); - handle |= getTableForFile(format); - result[i] = handle; - } - } - outCount = count; - return result; -} - -/* - for getObjectPropDesc - - packet.putUInt16(property); - packet.putUInt16(dataType); - packet.putUInt8(getSet); - // default value DTS - packet.putUInt32(groupCode); - packet.putUInt8(formFlag); - // form, variable -*/ - } // namespace android diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index 51d5fb136d25..0c70d9fe6b79 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -23,65 +23,57 @@ namespace android { class MtpDataPacket; -class SqliteStatement; - -class MtpDatabase : public SqliteDatabase { -private: - SqliteStatement* mFileIdQuery; - SqliteStatement* mFilePathQuery; - SqliteStatement* mObjectInfoQuery; - SqliteStatement* mFileInserter; - SqliteStatement* mFileDeleter; - SqliteStatement* mAudioInserter; - SqliteStatement* mAudioDeleter; +class MtpDatabase { public: - MtpDatabase(); - virtual ~MtpDatabase(); - - static uint32_t getTableForFile(MtpObjectFormat format); - - bool open(const char* path, bool create); - MtpObjectHandle getObjectHandle(const char* path); - MtpObjectHandle addFile(const char* path, + virtual ~MtpDatabase(); + + static uint32_t getTableForFile(MtpObjectFormat format); + + virtual MtpObjectHandle getObjectHandle(const char* path) = 0; + virtual MtpObjectHandle addFile(const char* path, + MtpObjectFormat format, + MtpObjectHandle parent, + MtpStorageID storage, + uint64_t size, + time_t modified) = 0; + + virtual MtpObjectHandle addAudioFile(MtpObjectHandle id) = 0; + + virtual MtpObjectHandle addAudioFile(MtpObjectHandle id, + const char* title, + const char* artist, + const char* album, + const char* albumArtist, + const char* genre, + const char* composer, + const char* mimeType, + int track, + int year, + int duration) = 0; + + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format, - MtpObjectHandle parent, - MtpStorageID storage, - uint64_t size, - time_t modified); - - MtpObjectHandle addAudioFile(MtpObjectHandle id); - - MtpObjectHandle addAudioFile(MtpObjectHandle id, - const char* title, - const char* artist, - const char* album, - const char* albumArtist, - const char* genre, - const char* composer, - const char* mimeType, - int track, - int year, - int duration); - - MtpObjectHandleList* getObjectList(MtpStorageID storageID, - MtpObjectFormat format, - MtpObjectHandle parent); + MtpObjectHandle parent) = 0; - MtpResponseCode getObjectProperty(MtpObjectHandle handle, - MtpObjectProperty property, - MtpDataPacket& packet); + virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet) = 0; - MtpResponseCode getObjectInfo(MtpObjectHandle handle, - MtpDataPacket& packet); + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, + MtpDataPacket& packet) = 0; - bool getObjectFilePath(MtpObjectHandle handle, - MtpString& filePath, - int64_t& fileLength); - bool deleteFile(MtpObjectHandle handle); + virtual bool getObjectFilePath(MtpObjectHandle handle, + MtpString& filePath, + int64_t& fileLength) = 0; + virtual bool deleteFile(MtpObjectHandle handle) = 0; // helper for media scanner - MtpObjectHandle* getFileList(int& outCount); + virtual MtpObjectHandle* getFileList(int& outCount) = 0; + + virtual void beginTransaction() = 0; + virtual void commitTransaction() = 0; + virtual void rollbackTransaction() = 0; }; }; // namespace android diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 0b99cb19f139..b9eeec5953ca 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -25,9 +25,9 @@ #include <cutils/properties.h> #include "MtpDebug.h" -#include "MtpDatabase.h" #include "MtpProperty.h" #include "MtpServer.h" +#include "MtpSqliteDatabase.h" #include "MtpStorage.h" #include "MtpStringBuffer.h" @@ -113,16 +113,20 @@ static const MtpObjectFormat kSupportedPlaybackFormats[] = { // MTP_FORMAT_PLS_PLAYLIST, }; -MtpServer::MtpServer(int fd, const char* databasePath) +MtpServer::MtpServer(int fd, const char* databasePath, + int fileGroup, int filePerm, int directoryPerm) : mFD(fd), mDatabasePath(databasePath), mDatabase(NULL), + mFileGroup(fileGroup), + mFilePermission(filePerm), + mDirectoryPermission(directoryPerm), mSessionID(0), mSessionOpen(false), mSendObjectHandle(kInvalidObjectHandle), mSendObjectFileSize(0) { - mDatabase = new MtpDatabase(); + mDatabase = new MtpSqliteDatabase(); mDatabase->open(databasePath, true); initObjectProperties(); @@ -536,10 +540,11 @@ MtpResponseCode MtpServer::doSendObjectInfo() { if (format == MTP_FORMAT_ASSOCIATION) { mode_t mask = umask(0); - int ret = mkdir((const char *)path, S_IRWXU | S_IRWXG | S_IRWXO); + int ret = mkdir((const char *)path, mDirectoryPermission); umask(mask); if (ret && ret != -EEXIST) return MTP_RESPONSE_GENERAL_ERROR; + chown((const char *)path, getuid(), mFileGroup); } else { mSendObjectFilePath = path; // save the handle for the SendObject call, which should follow @@ -571,12 +576,19 @@ MtpResponseCode MtpServer::doSendObject() { if (mfr.fd < 0) { return MTP_RESPONSE_GENERAL_ERROR; } + fchown(mfr.fd, getuid(), mFileGroup); + // set permissions + mode_t mask = umask(0); + fchmod(mfr.fd, mFilePermission); + umask(mask); + mfr.offset = 0; mfr.length = mSendObjectFileSize; // transfer the file ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); close(mfr.fd); + // FIXME - we need to delete mSendObjectHandle from the database if this fails. LOGV("MTP_RECEIVE_FILE returned %d", ret); mSendObjectHandle = kInvalidObjectHandle; diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index 40329c573835..25635afbe0ea 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -27,7 +27,7 @@ namespace android { class MtpStorage; -class MtpDatabase; +class MtpSqliteDatabase; class MtpProperty; class MtpServer { @@ -39,7 +39,13 @@ private: // path to our sqlite3 database const char* mDatabasePath; - MtpDatabase* mDatabase; + MtpSqliteDatabase* mDatabase; + + // group to own new files and folders + int mFileGroup; + // permissions for new files and directories + int mFilePermission; + int mDirectoryPermission; // current session ID MtpSessionID mSessionID; @@ -61,7 +67,8 @@ private: size_t mSendObjectFileSize; public: - MtpServer(int fd, const char* databasePath); + MtpServer(int fd, const char* databasePath, + int fileGroup, int filePerm, int directoryPerm); virtual ~MtpServer(); void addStorage(const char* filePath); diff --git a/media/mtp/MtpSqliteDatabase.cpp b/media/mtp/MtpSqliteDatabase.cpp new file mode 100644 index 000000000000..fa3bdfe7cbfb --- /dev/null +++ b/media/mtp/MtpSqliteDatabase.cpp @@ -0,0 +1,564 @@ +/* + * 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 "MtpSqliteDatabase" + +#include "MtpDebug.h" +#include "MtpSqliteDatabase.h" +#include "MtpDataPacket.h" +#include "MtpUtils.h" +#include "SqliteDatabase.h" +#include "SqliteStatement.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sqlite3.h> + +namespace android { + +#define FILE_ID_COLUMN 1 +#define FILE_PATH_COLUMN 2 +#define FILE_FORMAT_COLUMN 3 +#define FILE_PARENT_COLUMN 4 +#define FILE_STORAGE_COLUMN 5 +#define FILE_SIZE_COLUMN 6 +#define FILE_MODIFIED_COLUMN 7 + +#define AUDIO_ID_COLUMN 1 +#define AUDIO_TITLE_COLUMN 2 +#define AUDIO_ARTIST_COLUMN 3 +#define AUDIO_ALBUM_COLUMN 4 +#define AUDIO_ALBUM_ARTIST_COLUMN 5 +#define AUDIO_GENRE_COLUMN 6 +#define AUDIO_COMPOSER_COLUMN 7 +#define AUDIO_TRACK_NUMBER_COLUMN 8 +#define AUDIO_YEAR_COLUMN 9 +#define AUDIO_DURATION_COLUMN 10 +#define AUDIO_USE_COUNT_COLUMN 11 +#define AUDIO_SAMPLE_RATE_COLUMN 12 +#define AUDIO_NUM_CHANNELS_COLUMN 13 +#define AUDIO_AUDIO_WAVE_CODEC_COLUMN 14 +#define AUDIO_AUDIO_BIT_RATE_COLUMN 15 + +#define FILE_TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \ + "_id INTEGER PRIMARY KEY," \ + "path TEXT," \ + "format INTEGER," \ + "parent INTEGER," \ + "storage INTEGER," \ + "size INTEGER," \ + "date_modified INTEGER" \ + ");" + +#define AUDIO_TABLE_CREATE "CREATE TABLE IF NOT EXISTS audio (" \ + "id INTEGER PRIMARY KEY," \ + "title TEXT," \ + "artist TEXT," \ + "album TEXT," \ + "album_artist TEXT," \ + "genre TEXT," \ + "composer TEXT," \ + "track_number INTEGER," \ + "year INTEGER," \ + "duration INTEGER," \ + "use_count INTEGER," \ + "sample_rate INTEGER," \ + "num_channels INTEGER," \ + "audio_wave_codec TEXT," \ + "audio_bit_rate INTEGER" \ + ");" + +#define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);" + +#define FILE_ID_QUERY "SELECT _id,format FROM files WHERE path = ?;" +#define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?" + +#define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_modified FROM files WHERE _id = ?;" +#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?);" +#define FILE_DELETE "DELETE FROM files WHERE _id = ?;" + +#define AUDIO_INSERT "INSERT INTO audio VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);" +#define AUDIO_DELETE "DELETE FROM audio WHERE id = ?;" + +struct PropertyTableEntry { + MtpObjectProperty property; + int type; + const char* columnName; +}; + +static const PropertyTableEntry kPropertyTable[] = { + { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" }, + { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" }, + { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" }, + { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" }, + { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" }, +}; + +static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) { + int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]); + const PropertyTableEntry* entry = kPropertyTable; + for (int i = 0; i < count; i++, entry++) { + if (entry->property == property) { + type = entry->type; + columnName = entry->columnName; + return true; + } + } + return false; +} + +MtpSqliteDatabase::MtpSqliteDatabase() + : mDatabase(NULL), + mFileIdQuery(NULL), + mFilePathQuery(NULL), + mObjectInfoQuery(NULL), + mFileInserter(NULL), + mFileDeleter(NULL), + mAudioInserter(NULL), + mAudioDeleter(NULL) +{ +} + +MtpSqliteDatabase::~MtpSqliteDatabase() { + delete mDatabase; + delete mFileIdQuery; + delete mFilePathQuery; + delete mObjectInfoQuery; + delete mFileInserter; + delete mFileDeleter; + delete mAudioInserter; + delete mAudioDeleter; +} + +bool MtpSqliteDatabase::open(const char* path, bool create) { + mDatabase = new SqliteDatabase; + + if (!mDatabase->open(path, create)) + goto fail; + + // create tables and indices if necessary + if (!mDatabase->exec(FILE_TABLE_CREATE)) { + LOGE("could not create file table"); + goto fail; + } + if (!mDatabase->exec(PATH_INDEX_CREATE)) { + LOGE("could not path index on file table"); + goto fail; + } + if (!mDatabase->exec(AUDIO_TABLE_CREATE)) { + LOGE("could not create file table"); + goto fail; + } + + if (!mFileIdQuery) { + mFileIdQuery = new SqliteStatement(mDatabase); + if (!mFileIdQuery->prepare(FILE_ID_QUERY)) { + LOGE("could not compile FILE_ID_QUERY"); + goto fail; + } + } + if (!mFilePathQuery) { + mFilePathQuery = new SqliteStatement(mDatabase); + if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) { + LOGE("could not compile FILE_PATH_QUERY"); + goto fail; + } + } + if (!mObjectInfoQuery) { + mObjectInfoQuery = new SqliteStatement(mDatabase); + if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) { + LOGE("could not compile GET_OBJECT_INFO_QUERY"); + goto fail; + } + } + if (!mFileInserter) { + mFileInserter = new SqliteStatement(mDatabase); + if (!mFileInserter->prepare(FILE_INSERT)) { + LOGE("could not compile FILE_INSERT\n"); + goto fail; + } + } + if (!mFileDeleter) { + mFileDeleter = new SqliteStatement(mDatabase); + if (!mFileDeleter->prepare(FILE_DELETE)) { + LOGE("could not compile FILE_DELETE\n"); + goto fail; + } + } + if (!mAudioInserter) { + mAudioInserter = new SqliteStatement(mDatabase); + if (!mAudioInserter->prepare(AUDIO_INSERT)) { + LOGE("could not compile AUDIO_INSERT\n"); + goto fail; + } + } + if (!mAudioDeleter) { + mAudioDeleter = new SqliteStatement(mDatabase); + if (!mAudioDeleter->prepare(AUDIO_DELETE)) { + LOGE("could not compile AUDIO_DELETE\n"); + goto fail; + } + } + + return true; + +fail: + delete mDatabase; + delete mFileIdQuery; + delete mFilePathQuery; + delete mObjectInfoQuery; + delete mFileInserter; + delete mFileDeleter; + delete mAudioInserter; + delete mAudioDeleter; + mDatabase = NULL; + mFileIdQuery = NULL; + mFilePathQuery = NULL; + mObjectInfoQuery = NULL; + mFileInserter = NULL; + mFileDeleter = NULL; + mAudioInserter = NULL; + mAudioDeleter = NULL; + return false; +} + +void MtpSqliteDatabase::close() { + if (mDatabase) { + mDatabase->close(); + mDatabase = NULL; + } +} + +MtpObjectHandle MtpSqliteDatabase::getObjectHandle(const char* path) { + mFileIdQuery->reset(); + mFileIdQuery->bind(1, path); + if (mFileIdQuery->step()) { + int row = mFileIdQuery->getColumnInt(0); + if (row > 0) { + MtpObjectFormat format = mFileIdQuery->getColumnInt(1); + row |= getTableForFile(format); + return row; + } + } + + return 0; +} + +MtpObjectHandle MtpSqliteDatabase::addFile(const char* path, + MtpObjectFormat format, + MtpObjectHandle parent, + MtpStorageID storage, + uint64_t size, + time_t modified) { + mFileInserter->bind(FILE_PATH_COLUMN, path); + mFileInserter->bind(FILE_FORMAT_COLUMN, format); + mFileInserter->bind(FILE_PARENT_COLUMN, parent); + mFileInserter->bind(FILE_STORAGE_COLUMN, storage); + mFileInserter->bind(FILE_SIZE_COLUMN, size); + mFileInserter->bind(FILE_MODIFIED_COLUMN, modified); + mFileInserter->step(); + mFileInserter->reset(); + int result = mDatabase->lastInsertedRow(); + return (result <= 0 ? kInvalidObjectHandle : result); +} + +MtpObjectHandle MtpSqliteDatabase::addAudioFile(MtpObjectHandle handle) { + mAudioInserter->bind(AUDIO_ID_COLUMN, handle); + mAudioInserter->step(); + mAudioInserter->reset(); + int result = mDatabase->lastInsertedRow(); + handle |= kObjectHandleTableAudio; + return (result > 0 ? handle : kInvalidObjectHandle); +} + +MtpObjectHandle MtpSqliteDatabase::addAudioFile(MtpObjectHandle handle, + const char* title, + const char* artist, + const char* album, + const char* albumArtist, + const char* genre, + const char* composer, + const char* mimeType, + int track, + int year, + int duration) { + mAudioInserter->bind(AUDIO_ID_COLUMN, handle); + if (title) mAudioInserter->bind(AUDIO_TITLE_COLUMN, title); + if (artist) mAudioInserter->bind(AUDIO_ARTIST_COLUMN, artist); + if (album) mAudioInserter->bind(AUDIO_ALBUM_COLUMN, album); + if (albumArtist) mAudioInserter->bind(AUDIO_ALBUM_ARTIST_COLUMN, albumArtist); + if (genre) mAudioInserter->bind(AUDIO_GENRE_COLUMN, genre); + if (composer) mAudioInserter->bind(AUDIO_COMPOSER_COLUMN, composer); + if (track) mAudioInserter->bind(AUDIO_TRACK_NUMBER_COLUMN, track); + if (year) mAudioInserter->bind(AUDIO_YEAR_COLUMN, year); + if (duration) mAudioInserter->bind(AUDIO_DURATION_COLUMN, duration); + mAudioInserter->step(); + mAudioInserter->reset(); + int result = mDatabase->lastInsertedRow(); + if (result <= 0) + return kInvalidObjectHandle; + result |= kObjectHandleTableAudio; + return result; +} + +MtpObjectHandleList* MtpSqliteDatabase::getObjectList(MtpStorageID storageID, + MtpObjectFormat format, + MtpObjectHandle parent) { + bool whereStorage = (storageID != 0xFFFFFFFF); + bool whereFormat = (format != 0); + bool whereParent = (parent != 0); + char intBuffer[20]; + + MtpString query("SELECT _id,format FROM files"); + if (whereStorage || whereFormat || whereParent) + query += " WHERE"; + if (whereStorage) { + snprintf(intBuffer, sizeof(intBuffer), "%d", storageID); + query += " storage = "; + query += intBuffer; + } + if (whereFormat) { + snprintf(intBuffer, sizeof(intBuffer), "%d", format); + if (whereStorage) + query += " AND"; + query += " format = "; + query += intBuffer; + } + if (whereParent) { + if (parent != MTP_PARENT_ROOT) + parent &= kObjectHandleIndexMask; + snprintf(intBuffer, sizeof(intBuffer), "%d", parent); + if (whereStorage || whereFormat) + query += " AND"; + query += " parent = "; + query += intBuffer; + } + query += ";"; + + SqliteStatement stmt(mDatabase); + LOGV("%s", (const char *)query); + stmt.prepare(query); + + MtpObjectHandleList* list = new MtpObjectHandleList(); + while (!stmt.isDone()) { + if (stmt.step()) { + int index = stmt.getColumnInt(0); + LOGV("stmt.getColumnInt returned %d", index); + if (index > 0) { + MtpObjectFormat format = stmt.getColumnInt(1); + index |= getTableForFile(format); + list->push(index); + } + } + } + LOGV("list size: %d", list->size()); + return list; +} + + +MtpResponseCode MtpSqliteDatabase::getObjectProperty(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet) { + int type; + const char* columnName; + char intBuffer[20]; + + if (handle != MTP_PARENT_ROOT) + handle &= kObjectHandleIndexMask; + + if (!getPropertyInfo(property, type, columnName)) + return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; + snprintf(intBuffer, sizeof(intBuffer), "%d", handle); + + MtpString query("SELECT "); + query += columnName; + query += " FROM files WHERE _id = "; + query += intBuffer; + query += ";"; + + SqliteStatement stmt(mDatabase); + LOGV("%s", (const char *)query); + stmt.prepare(query); + + if (!stmt.step()) + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + + switch (type) { + case MTP_TYPE_INT8: + packet.putInt8(stmt.getColumnInt(0)); + break; + case MTP_TYPE_UINT8: + packet.putUInt8(stmt.getColumnInt(0)); + break; + case MTP_TYPE_INT16: + packet.putInt16(stmt.getColumnInt(0)); + break; + case MTP_TYPE_UINT16: + packet.putUInt16(stmt.getColumnInt(0)); + break; + case MTP_TYPE_INT32: + packet.putInt32(stmt.getColumnInt(0)); + break; + case MTP_TYPE_UINT32: + packet.putUInt32(stmt.getColumnInt(0)); + break; + case MTP_TYPE_INT64: + packet.putInt64(stmt.getColumnInt64(0)); + break; + case MTP_TYPE_UINT64: + packet.putUInt64(stmt.getColumnInt64(0)); + break; + case MTP_TYPE_STR: + packet.putString(stmt.getColumnString(0)); + break; + default: + LOGE("unsupported object type\n"); + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpSqliteDatabase::getObjectInfo(MtpObjectHandle handle, + MtpDataPacket& packet) { + char date[20]; + + if (handle != MTP_PARENT_ROOT) + handle &= kObjectHandleIndexMask; + + mObjectInfoQuery->reset(); + mObjectInfoQuery->bind(1, handle); + if (!mObjectInfoQuery->step()) + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + + MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0); + MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1); + MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2); + // extract name from path. do we want a separate database entry for this? + const char* name = mObjectInfoQuery->getColumnString(3); + const char* lastSlash = strrchr(name, '/'); + if (lastSlash) + name = lastSlash + 1; + int64_t size = mObjectInfoQuery->getColumnInt64(4); + time_t modified = mObjectInfoQuery->getColumnInt(5); + int associationType = (format == MTP_FORMAT_ASSOCIATION ? + MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : + MTP_ASSOCIATION_TYPE_UNDEFINED); + + LOGV("storageID: %d, format: %d, parent: %d", storageID, format, parent); + + packet.putUInt32(storageID); + packet.putUInt16(format); + packet.putUInt16(0); // protection status + packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size)); + packet.putUInt16(0); // thumb format + packet.putUInt32(0); // thumb compressed size + packet.putUInt32(0); // thumb pix width + packet.putUInt32(0); // thumb pix height + packet.putUInt32(0); // image pix width + packet.putUInt32(0); // image pix height + packet.putUInt32(0); // image bit depth + packet.putUInt32(parent); + packet.putUInt16(associationType); + packet.putUInt32(0); // association desc + packet.putUInt32(0); // sequence number + packet.putString(name); // file name + packet.putEmptyString(); + formatDateTime(modified, date, sizeof(date)); + packet.putString(date); // date modified + packet.putEmptyString(); // keywords + + return MTP_RESPONSE_OK; +} + +bool MtpSqliteDatabase::getObjectFilePath(MtpObjectHandle handle, + MtpString& filePath, + int64_t& fileLength) { + if (handle != MTP_PARENT_ROOT) + handle &= kObjectHandleIndexMask; + mFilePathQuery->reset(); + mFilePathQuery->bind(1, handle); + if (!mFilePathQuery->step()) + return false; + + const char* path = mFilePathQuery->getColumnString(0); + if (!path) + return false; + filePath = path; + fileLength = mFilePathQuery->getColumnInt64(1); + return true; +} + +bool MtpSqliteDatabase::deleteFile(MtpObjectHandle handle) { + uint32_t table = handle & kObjectHandleTableMask; + handle &= kObjectHandleIndexMask; + mFileDeleter->bind(1, handle); + mFileDeleter->step(); + mFileDeleter->reset(); + if (table == kObjectHandleTableAudio) { + mAudioDeleter->bind(1, handle); + mAudioDeleter->step(); + mAudioDeleter->reset(); + } + + return true; +} + +MtpObjectHandle* MtpSqliteDatabase::getFileList(int& outCount) { + MtpObjectHandle* result = NULL; + int count = 0; + SqliteStatement stmt(mDatabase); + stmt.prepare("SELECT count(*) FROM files;"); + + MtpObjectHandleList* list = new MtpObjectHandleList(); + if (stmt.step()) + count = stmt.getColumnInt(0); + + if (count > 0) { + result = new MtpObjectHandle[count]; + memset(result, 0, count * sizeof(*result)); + SqliteStatement stmt2(mDatabase); + stmt2.prepare("SELECT _id,format FROM files;"); + + for (int i = 0; i < count; i++) { + if (!stmt2.step()) { + LOGW("getFileList ended early"); + count = i; + break; + } + MtpObjectHandle handle = stmt2.getColumnInt(0); + MtpObjectFormat format = stmt2.getColumnInt(1); + handle |= getTableForFile(format); + result[i] = handle; + } + } + outCount = count; + return result; +} + +void MtpSqliteDatabase::beginTransaction() { + mDatabase->beginTransaction(); +} + +void MtpSqliteDatabase::commitTransaction() { + mDatabase->commitTransaction(); +} + +void MtpSqliteDatabase::rollbackTransaction() { + mDatabase->rollbackTransaction(); +} + +} // namespace android diff --git a/media/mtp/MtpSqliteDatabase.h b/media/mtp/MtpSqliteDatabase.h new file mode 100644 index 000000000000..74626a862479 --- /dev/null +++ b/media/mtp/MtpSqliteDatabase.h @@ -0,0 +1,96 @@ +/* + * 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 _MTP_SQLITE_DATABASE_H +#define _MTP_SQLITE_DATABASE_H + +#include "MtpTypes.h" +#include "MtpDatabase.h" + +class SqliteDatabase; + +namespace android { + +class MtpDataPacket; +class SqliteStatement; + +class MtpSqliteDatabase : public MtpDatabase { +private: + SqliteDatabase* mDatabase; + SqliteStatement* mFileIdQuery; + SqliteStatement* mFilePathQuery; + SqliteStatement* mObjectInfoQuery; + SqliteStatement* mFileInserter; + SqliteStatement* mFileDeleter; + SqliteStatement* mAudioInserter; + SqliteStatement* mAudioDeleter; + +public: + MtpSqliteDatabase(); + virtual ~MtpSqliteDatabase(); + + bool open(const char* path, bool create); + void close(); + + virtual MtpObjectHandle getObjectHandle(const char* path); + virtual MtpObjectHandle addFile(const char* path, + MtpObjectFormat format, + MtpObjectHandle parent, + MtpStorageID storage, + uint64_t size, + time_t modified); + + virtual MtpObjectHandle addAudioFile(MtpObjectHandle id); + + virtual MtpObjectHandle addAudioFile(MtpObjectHandle id, + const char* title, + const char* artist, + const char* album, + const char* albumArtist, + const char* genre, + const char* composer, + const char* mimeType, + int track, + int year, + int duration); + + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, + MtpObjectFormat format, + MtpObjectHandle parent); + + virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet); + + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, + MtpDataPacket& packet); + + virtual bool getObjectFilePath(MtpObjectHandle handle, + MtpString& filePath, + int64_t& fileLength); + virtual bool deleteFile(MtpObjectHandle handle); + + // helper for media scanner + virtual MtpObjectHandle* getFileList(int& outCount); + + virtual void beginTransaction(); + virtual void commitTransaction(); + virtual void rollbackTransaction(); +}; + +}; // namespace android + +#endif // _MTP_SQLITE_DATABASE_H diff --git a/media/mtp/SqliteDatabase.h b/media/mtp/SqliteDatabase.h index 56dd9dd86647..7d008f9104a9 100644 --- a/media/mtp/SqliteDatabase.h +++ b/media/mtp/SqliteDatabase.h @@ -29,8 +29,8 @@ public: SqliteDatabase(); virtual ~SqliteDatabase(); - virtual bool open(const char* path, bool create); - virtual void close(); + bool open(const char* path, bool create); + void close(); bool exec(const char* sql); int lastInsertedRow(); diff --git a/media/mtp/mtptest.cpp b/media/mtp/mtptest.cpp index 9062494f8c74..af0f77fde057 100644 --- a/media/mtp/mtptest.cpp +++ b/media/mtp/mtptest.cpp @@ -27,6 +27,7 @@ #include "MtpServer.h" #include "MtpStorage.h" #include "f_mtp.h" +#include "private/android_filesystem_config.h" using namespace android; @@ -76,7 +77,7 @@ int main(int argc, char* argv[]) { enable_usb_function("usb_mass_storage", false); enable_usb_function("mtp", true); - MtpServer server(fd, "/data/data/mtp/mtp.db"); + MtpServer server(fd, "/data/data/mtp/mtp.db", AID_SDCARD_RW, 0664, 0775); server.addStorage(storagePath); server.scanStorage(); server.run(); diff --git a/media/mtp/scantest.cpp b/media/mtp/scantest.cpp index f910bb63ead3..3702a5d06e6a 100644 --- a/media/mtp/scantest.cpp +++ b/media/mtp/scantest.cpp @@ -16,7 +16,7 @@ #include <stdio.h> -#include "MtpDatabase.h" +#include "MtpSqliteDatabase.h" #include "MtpMediaScanner.h" using namespace android; @@ -27,7 +27,7 @@ int main(int argc, char* argv[]) { return -1; } - MtpDatabase* database = new MtpDatabase(); + MtpSqliteDatabase* database = new MtpSqliteDatabase(); database->open("scantest.db", true); MtpMediaScanner scanner(1, argv[1], database); diff --git a/native/android/Android.mk b/native/android/Android.mk index 8c621b601736..fe8ed0003a0a 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -7,7 +7,8 @@ include $(CLEAR_VARS) # LOCAL_SRC_FILES:= \ activity.cpp \ - input.cpp + input.cpp \ + native_window.cpp LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp new file mode 100644 index 000000000000..448cbfccb666 --- /dev/null +++ b/native/android/native_window.cpp @@ -0,0 +1,47 @@ +/* + * 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 "Surface" +#include <utils/Log.h> + +#include <android/native_window.h> +#include <surfaceflinger/Surface.h> + +using android::Surface; + +static int32_t getWindowProp(ANativeWindow* window, int what) { + int value; + int res = window->query(window, what, &value); + return res < 0 ? res : value; +} + +int32_t ANativeWindow_getWidth(ANativeWindow* window) { + return getWindowProp(window, NATIVE_WINDOW_WIDTH); +} + +int32_t ANativeWindow_getHeight(ANativeWindow* window) { + return getWindowProp(window, NATIVE_WINDOW_HEIGHT); +} + +int32_t ANativeWindow_getFormat(ANativeWindow* window) { + return getWindowProp(window, NATIVE_WINDOW_FORMAT); +} + +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, + int32_t height, int32_t format) { + native_window_set_buffers_geometry(window, width, height, format); + return 0; +} diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h index c5c8f9defda3..bf5c641fc162 100644 --- a/native/include/android/native_activity.h +++ b/native/include/android/native_activity.h @@ -24,15 +24,12 @@ #include <jni.h> #include <android/input.h> +#include <android/native_window.h> #ifdef __cplusplus extern "C" { #endif -// Temporary until native surface API is defined. -struct ASurfaceHolder; -typedef struct ASurfaceHolder ASurfaceHolder; - struct ANativeActivityCallbacks; /** @@ -129,30 +126,20 @@ typedef struct ANativeActivityCallbacks { void (*onWindowFocusChanged)(ANativeActivity* activity, int hasFocus); /** - * The drawing surface for this native activity has been created. You - * can use the given surface object to start drawing. NOTE: surface - * drawing API is not yet defined. - */ - void (*onSurfaceCreated)(ANativeActivity* activity, ASurfaceHolder* surface); - - /** - * The drawing surface for this native activity has changed. The surface - * given here is guaranteed to be the same as the one last given to - * onSurfaceCreated. This is simply to inform you about interesting - * changed to that surface. + * The drawing window for this native activity has been created. You + * can use the given native window object to start drawing. */ - void (*onSurfaceChanged)(ANativeActivity* activity, ASurfaceHolder* surface, - int format, int width, int height); + void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window); /** - * The drawing surface for this native activity is going to be destroyed. - * You MUST ensure that you do not touch the surface object after returning - * from this function: in the common case of drawing to the surface from + * The drawing window for this native activity is going to be destroyed. + * You MUST ensure that you do not touch the window object after returning + * from this function: in the common case of drawing to the window from * another thread, that means the implementation of this callback must * properly synchronize with the other thread to stop its drawing before * returning from here. */ - void (*onSurfaceDestroyed)(ANativeActivity* activity, ASurfaceHolder* surface); + void (*onNativeWindowDestroyed)(ANativeActivity* activity, ANativeWindow* window); /** * The input queue for this native activity's window has been created. diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h index e6d5fea0a2b1..678ba3d7c7e2 100644 --- a/native/include/android/native_window.h +++ b/native/include/android/native_window.h @@ -22,9 +22,51 @@ extern "C" { #endif +/* + * Pixel formats that a window can use. + */ +enum { + WINDOW_FORMAT_RGBA_8888 = 1, + WINDOW_FORMAT_RGBX_8888 = 2, + WINDOW_FORMAT_RGB_565 = 4, +}; + struct ANativeWindow; typedef struct ANativeWindow ANativeWindow; +/* + * Return the current width in pixels of the window surface. Returns a + * negative value on error. + */ +int32_t ANativeWindow_getWidth(ANativeWindow* window); + +/* + * Return the current height in pixels of the window surface. Returns a + * negative value on error. + */ +int32_t ANativeWindow_getHeight(ANativeWindow* window); + +/* + * Return the current pixel format of the window surface. Returns a + * negative value on error. + */ +int32_t ANativeWindow_getFormat(ANativeWindow* window); + +/* + * Change the format and size of the window buffers. + * + * The width and height control the number of pixels in the buffers, not the + * dimensions of the window on screen. If these are different than the + * window's physical size, then it buffer will be scaled to match that size + * when compositing it to the screen. + * + * The format may be one of the window format constants above. + * + * For all of these parameters, if 0 is supplied than the window's base + * value will come back in force. + */ +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, + int32_t height, int32_t format); #ifdef __cplusplus }; diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index ba09d082feec..714fd3eec7c5 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -1045,6 +1045,10 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, int i=0, index=0; egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); if (cnx) { + if (share_list != EGL_NO_CONTEXT) { + egl_context_t* const c = get_context(share_list); + share_list = c->context; + } EGLContext context = cnx->egl.eglCreateContext( dp->disp[i].dpy, dp->disp[i].config[index], share_list, attrib_list); diff --git a/opengl/tests/gl_jni/jni/gl_code.cpp b/opengl/tests/gl_jni/jni/gl_code.cpp index f031c79cd33e..ef66841f2792 100644 --- a/opengl/tests/gl_jni/jni/gl_code.cpp +++ b/opengl/tests/gl_jni/jni/gl_code.cpp @@ -181,4 +181,3 @@ JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * { background = 1.0f - background; } - diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java index 4ddd45ca97e1..07bcce7056be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java @@ -584,7 +584,7 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks } if (expanded == null) { String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); - Slog.e(TAG, "couldn't inflate view for notification " + ident); + Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); return null; } else { content.addView(expanded); diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg Binary files differnew file mode 100644 index 000000000000..92851f3df924 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg diff --git a/tests/HwAccelerationTest/res/drawable/sunset2.png b/tests/HwAccelerationTest/res/drawable/sunset2.png Binary files differnew file mode 100644 index 000000000000..3258ee745389 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/sunset2.png |