diff options
47 files changed, 1601 insertions, 1230 deletions
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 064755e95fbd..b089bf2b6584 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -108,21 +108,22 @@ abstract public class ContentProviderNative extends Binder implements IContentPr String sortOrder = data.readString(); IContentObserver observer = IContentObserver.Stub.asInterface( data.readStrongBinder()); - CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder); if (cursor != null) { CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor( - cursor, observer, getProviderName(), window); + cursor, observer, getProviderName()); final IBinder binder = adaptor.asBinder(); final int count = adaptor.count(); final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex( adaptor.getColumnNames()); + final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls(); reply.writeNoException(); reply.writeStrongBinder(binder); reply.writeInt(count); reply.writeInt(index); + reply.writeInt(wantsAllOnMoveCalls ? 1 : 0); } else { reply.writeNoException(); reply.writeStrongBinder(null); @@ -324,67 +325,58 @@ final class ContentProviderProxy implements IContentProvider public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws RemoteException { - CursorWindow window = new CursorWindow(false /* window will be used remotely */); + BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); try { - BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - - url.writeToParcel(data, 0); - int length = 0; - if (projection != null) { - length = projection.length; - } - data.writeInt(length); - for (int i = 0; i < length; i++) { - data.writeString(projection[i]); - } - data.writeString(selection); - if (selectionArgs != null) { - length = selectionArgs.length; - } else { - length = 0; - } - data.writeInt(length); - for (int i = 0; i < length; i++) { - data.writeString(selectionArgs[i]); - } - data.writeString(sortOrder); - data.writeStrongBinder(adaptor.getObserver().asBinder()); - window.writeToParcel(data, 0); - - mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionFromParcel(reply); - - IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder()); - if (bulkCursor != null) { - int rowCount = reply.readInt(); - int idColumnPosition = reply.readInt(); - adaptor.initialize(bulkCursor, rowCount, idColumnPosition); - } else { - adaptor.close(); - adaptor = null; - } - return adaptor; - } catch (RemoteException ex) { - adaptor.close(); - throw ex; - } catch (RuntimeException ex) { + data.writeInterfaceToken(IContentProvider.descriptor); + + url.writeToParcel(data, 0); + int length = 0; + if (projection != null) { + length = projection.length; + } + data.writeInt(length); + for (int i = 0; i < length; i++) { + data.writeString(projection[i]); + } + data.writeString(selection); + if (selectionArgs != null) { + length = selectionArgs.length; + } else { + length = 0; + } + data.writeInt(length); + for (int i = 0; i < length; i++) { + data.writeString(selectionArgs[i]); + } + data.writeString(sortOrder); + data.writeStrongBinder(adaptor.getObserver().asBinder()); + + mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder()); + if (bulkCursor != null) { + int rowCount = reply.readInt(); + int idColumnPosition = reply.readInt(); + boolean wantsAllOnMoveCalls = reply.readInt() != 0; + adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls); + } else { adaptor.close(); - throw ex; - } finally { - data.recycle(); - reply.recycle(); + adaptor = null; } + return adaptor; + } catch (RemoteException ex) { + adaptor.close(); + throw ex; + } catch (RuntimeException ex) { + adaptor.close(); + throw ex; } finally { - // We close the window now because the cursor adaptor does not - // take ownership of the window until the first call to onMove. - // The adaptor will obtain a fresh reference to the window when - // it is filled. - window.close(); + data.recycle(); + reply.recycle(); } } diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index 5836265c4945..d0aedd2c9280 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -189,12 +189,14 @@ public abstract class AbstractWindowedCursor extends AbstractCursor { /** * If there is a window, clear it. * Otherwise, creates a local window. + * + * @param name The window name. * @hide */ - protected void clearOrCreateLocalWindow() { + protected void clearOrCreateLocalWindow(String name) { if (mWindow == null) { // If there isn't a window set already it will only be accessed locally - mWindow = new CursorWindow(true /* the window is local only */); + mWindow = new CursorWindow(name, true /* the window is local only */); } else { mWindow.clear(); } diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index 4fada8c2fd29..20a9c67198b2 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -109,9 +109,8 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor case REQUERY_TRANSACTION: { data.enforceInterface(IBulkCursor.descriptor); IContentObserver observer = - IContentObserver.Stub.asInterface(data.readStrongBinder()); - CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); - int count = requery(observer, window); + IContentObserver.Stub.asInterface(data.readStrongBinder()); + int count = requery(observer); reply.writeNoException(); reply.writeInt(count); reply.writeBundle(getExtras()); @@ -294,13 +293,12 @@ final class BulkCursorProxy implements IBulkCursor { } } - public int requery(IContentObserver observer, CursorWindow window) throws RemoteException { + public int requery(IContentObserver observer) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IBulkCursor.descriptor); data.writeStrongInterface(observer); - window.writeToParcel(data, 0); boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index cbdd07fb29d3..885046bf1995 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -38,11 +38,13 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { * Initializes the adaptor. * Must be called before first use. */ - public void initialize(IBulkCursor bulkCursor, int count, int idIndex) { + public void initialize(IBulkCursor bulkCursor, int count, int idIndex, + boolean wantsAllOnMoveCalls) { mBulkCursor = bulkCursor; mColumns = null; // lazily retrieved mCount = count; mRowIdColumnIndex = idIndex; + mWantsAllOnMoveCalls = wantsAllOnMoveCalls; } /** @@ -86,15 +88,12 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { // Make sure we have the proper window - if (mWindow != null) { - if (newPosition < mWindow.getStartPosition() || - newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { - setWindow(mBulkCursor.getWindow(newPosition)); - } else if (mWantsAllOnMoveCalls) { - mBulkCursor.onMove(newPosition); - } - } else { + if (mWindow == null + || newPosition < mWindow.getStartPosition() + || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) { setWindow(mBulkCursor.getWindow(newPosition)); + } else if (mWantsAllOnMoveCalls) { + mBulkCursor.onMove(newPosition); } } catch (RemoteException ex) { // We tried to get a window and failed @@ -145,25 +144,19 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { throwIfCursorIsClosed(); try { - CursorWindow newWindow = new CursorWindow(false /* create a remote window */); - try { - mCount = mBulkCursor.requery(getObserver(), newWindow); - if (mCount != -1) { - mPos = -1; - closeWindow(); - - // super.requery() will call onChanged. Do it here instead of relying on the - // observer from the far side so that observers can see a correct value for mCount - // when responding to onChanged. - super.requery(); - return true; - } else { - deactivate(); - return false; - } - } finally { - // Don't take ownership of the window until the next call to onMove. - newWindow.close(); + mCount = mBulkCursor.requery(getObserver()); + if (mCount != -1) { + mPos = -1; + closeWindow(); + + // super.requery() will call onChanged. Do it here instead of relying on the + // observer from the far side so that observers can see a correct value for mCount + // when responding to onChanged. + super.requery(); + return true; + } else { + deactivate(); + return false; } } catch (Exception ex) { Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage()); diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java index a65b3b304558..dd2c9b7c132c 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -53,6 +53,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative * for managing the lifetime of their window. */ private CursorWindow mWindowForNonWindowedCursor; + private boolean mWindowForNonWindowedCursorWasFilled; private static final class ContentObserverProxy extends ContentObserver { protected IContentObserver mRemote; @@ -87,26 +88,11 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } - public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, - CursorWindow window) { + public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, + String providerName) { try { mCursor = (CrossProcessCursor) cursor; - if (mCursor instanceof AbstractWindowedCursor) { - AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor; - if (windowedCursor.hasWindow()) { - if (Log.isLoggable(TAG, Log.VERBOSE) || false) { - Log.v(TAG, "Cross process cursor has a local window before setWindow in " - + providerName, new RuntimeException()); - } - } - windowedCursor.setWindow(window); // cursor takes ownership of window - } else { - mWindowForNonWindowedCursor = window; // we own the window - mCursor.fillWindow(0, window); - } } catch (ClassCastException e) { - // TODO Implement this case. - window.close(); throw new UnsupportedOperationException( "Only CrossProcessCursor cursors are supported across process for now", e); } @@ -117,17 +103,22 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } - private void closeCursorAndWindowLocked() { + private void closeWindowForNonWindowedCursorLocked() { + if (mWindowForNonWindowedCursor != null) { + mWindowForNonWindowedCursor.close(); + mWindowForNonWindowedCursor = null; + mWindowForNonWindowedCursorWasFilled = false; + } + } + + private void disposeLocked() { if (mCursor != null) { unregisterObserverProxyLocked(); mCursor.close(); mCursor = null; } - if (mWindowForNonWindowedCursor != null) { - mWindowForNonWindowedCursor.close(); - mWindowForNonWindowedCursor = null; - } + closeWindowForNonWindowedCursorLocked(); } private void throwIfCursorIsClosed() { @@ -139,7 +130,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative @Override public void binderDied() { synchronized (mLock) { - closeCursorAndWindowLocked(); + disposeLocked(); } } @@ -148,17 +139,30 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative synchronized (mLock) { throwIfCursorIsClosed(); - mCursor.moveToPosition(startPos); - - final CursorWindow window; + CursorWindow window; if (mCursor instanceof AbstractWindowedCursor) { - window = ((AbstractWindowedCursor)mCursor).getWindow(); + AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor)mCursor; + window = windowedCursor.getWindow(); + if (window == null) { + window = new CursorWindow(mProviderName, false /*localOnly*/); + windowedCursor.setWindow(window); + } + + mCursor.moveToPosition(startPos); } else { window = mWindowForNonWindowedCursor; - if (window != null - && (startPos < window.getStartPosition() || - startPos >= (window.getStartPosition() + window.getNumRows()))) { + if (window == null) { + window = new CursorWindow(mProviderName, false /*localOnly*/); + mWindowForNonWindowedCursor = window; + } + + mCursor.moveToPosition(startPos); + + if (!mWindowForNonWindowedCursorWasFilled + || startPos < window.getStartPosition() + || startPos >= window.getStartPosition() + window.getNumRows()) { mCursor.fillWindow(startPos, window); + mWindowForNonWindowedCursorWasFilled = true; } } @@ -206,29 +210,24 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative unregisterObserverProxyLocked(); mCursor.deactivate(); } + + closeWindowForNonWindowedCursorLocked(); } } @Override public void close() { synchronized (mLock) { - closeCursorAndWindowLocked(); + disposeLocked(); } } @Override - public int requery(IContentObserver observer, CursorWindow window) { + public int requery(IContentObserver observer) { synchronized (mLock) { throwIfCursorIsClosed(); - if (mCursor instanceof AbstractWindowedCursor) { - ((AbstractWindowedCursor) mCursor).setWindow(window); - } else { - if (mWindowForNonWindowedCursor != null) { - mWindowForNonWindowedCursor.close(); - } - mWindowForNonWindowedCursor = window; - } + closeWindowForNonWindowedCursorLocked(); try { if (!mCursor.requery()) { @@ -241,12 +240,6 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative throw leakProgram; } - if (!(mCursor instanceof AbstractWindowedCursor)) { - if (window != null) { - mCursor.fillWindow(0, window); - } - } - unregisterObserverProxyLocked(); createAndRegisterObserverProxyLocked(observer); return mCursor.getCount(); diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 5a91b80034e4..a18a72133455 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -22,7 +22,6 @@ import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; import android.database.sqlite.SQLiteException; import android.os.Binder; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -31,6 +30,13 @@ import android.util.SparseIntArray; /** * A buffer containing multiple cursor rows. + * <p> + * A {@link CursorWindow} is read-write when created and used locally. When sent + * to a remote process (by writing it to a {@link Parcel}), the remote process + * receives a read-only view of the cursor window. Typically the cursor window + * will be allocated by the producer, filled with data, and then sent to the + * consumer for reading. + * </p> */ public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; @@ -52,10 +58,11 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private final CloseGuard mCloseGuard = CloseGuard.get(); - private static native int nativeInitializeEmpty(int cursorWindowSize, boolean localOnly); - private static native int nativeInitializeFromBinder(IBinder nativeBinder); + private static native int nativeCreate(String name, + int cursorWindowSize, boolean localOnly); + private static native int nativeCreateFromParcel(Parcel parcel); private static native void nativeDispose(int windowPtr); - private static native IBinder nativeGetBinder(int windowPtr); + private static native void nativeWriteToParcel(int windowPtr, Parcel parcel); private static native void nativeClear(int windowPtr); @@ -79,18 +86,21 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private static native boolean nativePutNull(int windowPtr, int row, int column); /** - * Creates a new empty cursor window. + * Creates a new empty cursor window and gives it a name. * <p> * The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to * set the number of columns before adding any rows to the cursor. * </p> * + * @param name The name of the cursor window, or null if none. * @param localWindow True if this window will be used in this process only, * false if it might be sent to another processes. + * + * @hide */ - public CursorWindow(boolean localWindow) { + public CursorWindow(String name, boolean localWindow) { mStartPos = 0; - mWindowPtr = nativeInitializeEmpty(sCursorWindowSize, localWindow); + mWindowPtr = nativeCreate(name, sCursorWindowSize, localWindow); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + (sCursorWindowSize / 1024) + " kb failed. " + printStats()); @@ -99,10 +109,23 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { recordNewWindow(Binder.getCallingPid(), mWindowPtr); } + /** + * Creates a new empty cursor window. + * <p> + * The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to + * set the number of columns before adding any rows to the cursor. + * </p> + * + * @param localWindow True if this window will be used in this process only, + * false if it might be sent to another processes. + */ + public CursorWindow(boolean localWindow) { + this(null, localWindow); + } + private CursorWindow(Parcel source) { - IBinder binder = source.readStrongBinder(); mStartPos = source.readInt(); - mWindowPtr = nativeInitializeFromBinder(binder); + mWindowPtr = nativeCreateFromParcel(source); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window could not be " + "created from binder."); @@ -687,8 +710,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(nativeGetBinder(mWindowPtr)); dest.writeInt(mStartPos); + nativeWriteToParcel(mWindowPtr, dest); if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { releaseReference(); diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java index 244c88fad38c..7c967970a34f 100644 --- a/core/java/android/database/IBulkCursor.java +++ b/core/java/android/database/IBulkCursor.java @@ -56,7 +56,7 @@ public interface IBulkCursor extends IInterface { public void close() throws RemoteException; - public int requery(IContentObserver observer, CursorWindow window) throws RemoteException; + public int requery(IContentObserver observer) throws RemoteException; boolean getWantsAllOnMoveCalls() throws RemoteException; diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index 9d7e15201be5..a1c36e24c000 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -155,7 +155,7 @@ public class SQLiteCursor extends AbstractWindowedCursor { } private void fillWindow(int startPos) { - clearOrCreateLocalWindow(); + clearOrCreateLocalWindow(getDatabase().getPath()); mWindow.setStartPosition(startPos); int count = getQuery().fillWindow(mWindow); if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0 diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index d65e6df12a6f..9bd4a3be3622 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1464,6 +1464,8 @@ public class Camera { private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw"; private static final String KEY_RECORDING_HINT = "recording-hint"; private static final String KEY_VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported"; + private static final String KEY_VIDEO_STABILIZATION = "video-stabilization"; + private static final String KEY_VIDEO_STABILIZATION_SUPPORTED = "video-stabilization-supported"; // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -2443,7 +2445,7 @@ public class Camera { * * @param value new white balance. * @see #getWhiteBalance() - * @see #setAutoWhiteBalanceLock() + * @see #setAutoWhiteBalanceLock(boolean) */ public void setWhiteBalance(String value) { set(KEY_WHITE_BALANCE, value); @@ -3208,6 +3210,59 @@ public class Camera { return TRUE.equals(str); } + /** + * <p>Enables and disables video stabilization. Use + * {@link #isVideoStabilizationSupported} to determine if calling this + * method is valid.</p> + * + * <p>Video stabilization reduces the shaking due to the motion of the + * camera in both the preview stream and in recorded videos, including + * data received from the preview callback. It does not reduce motion + * blur in images captured with + * {@link Camera#takePicture takePicture}.</p> + * + * <p>Video stabilization can be enabled and disabled while preview or + * recording is active, but toggling it may cause a jump in the video + * stream that may be undesirable in a recorded video.</p> + * + * @param toggle Set to true to enable video stabilization, and false to + * disable video stabilization. + * @see #isVideoStabilizationSupported() + * @see #getVideoStabilization() + * @hide + */ + public void setVideoStabilization(boolean toggle) { + set(KEY_VIDEO_STABILIZATION, toggle ? TRUE : FALSE); + } + + /** + * Get the current state of video stabilization. See + * {@link #setVideoStabilization} for details of video stabilization. + * + * @return true if video stabilization is enabled + * @see #isVideoStabilizationSupported() + * @see #setVideoStabilization(boolean) + * @hide + */ + public boolean getVideoStabilization() { + String str = get(KEY_VIDEO_STABILIZATION); + return TRUE.equals(str); + } + + /** + * Returns true if video stabilization is supported. See + * {@link #setVideoStabilization} for details of video stabilization. + * + * @return true if video stabilization is supported + * @see #setVideoStabilization(boolean) + * @see #getVideoStabilization() + * @hide + */ + public boolean isVideoStabilizationSupported() { + String str = get(KEY_VIDEO_STABILIZATION_SUPPORTED); + return TRUE.equals(str); + } + // Splits a comma delimited string to an ArrayList of String. // Return null if the passing string is null or the size is 0. private ArrayList<String> split(String str) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bc05078906e6..3d2a3cefeab2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4087,7 +4087,9 @@ public final class Settings { MOUNT_UMS_AUTOSTART, MOUNT_UMS_PROMPT, MOUNT_UMS_NOTIFY_ENABLED, - UI_NIGHT_MODE + UI_NIGHT_MODE, + LOCK_SCREEN_OWNER_INFO, + LOCK_SCREEN_OWNER_INFO_ENABLED }; /** diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index c8b67a87f745..2cc928f966fe 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -1204,22 +1204,20 @@ class BrowserFrame extends Handler { * We delegate the request to CallbackProxy, and route its response to * {@link #nativeSslClientCert(int, X509Certificate)}. */ - private void requestClientCert(int handle, byte[] host_and_port_bytes) { - String host_and_port = new String(host_and_port_bytes, Charsets.UTF_8); + private void requestClientCert(int handle, String hostAndPort) { SslClientCertLookupTable table = SslClientCertLookupTable.getInstance(); - if (table.IsAllowed(host_and_port)) { + if (table.IsAllowed(hostAndPort)) { // previously allowed nativeSslClientCert(handle, - table.PrivateKey(host_and_port), - table.CertificateChain(host_and_port)); - } else if (table.IsDenied(host_and_port)) { + table.PrivateKey(hostAndPort), + table.CertificateChain(hostAndPort)); + } else if (table.IsDenied(hostAndPort)) { // previously denied nativeSslClientCert(handle, null, null); } else { // previously ignored or new mCallbackProxy.onReceivedClientCertRequest( - new ClientCertRequestHandler(this, handle, host_and_port, table), - host_and_port); + new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort); } } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 66371f5fc5dd..b0ecf092ea58 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -1002,6 +1002,9 @@ import junit.framework.Assert; | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN; + if (!mWebView.nativeFocusCandidateIsSpellcheck()) { + inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } if (TEXT_AREA != type && mWebView.nativeFocusCandidateHasNextTextfield()) { imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6d19c23fd2cb..9648cd015d74 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -9459,6 +9459,7 @@ public class WebView extends AbsoluteLayout private native boolean nativeFocusCandidateIsTextInput(); /* package */ native int nativeFocusCandidateMaxLength(); /* package */ native boolean nativeFocusCandidateIsAutoComplete(); + /* package */ native boolean nativeFocusCandidateIsSpellcheck(); /* package */ native String nativeFocusCandidateName(); private native Rect nativeFocusCandidateNodeBounds(); /** diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index df8eb053845d..475b8eeaf028 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -335,6 +335,7 @@ public abstract class AbsSeekBar extends ProgressBar { mTouchDownX = event.getX(); } else { setPressed(true); + invalidate(mThumb.getBounds()); // This may be within the padding region onStartTrackingTouch(); trackTouchEvent(event); attemptClaimDrag(); @@ -348,6 +349,7 @@ public abstract class AbsSeekBar extends ProgressBar { final float x = event.getX(); if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) { setPressed(true); + invalidate(mThumb.getBounds()); // This may be within the padding region onStartTrackingTouch(); trackTouchEvent(event); attemptClaimDrag(); diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index fd19b5fea9d2..e16a8bdff8fa 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -922,15 +922,20 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { if (selectedView != null) { event.setEnabled(selectedView.isEnabled()); } + event.setCurrentItemIndex(getSelectedItemPosition()); event.setFromIndex(getFirstVisiblePosition()); event.setToIndex(getLastVisiblePosition()); event.setItemCount(getAdapter().getCount()); } private boolean isScrollableForAccessibility() { - final int itemCount = getAdapter().getCount(); - return itemCount > 0 - && (getFirstVisiblePosition() > 0 || getLastVisiblePosition() < itemCount - 1); + T adapter = getAdapter(); + if (adapter != null) { + final int itemCount = adapter.getCount(); + return itemCount > 0 + && (getFirstVisiblePosition() > 0 || getLastVisiblePosition() < itemCount - 1); + } + return false; } @Override diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 51506e8c6f7a..083a9521a4d2 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -29,12 +29,14 @@ import android.os.Handler; import android.os.SystemClock; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.widget.AbsListView.OnScrollListener; /** * Helper class for AbsListView to draw and control the Fast Scroll thumb */ class FastScroller { + private static final String TAG = "FastScroller"; // Minimum number of pages to justify showing a fast scroll thumb private static int MIN_PAGES = 4; @@ -81,15 +83,15 @@ class FastScroller { private Drawable mOverlayDrawableLeft; private Drawable mOverlayDrawableRight; - private int mThumbH; - private int mThumbW; - private int mThumbY; + int mThumbH; + int mThumbW; + int mThumbY; private RectF mOverlayPos; private int mOverlaySize; - private AbsListView mList; - private boolean mScrollCompleted; + AbsListView mList; + boolean mScrollCompleted; private int mVisibleItem; private Paint mPaint; private int mListOffset; @@ -105,7 +107,7 @@ class FastScroller { private Handler mHandler = new Handler(); - private BaseAdapter mListAdapter; + BaseAdapter mListAdapter; private SectionIndexer mSectionIndexer; private boolean mChangedBounds; @@ -118,10 +120,36 @@ class FastScroller { private boolean mMatchDragPosition; + float mInitialTouchY; + boolean mPendingDrag; + private int mScaledTouchSlop; + private static final int FADE_TIMEOUT = 1500; + private static final int PENDING_DRAG_DELAY = 180; private final Rect mTmpRect = new Rect(); + private final Runnable mDeferStartDrag = new Runnable() { + public void run() { + if (mList.mIsAttached) { + beginDrag(); + + final int viewHeight = mList.getHeight(); + // Jitter + int newThumbY = (int) mInitialTouchY - mThumbH + 10; + if (newThumbY < 0) { + newThumbY = 0; + } else if (newThumbY + mThumbH > viewHeight) { + newThumbY = viewHeight - mThumbH; + } + mThumbY = newThumbY; + scrollTo((float) mThumbY / (viewHeight - mThumbH)); + } + + mPendingDrag = false; + } + }; + public FastScroller(Context context, AbsListView listView) { mList = listView; init(context); @@ -264,6 +292,8 @@ class FastScroller { ta.recycle(); + mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB; @@ -456,7 +486,7 @@ class FastScroller { return mSections; } - private void getSectionsFromIndexer() { + void getSectionsFromIndexer() { Adapter adapter = mList.getAdapter(); mSectionIndexer = null; if (adapter instanceof HeaderViewListAdapter) { @@ -489,7 +519,7 @@ class FastScroller { mListAdapter = null; } - private void scrollTo(float position) { + void scrollTo(float position) { int count = mList.getCount(); mScrollCompleted = false; float fThreshold = (1.0f / count) / 8; @@ -647,12 +677,45 @@ class FastScroller { cancelFling.recycle(); } + void cancelPendingDrag() { + mList.removeCallbacks(mDeferStartDrag); + mPendingDrag = false; + } + + void startPendingDrag() { + mPendingDrag = true; + mList.postDelayed(mDeferStartDrag, PENDING_DRAG_DELAY); + } + + void beginDrag() { + setState(STATE_DRAGGING); + if (mListAdapter == null && mList != null) { + getSectionsFromIndexer(); + } + if (mList != null) { + mList.requestDisallowInterceptTouchEvent(true); + mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } + + cancelFling(); + } + boolean onInterceptTouchEvent(MotionEvent ev) { - if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) { - if (isPointInside(ev.getX(), ev.getY())) { - setState(STATE_DRAGGING); - return true; - } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) { + if (!mList.isInScrollingContainer()) { + beginDrag(); + return true; + } + mInitialTouchY = ev.getY(); + startPendingDrag(); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + cancelPendingDrag(); + break; } return false; } @@ -666,19 +729,32 @@ class FastScroller { if (action == MotionEvent.ACTION_DOWN) { if (isPointInside(me.getX(), me.getY())) { - setState(STATE_DRAGGING); - if (mListAdapter == null && mList != null) { - getSectionsFromIndexer(); + if (!mList.isInScrollingContainer()) { + beginDrag(); + return true; } - if (mList != null) { - mList.requestDisallowInterceptTouchEvent(true); - mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + mInitialTouchY = me.getY(); + startPendingDrag(); + } + } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here + if (mPendingDrag) { + // Allow a tap to scroll. + beginDrag(); + + final int viewHeight = mList.getHeight(); + // Jitter + int newThumbY = (int) me.getY() - mThumbH + 10; + if (newThumbY < 0) { + newThumbY = 0; + } else if (newThumbY + mThumbH > viewHeight) { + newThumbY = viewHeight - mThumbH; } + mThumbY = newThumbY; + scrollTo((float) mThumbY / (viewHeight - mThumbH)); - cancelFling(); - return true; + cancelPendingDrag(); + // Will hit the STATE_DRAGGING check below } - } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here if (mState == STATE_DRAGGING) { if (mList != null) { // ViewGroup does the right thing already, but there might @@ -698,6 +774,23 @@ class FastScroller { return true; } } else if (action == MotionEvent.ACTION_MOVE) { + if (mPendingDrag) { + final float y = me.getY(); + if (Math.abs(y - mInitialTouchY) > mScaledTouchSlop) { + setState(STATE_DRAGGING); + if (mListAdapter == null && mList != null) { + getSectionsFromIndexer(); + } + if (mList != null) { + mList.requestDisallowInterceptTouchEvent(true); + mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } + + cancelFling(); + cancelPendingDrag(); + // Will hit the STATE_DRAGGING check below + } + } if (mState == STATE_DRAGGING) { final int viewHeight = mList.getHeight(); // Jitter @@ -717,6 +810,8 @@ class FastScroller { } return true; } + } else if (action == MotionEvent.ACTION_CANCEL) { + cancelPendingDrag(); } return false; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 41daf70ffdb8..041e8a498efb 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2476,6 +2476,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (gravity != mGravity) { invalidate(); + mLayoutAlignment = null; } mGravity = gravity; diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index aabea2c91458..f25d65fcdabd 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -31,9 +31,6 @@ import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageButton; import java.util.ArrayList; @@ -71,8 +68,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback(); int mOpenSubMenuId; - public ActionMenuPresenter() { - super(com.android.internal.R.layout.action_menu_layout, + public ActionMenuPresenter(Context context) { + super(context, com.android.internal.R.layout.action_menu_layout, com.android.internal.R.layout.action_menu_item_layout); } @@ -98,7 +95,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter int width = mWidthLimit; if (mReserveOverflow) { if (mOverflowButton == null) { - mOverflowButton = new OverflowMenuButton(mContext); + mOverflowButton = new OverflowMenuButton(mSystemContext); final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); mOverflowButton.measure(spec, spec); } @@ -215,7 +212,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter if (hasOverflow) { if (mOverflowButton == null) { - mOverflowButton = new OverflowMenuButton(mContext); + mOverflowButton = new OverflowMenuButton(mSystemContext); } ViewGroup parent = (ViewGroup) mOverflowButton.getParent(); if (parent != mMenuView) { diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java index bec437a744d6..1e06b5a601f3 100644 --- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java @@ -29,8 +29,10 @@ import java.util.ArrayList; * be reused if possible when items change. */ public abstract class BaseMenuPresenter implements MenuPresenter { + protected Context mSystemContext; protected Context mContext; protected MenuBuilder mMenu; + protected LayoutInflater mSystemInflater; protected LayoutInflater mInflater; private Callback mCallback; @@ -44,10 +46,13 @@ public abstract class BaseMenuPresenter implements MenuPresenter { /** * Construct a new BaseMenuPresenter. * + * @param context Context for generating system-supplied views * @param menuLayoutRes Layout resource ID for the menu container view * @param itemLayoutRes Layout resource ID for a single item view */ - public BaseMenuPresenter(int menuLayoutRes, int itemLayoutRes) { + public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) { + mSystemContext = context; + mSystemInflater = LayoutInflater.from(context); mMenuLayoutRes = menuLayoutRes; mItemLayoutRes = itemLayoutRes; } @@ -62,7 +67,7 @@ public abstract class BaseMenuPresenter implements MenuPresenter { @Override public MenuView getMenuView(ViewGroup root) { if (mMenuView == null) { - mMenuView = (MenuView) mInflater.inflate(mMenuLayoutRes, root, false); + mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false); mMenuView.initialize(mMenu); updateMenuView(true); } @@ -138,7 +143,7 @@ public abstract class BaseMenuPresenter implements MenuPresenter { * @return The new item view */ public MenuView.ItemView createItemView(ViewGroup parent) { - return (MenuView.ItemView) mInflater.inflate(mItemLayoutRes, parent, false); + return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false); } /** diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java index 24ddad668ac3..2439b5df3acc 100644 --- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java @@ -22,7 +22,6 @@ import android.os.Bundle; import android.os.Parcelable; import android.util.SparseArray; import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -43,16 +42,15 @@ public class IconMenuPresenter extends BaseMenuPresenter { private static final String VIEWS_TAG = "android:menu:icon"; private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu"; - public IconMenuPresenter() { - super(com.android.internal.R.layout.icon_menu_layout, + public IconMenuPresenter(Context context) { + super(new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu), + com.android.internal.R.layout.icon_menu_layout, com.android.internal.R.layout.icon_menu_item_layout); } @Override public void initForMenu(Context context, MenuBuilder menu) { - mContext = new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu); - mInflater = LayoutInflater.from(mContext); - mMenu = menu; + super.initForMenu(context, menu); mMaxItems = -1; } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index acffa5c16d1b..446dab114dde 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -207,7 +207,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi }); final MenuBuilder menu = (MenuBuilder) mode.getMenu(); - mActionMenuPresenter = new ActionMenuPresenter(); + mActionMenuPresenter = new ActionMenuPresenter(mContext); mActionMenuPresenter.setReserveOverflow(true); final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 6d2e8232156b..61bce60b833a 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -373,7 +373,7 @@ public class ActionBarView extends AbsActionBarView { } } if (mActionMenuPresenter == null) { - mActionMenuPresenter = new ActionMenuPresenter(); + mActionMenuPresenter = new ActionMenuPresenter(mContext); mActionMenuPresenter.setCallback(cb); mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter); mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index fe1aca0b0600..722aeea68296 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -57,14 +57,23 @@ static void throwUnknownTypeException(JNIEnv * env, jint type) { jniThrowException(env, "java/lang/IllegalStateException", msg.string()); } -static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz, - jint cursorWindowSize, jboolean localOnly) { - CursorWindow* window = new CursorWindow(cursorWindowSize); - if (!window) { - return 0; - } - if (!window->initBuffer(localOnly)) { - delete window; +static jint nativeCreate(JNIEnv* env, jclass clazz, + jstring nameObj, jint cursorWindowSize, jboolean localOnly) { + String8 name; + if (nameObj) { + const char* nameStr = env->GetStringUTFChars(nameObj, NULL); + name.setTo(nameStr); + env->ReleaseStringUTFChars(nameObj, nameStr); + } + if (name.size() == 0) { + name.setTo("<unnamed>"); + } + + CursorWindow* window; + status_t status = CursorWindow::create(name, cursorWindowSize, localOnly, &window); + if (status || !window) { + LOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.", + name.string(), cursorWindowSize, status); return 0; } @@ -72,19 +81,13 @@ static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz, return reinterpret_cast<jint>(window); } -static jint nativeInitializeFromBinder(JNIEnv* env, jclass clazz, jobject binderObj) { - sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, binderObj)); - if (memory == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); - return 0; - } +static jint nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { + Parcel* parcel = parcelForJavaObject(env, parcelObj); - CursorWindow* window = new CursorWindow(); - if (!window) { - return 0; - } - if (!window->setMemory(memory)) { - delete window; + CursorWindow* window; + status_t status = CursorWindow::createFromParcel(parcel, &window); + if (status || !window) { + LOGE("Could not create CursorWindow from Parcel due to error %d.", status); return 0; } @@ -101,22 +104,26 @@ static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) { } } -static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) { +static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jint windowPtr, + jobject parcelObj) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - if (window) { - sp<IMemory> memory = window->getMemory(); - if (memory != NULL) { - sp<IBinder> binder = memory->asBinder(); - return javaObjectForIBinder(env, binder); - } + Parcel* parcel = parcelForJavaObject(env, parcelObj); + + status_t status = window->writeToParcel(parcel); + if (status) { + String8 msg; + msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status); + jniThrowRuntimeException(env, msg.string()); } - return NULL; } static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Clearing window %p", window); - window->clear(); + status_t status = window->clear(); + if (status) { + LOG_WINDOW("Could not clear window. error=%d", status); + } } static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { @@ -127,12 +134,14 @@ static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr, jint columnNum) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - return window->setNumColumns(columnNum); + status_t status = window->setNumColumns(columnNum); + return status == OK; } static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - return window->allocRow() != NULL; + status_t status = window->allocRow(); + return status == OK; } static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) { @@ -145,14 +154,14 @@ static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { // FIXME: This is really broken but we have CTS tests that depend // on this legacy behavior. //throwExceptionWithRowCol(env, row, column); - return FIELD_TYPE_NULL; + return CursorWindow::FIELD_TYPE_NULL; } - return fieldSlot->type; + return window->getFieldSlotType(fieldSlot); } static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, @@ -160,29 +169,29 @@ static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return NULL; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) { + size_t size; + const void* value = window->getFieldSlotValueBlob(fieldSlot, &size); jbyteArray byteArray = env->NewByteArray(size); if (!byteArray) { env->ExceptionClear(); throw_sqlite3_exception(env, "Native could not create new byte[]"); return NULL; } - env->SetByteArrayRegion(byteArray, 0, size, - reinterpret_cast<jbyte*>(window->offsetToPtr(fieldSlot->data.buffer.offset))); + env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value)); return byteArray; - } else if (type == FIELD_TYPE_INTEGER) { + } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); - } else if (type == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { // do nothing } else { throwUnknownTypeException(env, type); @@ -195,43 +204,37 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return NULL; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; -#if WINDOW_STORAGE_UTF8 - if (size <= 1) { + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::FIELD_TYPE_STRING) { + size_t sizeIncludingNull; + const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); + if (sizeIncludingNull <= 1) { return gEmptyString; } // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF // doesn't like UTF-8 strings with high codepoints. It actually expects // Modified UTF-8 with encoded surrogate pairs. - String16 utf16(window->getFieldSlotValueString(fieldSlot), size - 1); + String16 utf16(value, sizeIncludingNull - 1); return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size()); -#else - size_t chars = size / sizeof(char16_t); - return chars ? env->NewString(reinterpret_cast<jchar*>( - window->getFieldSlotValueString(fieldSlot)), chars) - : gEmptyString; -#endif - } else if (type == FIELD_TYPE_INTEGER) { + } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { int64_t value = window->getFieldSlotValueLong(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%lld", value); return env->NewStringUTF(buf); - } else if (type == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%g", value); return env->NewStringUTF(buf); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { return NULL; - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); return NULL; } else { @@ -281,21 +284,6 @@ static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, } } -#if !WINDOW_STORAGE_UTF8 -static void fillCharArrayBuffer(JNIEnv* env, jobject bufferObj, - const char16_t* str, size_t len) { - jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, len); - if (dataObj) { - if (len) { - jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); - memcpy(data, str, len * sizeof(jchar)); - env->ReleasePrimitiveArrayCritical(dataObj, data, 0); - } - env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, len); - } -} -#endif - static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); if (dataObj) { @@ -308,44 +296,34 @@ static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; -#if WINDOW_STORAGE_UTF8 - if (size > 1) { - fillCharArrayBufferUTF(env, bufferObj, - window->getFieldSlotValueString(fieldSlot), size - 1); + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::FIELD_TYPE_STRING) { + size_t sizeIncludingNull; + const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); + if (sizeIncludingNull > 1) { + fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1); } else { clearCharArrayBuffer(env, bufferObj); } -#else - size_t chars = size / sizeof(char16_t); - if (chars) { - fillCharArrayBuffer(env, bufferObj, - window->getFieldSlotValueString(fieldSlot), chars); - } else { - clearCharArrayBuffer(env, bufferObj); - } -#endif - } else if (type == FIELD_TYPE_INTEGER) { + } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { int64_t value = window->getFieldSlotValueLong(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%lld", value); fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); - } else if (type == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%g", value); fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { clearCharArrayBuffer(env, bufferObj); - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); } else { throwUnknownTypeException(env, type); @@ -357,29 +335,24 @@ static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return 0; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_INTEGER) { + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::FIELD_TYPE_INTEGER) { return window->getFieldSlotValueLong(fieldSlot); - } else if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; -#if WINDOW_STORAGE_UTF8 - return size > 1 ? strtoll(window->getFieldSlotValueString(fieldSlot), NULL, 0) : 0L; -#else - size_t chars = size / sizeof(char16_t); - return chars ? strtoll(String8(window->getFieldSlotValueString(fieldSlot), chars) - .string(), NULL, 0) : 0L; -#endif - } else if (type == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::FIELD_TYPE_STRING) { + size_t sizeIncludingNull; + const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); + return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L; + } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { return jlong(window->getFieldSlotValueDouble(fieldSlot)); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { return 0; - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to long"); return 0; } else { @@ -393,29 +366,24 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return 0.0; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_FLOAT) { + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::FIELD_TYPE_FLOAT) { return window->getFieldSlotValueDouble(fieldSlot); - } else if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; -#if WINDOW_STORAGE_UTF8 - return size > 1 ? strtod(window->getFieldSlotValueString(fieldSlot), NULL) : 0.0; -#else - size_t chars = size / sizeof(char16_t); - return chars ? strtod(String8(window->getFieldSlotValueString(fieldSlot), chars) - .string(), NULL) : 0.0; -#endif - } else if (type == FIELD_TYPE_INTEGER) { + } else if (type == CursorWindow::FIELD_TYPE_STRING) { + size_t sizeIncludingNull; + const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); + return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0; + } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { return jdouble(window->getFieldSlotValueLong(fieldSlot)); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { return 0.0; - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to double"); return 0.0; } else { @@ -427,82 +395,50 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr, jbyteArray valueObj, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); - if (fieldSlot == NULL) { - LOG_WINDOW(" getFieldSlotWithCheck error "); - return false; - } - jsize len = env->GetArrayLength(valueObj); - uint32_t offset = window->alloc(len); - if (!offset) { - LOG_WINDOW("Failed allocating %u bytes", len); - return false; - } void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); - window->copyIn(offset, static_cast<const uint8_t*>(value), len); + status_t status = window->putBlob(row, column, value, len); env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); - fieldSlot->type = FIELD_TYPE_BLOB; - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = len; - LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, column, len, offset); + if (status) { + LOG_WINDOW("Failed to put blob. error=%d", status); + return false; + } + + LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len); return true; } static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr, jstring valueObj, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); - if (fieldSlot == NULL) { - LOG_WINDOW(" getFieldSlotWithCheck error "); - return false; - } -#if WINDOW_STORAGE_UTF8 - size_t size = env->GetStringUTFLength(valueObj) + 1; + size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1; const char* valueStr = env->GetStringUTFChars(valueObj, NULL); -#else - size_t size = env->GetStringLength(valueObj) * sizeof(jchar); - const jchar* valueStr = env->GetStringChars(valueObj, NULL); -#endif if (!valueStr) { - LOG_WINDOW("value can't be transfer to UTFChars"); + LOG_WINDOW("value can't be transferred to UTFChars"); return false; } + status_t status = window->putString(row, column, valueStr, sizeIncludingNull); + env->ReleaseStringUTFChars(valueObj, valueStr); - uint32_t offset = window->alloc(size); - if (!offset) { - LOG_WINDOW("Failed allocating %u bytes", size); -#if WINDOW_STORAGE_UTF8 - env->ReleaseStringUTFChars(valueObj, valueStr); -#else - env->ReleaseStringChars(valueObj, valueStr); -#endif + if (status) { + LOG_WINDOW("Failed to put string. error=%d", status); return false; } - window->copyIn(offset, reinterpret_cast<const uint8_t*>(valueStr), size); - -#if WINDOW_STORAGE_UTF8 - env->ReleaseStringUTFChars(valueObj, valueStr); -#else - env->ReleaseStringChars(valueObj, valueStr); -#endif - - fieldSlot->type = FIELD_TYPE_STRING; - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = size; - LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, column, size, offset); + LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull); return true; } static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, jlong value, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - if (!window->putLong(row, column, value)) { - LOG_WINDOW(" getFieldSlotWithCheck error "); + status_t status = window->putLong(row, column, value); + + if (status) { + LOG_WINDOW("Failed to put long. error=%d", status); return false; } @@ -513,8 +449,10 @@ static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, jdouble value, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - if (!window->putDouble(row, column, value)) { - LOG_WINDOW(" getFieldSlotWithCheck error "); + status_t status = window->putDouble(row, column, value); + + if (status) { + LOG_WINDOW("Failed to put double. error=%d", status); return false; } @@ -525,8 +463,10 @@ static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - if (!window->putNull(row, column)) { - LOG_WINDOW(" getFieldSlotWithCheck error "); + status_t status = window->putNull(row, column); + + if (status) { + LOG_WINDOW("Failed to put null. error=%d", status); return false; } @@ -537,14 +477,14 @@ static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - { "nativeInitializeEmpty", "(IZ)I", - (void*)nativeInitializeEmpty }, - { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I", - (void*)nativeInitializeFromBinder }, + { "nativeCreate", "(Ljava/lang/String;IZ)I", + (void*)nativeCreate }, + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I", + (void*)nativeCreateFromParcel }, { "nativeDispose", "(I)V", (void*)nativeDispose }, - { "nativeGetBinder", "(I)Landroid/os/IBinder;", - (void*)nativeGetBinder }, + { "nativeWriteToParcel", "(ILandroid/os/Parcel;)V", + (void*)nativeWriteToParcel }, { "nativeClear", "(I)V", (void*)nativeClear }, { "nativeGetNumRows", "(I)I", diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index 022a64c5b65d..8170f46ecb29 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -61,7 +61,8 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, window->getNumRows(), window->size(), window->freeSpace()); int numColumns = sqlite3_column_count(statement); - if (!window->setNumColumns(numColumns)) { + status_t status = window->setNumColumns(numColumns); + if (status) { LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns); jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch"); return 0; @@ -88,10 +89,10 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, // Allocate a new field directory for the row. This pointer is not reused // since it may be possible for it to be relocated on a call to alloc() when // the field data is being allocated. - field_slot_t* fieldDir = window->allocRow(); - if (!fieldDir) { - LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", - startPos, addedRows); + status = window->allocRow(); + if (status) { + LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d", + startPos, addedRows, status); windowFull = true; continue; } @@ -101,37 +102,28 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, int type = sqlite3_column_type(statement, i); if (type == SQLITE_TEXT) { // TEXT data -#if WINDOW_STORAGE_UTF8 - const uint8_t* text = reinterpret_cast<const uint8_t*>( + const char* text = reinterpret_cast<const char*>( sqlite3_column_text(statement, i)); // SQLite does not include the NULL terminator in size, but does // ensure all strings are NULL terminated, so increase size by // one to make sure we store the terminator. - size_t size = sqlite3_column_bytes(statement, i) + 1; -#else - const uint8_t* text = reinterpret_cast<const uint8_t*>( - sqlite3_column_text16(statement, i)); - size_t size = sqlite3_column_bytes16(statement, i); -#endif - int offset = window->alloc(size); - if (!offset) { - LOG_WINDOW("Failed allocating %u bytes for text/blob at %d,%d", size, - startPos + addedRows, i); + size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1; + status = window->putString(addedRows, i, text, sizeIncludingNull); + if (status) { + LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d", + sizeIncludingNull, startPos + addedRows, i, status); windowFull = true; break; } - window->copyIn(offset, text, size); - - field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); - fieldSlot->type = FIELD_TYPE_STRING; - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = size; - LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + addedRows, i, size); + LOG_WINDOW("%d,%d is TEXT with %u bytes", + startPos + addedRows, i, sizeIncludingNull); } else if (type == SQLITE_INTEGER) { // INTEGER data int64_t value = sqlite3_column_int64(statement, i); - if (!window->putLong(addedRows, i, value)) { - LOG_WINDOW("Failed allocating space for a long in column %d", i); + status = window->putLong(addedRows, i, value); + if (status) { + LOG_WINDOW("Failed allocating space for a long in column %d, error=%d", + i, status); windowFull = true; break; } @@ -139,35 +131,33 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, } else if (type == SQLITE_FLOAT) { // FLOAT data double value = sqlite3_column_double(statement, i); - if (!window->putDouble(addedRows, i, value)) { - LOG_WINDOW("Failed allocating space for a double in column %d", i); + status = window->putDouble(addedRows, i, value); + if (status) { + LOG_WINDOW("Failed allocating space for a double in column %d, error=%d", + i, status); windowFull = true; break; } LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value); } else if (type == SQLITE_BLOB) { // BLOB data - uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i); - size_t size = sqlite3_column_bytes16(statement, i); - int offset = window->alloc(size); - if (!offset) { - LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size, - startPos + addedRows, i); + const void* blob = sqlite3_column_blob(statement, i); + size_t size = sqlite3_column_bytes(statement, i); + status = window->putBlob(addedRows, i, blob, size); + if (status) { + LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d", + size, startPos + addedRows, i, status); windowFull = true; break; } - window->copyIn(offset, blob, size); - - field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); - fieldSlot->type = FIELD_TYPE_BLOB; - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = size; - LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", - startPos + addedRows, i, size, offset); + LOG_WINDOW("%d,%d is Blob with %u bytes", + startPos + addedRows, i, size); } else if (type == SQLITE_NULL) { // NULL field - if (!window->putNull(addedRows, i)) { - LOG_WINDOW("Failed allocating space for a null in column %d", i); + status = window->putNull(addedRows, i); + if (status) { + LOG_WINDOW("Failed allocating space for a null in column %d, error=%d", + i, status); windowFull = true; break; } diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index b2a78fe98222..5a9313e7f70c 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -581,8 +581,9 @@ resource usage. </p></dd> <dt><a name="screen"></a>{@code android:screenOrientation}</dt> -<dd>The orientation of the activity's display on the device. -The value can be any one of the following strings: +<dd>The orientation of the activity's display on the device. + +<p>The value can be any one of the following strings:</p> <table> <tr> @@ -640,7 +641,23 @@ is ignored, so the display will not rotate based on how the user moves the devic distinction, the system chooses the orientation using the same policy as for the "{@code unspecified}" setting.</td> </tr> -</table></dd> +</table> + +<p class="note"><strong>Note:</strong> When you declare one of the landscape or portrait values, +it is considered a hard requirement for the orientation in which the activity runs. As such, +the value you declare enables filtering by services such as Android Market so your application is +available only to devices that support the orientation required by your activities. For +example, if you declare either {@code "landscape"}, {@code "reverseLandscape"}, or +{@code "sensorLandscape"}, then your application will be available only to devices that support +landscape orientation. However, you should also explicitly declare that +your application requires either portrait or landscape orientation with the <a +href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> +element. For example, <code><uses-feature +android:name="android.hardware.screen.portrait"/></code>. This is purely a filtering behavior +provided by Android Market (and other services that support it) and the platform itself does not +control whether your app can be installed when a device supports only certain orientations.</p> + +</dd> <dt><a name="state"></a>{@code android:stateNotNeeded}</dt> <dd>Whether or not the activity can be killed and successfully restarted diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd index 49d6b6204779..9f806380e921 100644 --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -111,7 +111,7 @@ the device before installing an application. However, other services with your application. For this reason, it's very important that you declare all of the features (from the list below) that your application uses. </p> -<p>For some features, there may exist a specfic attribute that allows you to define +<p>For some features, there may exist a specific attribute that allows you to define a version of the feature, such as the version of Open GL used (declared with <a href="#glEsVersion"><code>glEsVersion</code></a>). Other features that either do or do not exist for a device, such as a camera, are declared using the @@ -277,7 +277,7 @@ application absolutely requires the feature and cannot function properly without it (<code>"true"</code>), or whether the application prefers to use the feature if available, but is designed to run without it (<code>"false"</code>).</p> -<p>Android Market handles explictly declared features in this way: </p> +<p>Android Market handles explicitly declared features in this way: </p> <ul> <li>If a feature is explicitly declared as being required, Android Market adds @@ -287,7 +287,7 @@ For example: <pre><uses-feature android:name="android.hardware.camera" android:required="true" /></pre></li> <li>If a feature is explicitly declared as <em>not</em> being required, Android Market <em>does not</em> add the feature to the list of required features. For -that reason, an explicity declared non-required feature is never considered when +that reason, an explicitly declared non-required feature is never considered when filtering the application. Even if the device does not provide the declared feature, Android Market will still consider the application compatible with the device and will show it to the user, unless other filtering rules apply. For @@ -650,6 +650,40 @@ the device.</td> <td>The application uses the device's proximity sensor.</td> <td></td> </tr> + +<tr> + <td rowspan="2">Screen</td> + <td><code>android.hardware.screen.landscape</code></td> + <td>The application requires landscape orientation.</td> + <td rowspan="2"> + <p>For example, if your app requires portrait orientation, you should declare +<code><uses-feature android:name="android.hardware.screen.portrait"/></code> so that only devices +that support portrait orientation (whether always or by user choice) can install your app. If your +application <em>supports</em> both orientations, then you don't need to declare either.</p> + <p>Both orientations are assumed <em>not required</em>, by default, so your app may be installed +on devices that support one or both orientations. However, if any of your activities request that +they run in a specific orientation, using the <a +href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code +android:screenOrientation}</a> attribute, then this also declares that the application requires that +orientation. For example, if you declare <a +href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code +android:screenOrientation}</a> with either {@code "landscape"}, {@code "reverseLandscape"}, or +{@code "sensorLandscape"}, then your application will be available only to devices that support +landscape orientation. As a best practice, you should still declare your requirement for this +orientation using a {@code <uses-feature>} element. If you declare an orientation for your +activity using <a href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code +android:screenOrientation}</a>, but don't actually <em>require</em> it, you can disable the +requirement by declaring the orientation with a {@code <uses-feature>} element and include +{@code android:required="false"}.</p> + <p>For backwards compatibility, any device running a platform version that supports only API +level 12 or lower is assumed to support both landscape and portrait.</p> + </td> +</tr> +<tr> + <td><code>android.hardware.screen.portrait</code></td> + <td>The application requires portrait orientation.</td> +</tr> + <tr> <td rowspan="3">Telephony</td> <td><code>android.hardware.telephony</code></td> @@ -672,17 +706,18 @@ device.</td> </tr> <tr> - <td rowspan="5">Touchscreen</td> + <td rowspan="7">Touchscreen</td> <td><code>android.hardware.faketouch</code></td> <td>The application uses basic touch interaction events, such as "click down", "click up", and drag.</td> - <td>When declared, this indicates that the application is compatible with a device that offers an -emulated touchscreen ("fake touch" interface), or better. A device that offers a fake touch -interface provides a user input system that emulates a subset of touchscreen capabilities. For -example, a mouse or remote control that drives an on-screen cursor provides a fake touch interface. -If your application requires only basic point and click interaction, you should declare this -feature. Because this is the minimum level of touch interaction, your app will also be compatible -with devices that offer more complex touch interfaces. + <td><p>When declared as required, this indicates that the application is compatible with a device +only if it offers an emulated touchscreen ("fake touch" interface), or better. A device that offers +a fake touch interface provides a user input system that emulates a subset of touchscreen +capabilities. For example, a mouse or remote control that drives an on-screen cursor provides a fake +touch interface. If your application requires basic point and click interaction (in other +words, it won't work with <em>only</em> a d-pad controller), you should declare this feature. +Because this is the minimum level of touch interaction, your app will also be compatible with +devices that offer more complex touch interfaces.</p> <p class="note"><strong>Note:</strong> Because applications require the {@code android.hardware.touchscreen} feature by default, if you want your application to be available to devices that provide a fake touch interface, you must also explicitly declare that a touch screen is @@ -690,18 +725,53 @@ devices that provide a fake touch interface, you must also explicitly declare th android:name="android.hardware.touchscreen" <strong>android:required="false"</strong> />}</p></td> </tr> + +<tr> + <td><code>android.hardware.faketouch.multitouch.distinct</code></td> + <td>The application performs distinct tracking of two or more "fingers" on a fake touch +interface. This is a superset of the faketouch feature.</td> + <td><p>When declared as required, this indicates that the application is compatible with a device +only if it supports touch emulation for events that supports distinct tracking of two or more +fingers, or better.</p> + <p>Unlike the distinct multitouch defined by {@code +android.hardware.touchscreen.multitouch.distinct}, input devices that support distinct multi-touch +with a fake touch interface will not support all two-finger gestures, because the input is +being transformed to cursor movement on the screen. That is, single finger gestures on such a device +move a cursor; two-finger swipes will result in single-finger touch events; other two-finger +gestures will result in the corresponding two-finger touch event. An example device that supports +distinct multi-touch with a fake touch interface is one that provides a trackpad for cursor movement +which also supports two or more fingers.</p></td> +</tr> + +<tr> + <td><code>android.hardware.faketouch.multitouch.jazzhand</code></td> + <td>The application performs distinct tracking of five or more "fingers" on a fake touch +interface. This is a superset of the faketouch feature.</td> + <td><p>When declared as required, this indicates that the application is compatible with a device +only if it supports touch emulation for events that supports distinct tracking of five or more +fingers.</p> + <p>Unlike the distinct multitouch defined by {@code +android.hardware.touchscreen.multitouch.jazzhand}, input devices that support jazzhand multi-touch +with a fake touch interface will not support all five-finger gestures, because the input is being +transformed to cursor movement on the screen. That is, single finger gestures on such a device move +a cursor; multi-finger gestures will result in single-finger touch events; other multi-finger +gestures will result in the corresponding multi-finger touch event. An example device that supports +distinct multi-touch with a fake touch interface is one that provides a trackpad for cursor movement +which also supports five or more fingers.</p></td> +</tr> + <tr> <td><code>android.hardware.touchscreen</code></td> <td>The application uses touchscreen capabilities for gestures that are more interactive -than basic touch events, such as a fling. This is a superset of the faketouch features.</td> - <td>By default, your application requires this. As such, your application is -<em>not</em> available to devices that provide only an emulated touch interface ("fake touch"), by -default. If you want your application available to devices that provide a fake touch interface, -you must explicitly declare that a touch screen is not required, by -declaring {@code android.hardware.touchscreen} with {@code android:required="false"}. You should -do so even if your application uses—but does not <em>require</em>—a real touch screen -interface. -<p>If your application <em>does require</em> a basic touch interface (in order to perform touch +than basic touch events, such as a fling. This is a superset of the basic faketouch feature.</td> + <td><p>By default, your application requires this. As such, your application is <em>not</em> +available to devices that provide only an emulated touch interface ("fake touch"), by default. If +you want your application available to devices that provide a fake touch interface (or even devices +that provide only a d-pad controller), you must explicitly declare that a touch screen is not +required, by declaring {@code android.hardware.touchscreen} with {@code android:required="false"}. +You should do so even if your application uses—but does not <em>require</em>—a real +touch screen interface.</p> +<p>If your application <em>does require</em> a touch interface (in order to perform touch gestures such as a fling), then you don't need to do anything, because this is required by default. However, it's best if you explicitly declare all features used by your application, so you should still declare this if your app uses it.</p> @@ -712,7 +782,7 @@ should declare the advanced touch screen features below.</p></td> <td><code>android.hardware.touchscreen.multitouch</code></td> <td>The application uses basic two-point multitouch capabilities on the device screen, such as for pinch gestures, but does not need to track touches independently. This -is a superset of touchscreen features.</td> +is a superset of touchscreen feature.</td> <td>This implicitly declares the <code>android.hardware.touchscreen</code> parent feature, unless declared with <code>android:required="false"</code>. </td> </tr> @@ -720,7 +790,7 @@ declared with <code>android:required="false"</code>. </td> <td><code>android.hardware.touchscreen.multitouch.distinct</code></td> <td>Subfeature. The application uses advanced multipoint multitouch capabilities on the device screen, such as for tracking two or more points fully -independently. This is a superset of multitouch features.</td> +independently. This is a superset of multitouch feature.</td> <td rowspan="2">This implicitly declares the <code>android.hardware.touchscreen.multitouch</code> parent feature, unless declared with <code>android:required="false"</code>. </td> </tr> @@ -728,7 +798,7 @@ parent feature, unless declared with <code>android:required="false"</code>. </td <td><code>android.hardware.touchscreen.multitouch.jazzhand</code></td> <td>The application uses advanced multipoint multitouch capabilities on the device screen, for tracking up to five points fully -independently. This is a superset of distinct multitouch features.</td> +independently. This is a superset of distinct multitouch feature.</td> </tr> <tr> diff --git a/docs/html/sdk/android-3.2.jd b/docs/html/sdk/android-3.2.jd index ea2b4ed6ad2e..aeaf9c8171ed 100644 --- a/docs/html/sdk/android-3.2.jd +++ b/docs/html/sdk/android-3.2.jd @@ -550,7 +550,11 @@ portrait orientation.</li> <p>A typical application that functions properly in both landscape and portrait orientations would not normally need to declare an orientation requirement. Rather, an application designed primarily for one orientation, such as an app designed for a television, could declare one of the constants to ensure that it isn't available to devices that don't provide that orientation.</p> -<p>If the application is targeting API level 12 or lower, the platform assumes that if app has not specified whether it requires portrait or landscape, both orientations are required.</p> +<p>If any of activities declared in the manifest request that they run in a specific orientation, +using the <a href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code +android:screenOrientation}</a> attribute, then this also declares that the application +requires that orientation.</p> + </li> <li>Other feature constants diff --git a/docs/html/sdk/oem-usb.jd b/docs/html/sdk/oem-usb.jd index ad3be4a6df10..88bf0089b30d 100644 --- a/docs/html/sdk/oem-usb.jd +++ b/docs/html/sdk/oem-usb.jd @@ -55,6 +55,14 @@ href="http://drivers.cmcs.com.tw/">http://drivers.cmcs.com.tw/</a></td> </tr> <tr> <td> + Fujitsu + </td> + <td><a +href="http://www.fmworld.net/product/phone/sp/android/develop/">http://www.fmworld.net/product/phone/sp/android/develop/</a> + </td> + </tr> + <tr> + <td> Fujitsu Toshiba </td> <td><a diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h index d227244df92c..5d490ed988af 100644 --- a/include/binder/CursorWindow.h +++ b/include/binder/CursorWindow.h @@ -21,18 +21,8 @@ #include <stddef.h> #include <stdint.h> -#include <binder/IMemory.h> -#include <utils/RefBase.h> - -#define DEFAULT_WINDOW_SIZE 4096 -#define WINDOW_ALLOCATION_SIZE 4096 - -#define ROW_SLOT_CHUNK_NUM_ROWS 16 - -// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS, -// with an offset after the rows that points to the next chunk -#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t)) - +#include <binder/Parcel.h> +#include <utils/String8.h> #if LOG_NDEBUG @@ -46,176 +36,157 @@ #endif - -// When defined to true strings are stored as UTF8, otherwise they're UTF16 -#define WINDOW_STORAGE_UTF8 1 - -// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window -#define WINDOW_STORAGE_INLINE_NUMERICS 1 - namespace android { -typedef struct -{ - uint32_t numRows; - uint32_t numColumns; -} window_header_t; - -typedef struct -{ - uint32_t offset; -} row_slot_t; - -typedef struct -{ - uint8_t type; - union { - double d; - int64_t l; - struct { - uint32_t offset; - uint32_t size; - } buffer; - } data; -} __attribute__((packed)) field_slot_t; - -#define FIELD_TYPE_NULL 0 -#define FIELD_TYPE_INTEGER 1 -#define FIELD_TYPE_FLOAT 2 -#define FIELD_TYPE_STRING 3 -#define FIELD_TYPE_BLOB 4 - /** * This class stores a set of rows from a database in a buffer. The begining of the - * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by - * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case + * window has first chunk of RowSlots, which are offsets to the row directory, followed by + * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a - * field_slot_t per column, which has the size, offset, and type of the data for that field. + * FieldSlot per column, which has the size, offset, and type of the data for that field. * Note that the data types come from sqlite3.h. + * + * Strings are stored in UTF-8. */ -class CursorWindow -{ +class CursorWindow { + CursorWindow(const String8& name, int ashmemFd, + void* data, size_t size, bool readOnly); + public: - CursorWindow(size_t maxSize); - CursorWindow(){} - bool setMemory(const sp<IMemory>&); - ~CursorWindow(); - - bool initBuffer(bool localOnly); - sp<IMemory> getMemory() {return mMemory;} - - size_t size() {return mSize;} - uint8_t * data() {return mData;} - uint32_t getNumRows() {return mHeader->numRows;} - uint32_t getNumColumns() {return mHeader->numColumns;} - void freeLastRow() { - if (mHeader->numRows > 0) { - mHeader->numRows--; - } - } - bool setNumColumns(uint32_t numColumns) - { - uint32_t cur = mHeader->numColumns; - if (cur > 0 && cur != numColumns) { - LOGE("Trying to go from %d columns to %d", cur, numColumns); - return false; - } - mHeader->numColumns = numColumns; - return true; - } - - int32_t freeSpace(); - - void clear(); - - /** - * Allocate a row slot and its directory. The returned - * pointer points to the begining of the row's directory - * or NULL if there wasn't room. The directory is - * initialied with NULL entries for each field. - */ - field_slot_t * allocRow(); - - /** - * Allocate a portion of the window. Returns the offset - * of the allocation, or 0 if there isn't enough space. - * If aligned is true, the allocation gets 4 byte alignment. - */ - uint32_t alloc(size_t size, bool aligned = false); - - /** - * Copy data into the window at the given offset. - */ - void copyIn(uint32_t offset, uint8_t const * data, size_t size); - void copyIn(uint32_t offset, int64_t data); - void copyIn(uint32_t offset, double data); - - void copyOut(uint32_t offset, uint8_t * data, size_t size); - int64_t copyOutLong(uint32_t offset); - double copyOutDouble(uint32_t offset); - - bool putLong(unsigned int row, unsigned int col, int64_t value); - bool putDouble(unsigned int row, unsigned int col, double value); - bool putNull(unsigned int row, unsigned int col); - - bool getLong(unsigned int row, unsigned int col, int64_t * valueOut); - bool getDouble(unsigned int row, unsigned int col, double * valueOut); - bool getNull(unsigned int row, unsigned int col, bool * valueOut); - - uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;} - - row_slot_t * allocRowSlot(); - - row_slot_t * getRowSlot(int row); - - /** - * return NULL if Failed to find rowSlot or - * Invalid rowSlot - */ - field_slot_t * getFieldSlotWithCheck(int row, int column); - field_slot_t * getFieldSlot(int row, int column) - { - int fieldDirOffset = getRowSlot(row)->offset; - return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; - } - - int64_t getFieldSlotValueLong(field_slot_t* fieldSlot) { -#if WINDOW_STORAGE_INLINE_NUMERICS + /* Field types. */ + enum { + FIELD_TYPE_NULL = 0, + FIELD_TYPE_INTEGER = 1, + FIELD_TYPE_FLOAT = 2, + FIELD_TYPE_STRING = 3, + FIELD_TYPE_BLOB = 4, + }; + + /* Opaque type that describes a field slot. */ + struct FieldSlot { + private: + int32_t type; + union { + double d; + int64_t l; + struct { + uint32_t offset; + uint32_t size; + } buffer; + } data; + + friend class CursorWindow; + } __attribute((packed)); + + ~CursorWindow(); + + static status_t create(const String8& name, size_t size, bool localOnly, + CursorWindow** outCursorWindow); + static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow); + + status_t writeToParcel(Parcel* parcel); + + inline String8 name() { return mName; } + inline size_t size() { return mSize; } + inline size_t freeSpace() { return mSize - mHeader->freeOffset; } + inline uint32_t getNumRows() { return mHeader->numRows; } + inline uint32_t getNumColumns() { return mHeader->numColumns; } + + status_t clear(); + status_t setNumColumns(uint32_t numColumns); + + /** + * Allocate a row slot and its directory. + * The row is initialized will null entries for each field. + */ + status_t allocRow(); + status_t freeLastRow(); + + status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size); + status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull); + status_t putLong(uint32_t row, uint32_t column, int64_t value); + status_t putDouble(uint32_t row, uint32_t column, double value); + status_t putNull(uint32_t row, uint32_t column); + + /** + * Gets the field slot at the specified row and column. + * Returns null if the requested row or column is not in the window. + */ + FieldSlot* getFieldSlot(uint32_t row, uint32_t column); + + inline int32_t getFieldSlotType(FieldSlot* fieldSlot) { + return fieldSlot->type; + } + + inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) { return fieldSlot->data.l; -#else - return copyOutLong(fieldSlot->data.buffer.offset); -#endif } - double getFieldSlotValueDouble(field_slot_t* fieldSlot) { -#if WINDOW_STORAGE_INLINE_NUMERICS + inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) { return fieldSlot->data.d; -#else - return copyOutDouble(fieldSlot->data.buffer.offset); -#endif } -#if WINDOW_STORAGE_UTF8 - char* getFieldSlotValueString(field_slot_t* fieldSlot) { - return reinterpret_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset)); + inline const char* getFieldSlotValueString(FieldSlot* fieldSlot, + size_t* outSizeIncludingNull) { + *outSizeIncludingNull = fieldSlot->data.buffer.size; + return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset)); } -#else - char16_t* getFieldSlotValueString(field_slot_t* fieldSlot) { - return reinterpret_cast<char16_t*>(offsetToPtr(fieldSlot->data.buffer.offset)); + + inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) { + *outSize = fieldSlot->data.buffer.size; + return offsetToPtr(fieldSlot->data.buffer.offset); } -#endif private: - uint8_t * mData; + static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100; + + struct Header { + // Offset of the lowest unused byte in the window. + uint32_t freeOffset; + + // Offset of the first row slot chunk. + uint32_t firstChunkOffset; + + uint32_t numRows; + uint32_t numColumns; + }; + + struct RowSlot { + uint32_t offset; + }; + + struct RowSlotChunk { + RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS]; + uint32_t nextChunkOffset; + }; + + String8 mName; + int mAshmemFd; + void* mData; size_t mSize; - size_t mMaxSize; - window_header_t * mHeader; - sp<IMemory> mMemory; + bool mReadOnly; + Header* mHeader; + + inline void* offsetToPtr(uint32_t offset) { + return static_cast<uint8_t*>(mData) + offset; + } + + inline uint32_t offsetFromPtr(void* ptr) { + return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData); + } /** - * Offset of the lowest unused data byte in the array. + * Allocate a portion of the window. Returns the offset + * of the allocation, or 0 if there isn't enough space. + * If aligned is true, the allocation gets 4 byte alignment. */ - uint32_t mFreeOffset; + uint32_t alloc(size_t size, bool aligned = false); + + RowSlot* getRowSlot(uint32_t row); + RowSlot* allocRowSlot(); + + status_t putBlobOrString(uint32_t row, uint32_t column, + const void* value, size_t size, int32_t type); }; }; // namespace android diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index a520a6a12e53..cd2c0a37aa3e 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -504,6 +504,25 @@ public: // Example value: "true" or "false". Read only. static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[]; + // The state of the video stabilization. If set to true, both the + // preview stream and the recorded video stream are stabilized by + // the camera. Only valid to set if KEY_VIDEO_STABILIZATION_SUPPORTED is + // set to true. + // + // The value of this key can be changed any time the camera is + // open. If preview or recording is active, it is acceptable for + // there to be a slight video glitch when video stabilization is + // toggled on and off. + // + // This only stabilizes video streams (between-frames stabilization), and + // has no effect on still image capture. + static const char KEY_VIDEO_STABILIZATION[]; + + // Returns true if video stabilization is supported. That is, applications + // can set KEY_VIDEO_STABILIZATION to true and have a stabilized preview + // stream and record stabilized videos. + static const char KEY_VIDEO_STABILIZATION_SUPPORTED[]; + // Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED. static const char TRUE[]; static const char FALSE[]; diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp index b02374f1ed78..1b85a71ca8ff 100644 --- a/libs/binder/CursorWindow.cpp +++ b/libs/binder/CursorWindow.cpp @@ -19,8 +19,9 @@ #include <utils/Log.h> #include <binder/CursorWindow.h> -#include <binder/MemoryHeapBase.h> -#include <binder/MemoryBase.h> + +#include <cutils/ashmem.h> +#include <sys/mman.h> #include <assert.h> #include <string.h> @@ -28,350 +29,325 @@ namespace android { -CursorWindow::CursorWindow(size_t maxSize) : - mMaxSize(maxSize) -{ +CursorWindow::CursorWindow(const String8& name, int ashmemFd, + void* data, size_t size, bool readOnly) : + mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) { + mHeader = static_cast<Header*>(mData); } -bool CursorWindow::setMemory(const sp<IMemory>& memory) -{ - mMemory = memory; - mData = (uint8_t *) memory->pointer(); - if (mData == NULL) { - return false; - } - mHeader = (window_header_t *) mData; - - // Make the window read-only - ssize_t size = memory->size(); - mSize = size; - mMaxSize = size; - mFreeOffset = size; -LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData); - return true; +CursorWindow::~CursorWindow() { + ::munmap(mData, mSize); + ::close(mAshmemFd); } -bool CursorWindow::initBuffer(bool localOnly) -{ - //TODO Use a non-memory dealer mmap region for localOnly - - sp<MemoryHeapBase> heap; - heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow"); - if (heap != NULL) { - mMemory = new MemoryBase(heap, 0, mMaxSize); - if (mMemory != NULL) { - mData = (uint8_t *) mMemory->pointer(); - if (mData) { - mHeader = (window_header_t *) mData; - mSize = mMaxSize; - - // Put the window into a clean state - clear(); - LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData); - return true; +status_t CursorWindow::create(const String8& name, size_t size, bool localOnly, + CursorWindow** outCursorWindow) { + String8 ashmemName("CursorWindow: "); + ashmemName.append(name); + ashmemName.append(localOnly ? " (local)" : " (remote)"); + + status_t result; + int ashmemFd = ashmem_create_region(ashmemName.string(), size); + if (ashmemFd < 0) { + result = -errno; + } else { + result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE); + if (result >= 0) { + void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); + if (data == MAP_FAILED) { + result = -errno; + } else { + result = ashmem_set_prot_region(ashmemFd, PROT_READ); + if (result >= 0) { + CursorWindow* window = new CursorWindow(name, ashmemFd, + data, size, false /*readOnly*/); + result = window->clear(); + if (!result) { + LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " + "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + window->mHeader->freeOffset, + window->mHeader->numRows, + window->mHeader->numColumns, + window->mSize, window->mData); + *outCursorWindow = window; + return OK; + } + delete window; + } } - } - LOGE("CursorWindow heap allocation failed"); - return false; + ::munmap(data, size); + } + ::close(ashmemFd); + } + *outCursorWindow = NULL; + return result; +} + +status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { + String8 name = parcel->readString8(); + + status_t result; + int ashmemFd = parcel->readFileDescriptor(); + if (ashmemFd == int(BAD_TYPE)) { + result = BAD_TYPE; } else { - LOGE("failed to create the CursorWindow heap"); - return false; + ssize_t size = ashmem_get_size_region(ashmemFd); + if (size < 0) { + result = UNKNOWN_ERROR; + } else { + int dupAshmemFd = ::dup(ashmemFd); + if (dupAshmemFd < 0) { + result = -errno; + } else { + void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0); + if (data == MAP_FAILED) { + result = -errno; + } else { + CursorWindow* window = new CursorWindow(name, dupAshmemFd, + data, size, true /*readOnly*/); + LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " + "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + window->mHeader->freeOffset, + window->mHeader->numRows, + window->mHeader->numColumns, + window->mSize, window->mData); + *outCursorWindow = window; + return OK; + } + ::close(dupAshmemFd); + } + } } + *outCursorWindow = NULL; + return result; } -CursorWindow::~CursorWindow() -{ - // Everything that matters is a smart pointer +status_t CursorWindow::writeToParcel(Parcel* parcel) { + status_t status = parcel->writeString8(mName); + if (!status) { + status = parcel->writeDupFileDescriptor(mAshmemFd); + } + return status; } -void CursorWindow::clear() -{ +status_t CursorWindow::clear() { + if (mReadOnly) { + return INVALID_OPERATION; + } + + mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk); + mHeader->firstChunkOffset = sizeof(Header); mHeader->numRows = 0; mHeader->numColumns = 0; - mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE; - // Mark the first chunk's next 'pointer' as null - *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0; + + RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset)); + firstChunk->nextChunkOffset = 0; + return OK; } -int32_t CursorWindow::freeSpace() -{ - int32_t freeSpace = mSize - mFreeOffset; - if (freeSpace < 0) { - freeSpace = 0; +status_t CursorWindow::setNumColumns(uint32_t numColumns) { + if (mReadOnly) { + return INVALID_OPERATION; + } + + uint32_t cur = mHeader->numColumns; + if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) { + LOGE("Trying to go from %d columns to %d", cur, numColumns); + return INVALID_OPERATION; } - return freeSpace; + mHeader->numColumns = numColumns; + return OK; } -field_slot_t * CursorWindow::allocRow() -{ +status_t CursorWindow::allocRow() { + if (mReadOnly) { + return INVALID_OPERATION; + } + // Fill in the row slot - row_slot_t * rowSlot = allocRowSlot(); + RowSlot* rowSlot = allocRowSlot(); if (rowSlot == NULL) { - return NULL; + return NO_MEMORY; } // Allocate the slots for the field directory - size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t); - uint32_t fieldDirOffset = alloc(fieldDirSize); + size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); + uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/); if (!fieldDirOffset) { mHeader->numRows--; - LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows); - return NULL; + LOG_WINDOW("The row failed, so back out the new row accounting " + "from allocRowSlot %d", mHeader->numRows); + return NO_MEMORY; } - field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset); - memset(fieldDir, 0x0, fieldDirSize); + FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset)); + memset(fieldDir, 0, fieldDirSize); -LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset); + LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", + mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); rowSlot->offset = fieldDirOffset; + return OK; +} + +status_t CursorWindow::freeLastRow() { + if (mReadOnly) { + return INVALID_OPERATION; + } - return fieldDir; + if (mHeader->numRows > 0) { + mHeader->numRows--; + } + return OK; } -uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) -{ - int32_t size; +uint32_t CursorWindow::alloc(size_t size, bool aligned) { uint32_t padding; if (aligned) { // 4 byte alignment - padding = 4 - (mFreeOffset & 0x3); + padding = (~mHeader->freeOffset + 1) & 3; } else { padding = 0; } - size = requestedSize + padding; - - if (size > freeSpace()) { - LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, - freeSpace(), mHeader->numRows); - // Only grow the window if the first row doesn't fit - if (mHeader->numRows > 1) { - LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows, - mMaxSize); - return 0; - } - - // Find a new size that will fit the allocation - int allocated = mSize - freeSpace(); - int newSize = mSize + WINDOW_ALLOCATION_SIZE; - while (size > (newSize - allocated)) { - newSize += WINDOW_ALLOCATION_SIZE; - if (newSize > mMaxSize) { - LOGE("Attempting to grow window beyond max size (%d)", mMaxSize); - return 0; - } - } -LOG_WINDOW("found size %d", newSize); - mSize = newSize; + uint32_t offset = mHeader->freeOffset + padding; + uint32_t nextFreeOffset = offset + size; + if (nextFreeOffset > mSize) { + LOGE("Window is full: requested allocation %d bytes, " + "free space %d bytes, window size %d bytes", + size, freeSpace(), mSize); + return 0; } - uint32_t offset = mFreeOffset + padding; - mFreeOffset += size; + mHeader->freeOffset = nextFreeOffset; return offset; } -row_slot_t * CursorWindow::getRowSlot(int row) -{ - LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row); - int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS; - int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS; - int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); - uint8_t * rowChunk = mData + sizeof(window_header_t); - for (int i = 0; i < chunkNum; i++) { - rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset))); - chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); +CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) { + uint32_t chunkPos = row; + RowSlotChunk* chunk = static_cast<RowSlotChunk*>( + offsetToPtr(mHeader->firstChunkOffset)); + while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) { + chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); + chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; } - return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); - LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row); + return &chunk->slots[chunkPos]; } -row_slot_t * CursorWindow::allocRowSlot() -{ - int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS; - int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS; - int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); - uint8_t * rowChunk = mData + sizeof(window_header_t); -LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos); - for (int i = 0; i < chunkNum; i++) { - uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset)); -LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset); - if (nextChunkOffset == 0) { - // Allocate a new row chunk - nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true); - if (nextChunkOffset == 0) { +CursorWindow::RowSlot* CursorWindow::allocRowSlot() { + uint32_t chunkPos = mHeader->numRows; + RowSlotChunk* chunk = static_cast<RowSlotChunk*>( + offsetToPtr(mHeader->firstChunkOffset)); + while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) { + chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); + chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; + } + if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { + if (!chunk->nextChunkOffset) { + chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/); + if (!chunk->nextChunkOffset) { return NULL; } - rowChunk = offsetToPtr(nextChunkOffset); -LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk); - *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData; - // Mark the new chunk's next 'pointer' as null - *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0; - } else { -LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset); - rowChunk = offsetToPtr(nextChunkOffset); - chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); } + chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); + chunk->nextChunkOffset = 0; + chunkPos = 0; } - mHeader->numRows++; - - return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); -} - -field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column) -{ - if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { - LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.", - row, column, mHeader->numRows, mHeader->numColumns); - return NULL; - } - row_slot_t * rowSlot = getRowSlot(row); - if (!rowSlot) { - LOGE("Failed to find rowSlot for row %d", row); - return NULL; - } - if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { - LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); - return NULL; - } - int fieldDirOffset = rowSlot->offset; - return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; + mHeader->numRows += 1; + return &chunk->slots[chunkPos]; } -void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) -{ - assert(offset + size <= mSize); - memcpy(mData + offset, data, size); -} - -void CursorWindow::copyIn(uint32_t offset, int64_t data) -{ - assert(offset + sizeof(int64_t) <= mSize); - memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t)); -} - -void CursorWindow::copyIn(uint32_t offset, double data) -{ - assert(offset + sizeof(double) <= mSize); - memcpy(mData + offset, (uint8_t *)&data, sizeof(double)); +CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) { + if (row >= mHeader->numRows || column >= mHeader->numColumns) { + LOGE("Failed to read row %d, column %d from a CursorWindow which " + "has %d rows, %d columns.", + row, column, mHeader->numRows, mHeader->numColumns); + return NULL; + } + RowSlot* rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d.", row); + return NULL; + } + FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset)); + return &fieldDir[column]; } -void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size) -{ - assert(offset + size <= mSize); - memcpy(data, mData + offset, size); +status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) { + return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB); } -int64_t CursorWindow::copyOutLong(uint32_t offset) -{ - int64_t value; - assert(offset + sizeof(int64_t) <= mSize); - memcpy(&value, mData + offset, sizeof(int64_t)); - return value; +status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value, + size_t sizeIncludingNull) { + return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING); } -double CursorWindow::copyOutDouble(uint32_t offset) -{ - double value; - assert(offset + sizeof(double) <= mSize); - memcpy(&value, mData + offset, sizeof(double)); - return value; -} +status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, + const void* value, size_t size, int32_t type) { + if (mReadOnly) { + return INVALID_OPERATION; + } -bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + FieldSlot* fieldSlot = getFieldSlot(row, column); if (!fieldSlot) { - return false; + return BAD_VALUE; } -#if WINDOW_STORAGE_INLINE_NUMERICS - fieldSlot->data.l = value; -#else - int offset = alloc(sizeof(int64_t)); + uint32_t offset = alloc(size); if (!offset) { - return false; + return NO_MEMORY; } - copyIn(offset, value); + memcpy(offsetToPtr(offset), value, size); + fieldSlot->type = type; fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = sizeof(int64_t); -#endif - fieldSlot->type = FIELD_TYPE_INTEGER; - return true; + fieldSlot->data.buffer.size = size; + return OK; } -bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); - if (!fieldSlot) { - return false; +status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) { + if (mReadOnly) { + return INVALID_OPERATION; } -#if WINDOW_STORAGE_INLINE_NUMERICS - fieldSlot->data.d = value; -#else - int offset = alloc(sizeof(int64_t)); - if (!offset) { - return false; + FieldSlot* fieldSlot = getFieldSlot(row, column); + if (!fieldSlot) { + return BAD_VALUE; } - copyIn(offset, value); - - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = sizeof(double); -#endif - fieldSlot->type = FIELD_TYPE_FLOAT; - return true; + fieldSlot->type = FIELD_TYPE_INTEGER; + fieldSlot->data.l = value; + return OK; } -bool CursorWindow::putNull(unsigned int row, unsigned int col) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); - if (!fieldSlot) { - return false; +status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) { + if (mReadOnly) { + return INVALID_OPERATION; } - fieldSlot->type = FIELD_TYPE_NULL; - fieldSlot->data.buffer.offset = 0; - fieldSlot->data.buffer.size = 0; - return true; -} - -bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); - if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) { - return false; + FieldSlot* fieldSlot = getFieldSlot(row, column); + if (!fieldSlot) { + return BAD_VALUE; } - *valueOut = getFieldSlotValueLong(fieldSlot); - return true; + fieldSlot->type = FIELD_TYPE_FLOAT; + fieldSlot->data.d = value; + return OK; } -bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); - if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) { - return false; +status_t CursorWindow::putNull(uint32_t row, uint32_t column) { + if (mReadOnly) { + return INVALID_OPERATION; } - *valueOut = getFieldSlotValueDouble(fieldSlot); - return true; -} - -bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + FieldSlot* fieldSlot = getFieldSlot(row, column); if (!fieldSlot) { - return false; - } - - if (fieldSlot->type != FIELD_TYPE_NULL) { - *valueOut = false; - } else { - *valueOut = true; + return BAD_VALUE; } - return true; + + fieldSlot->type = FIELD_TYPE_NULL; + fieldSlot->data.buffer.offset = 0; + fieldSlot->data.buffer.size = 0; + return OK; } }; // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 608877eb7dd7..c7180cee03cd 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -752,7 +752,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); if (result < 0) { - status = -result; + status = result; } else { void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { @@ -760,7 +760,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) } else { result = ashmem_set_prot_region(fd, PROT_READ); if (result < 0) { - status = -result; + status = result; } else { status = writeInt32(1); if (!status) { diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp index 0dcab6b37a34..c6087b404667 100644 --- a/libs/camera/CameraParameters.cpp +++ b/libs/camera/CameraParameters.cpp @@ -88,6 +88,8 @@ const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw"; const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint"; const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported"; +const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization"; +const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported"; const char CameraParameters::TRUE[] = "true"; const char CameraParameters::FALSE[] = "false"; diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 198ae4c8f7f7..77acfe6beae8 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -35,14 +35,27 @@ import android.util.Log; import java.lang.IllegalArgumentException; /** - * TODO javadoc update for ComponentName - PendingIntent change * RemoteControlClient enables exposing information meant to be consumed by remote controls * capable of displaying metadata, artwork and media transport control buttons. - * A remote control client object is associated with a media button event receiver. This + * + * <p>A remote control client object is associated with a media button event receiver. This * event receiver must have been previously registered with * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the * RemoteControlClient can be registered through * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. + * + * <p>Here is an example of creating a RemoteControlClient instance after registering a media + * button event receiver: + * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName()); + * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver); + * // build the PendingIntent for the remote control client + * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + * mediaButtonIntent.setComponent(myEventReceiver); + * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); + * // create and register the remote control client + * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent); + * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre> */ public class RemoteControlClient { diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java index fbf00d2e3ff3..886a14d9e756 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java +++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java @@ -121,6 +121,7 @@ import android.view.View; createAnimation(appearing); mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mContentView.buildLayer(); mContentAnim.start(); mVisible = appearing; diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java new file mode 100644 index 000000000000..47aa84946379 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recent; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Process; +import android.os.SystemClock; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.LruCache; + +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.PhoneStatusBar; +import com.android.systemui.statusbar.tablet.TabletStatusBar; + +public class RecentTasksLoader { + static final String TAG = "RecentTasksLoader"; + static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; + + private static final int DISPLAY_TASKS = 20; + private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps + + private Context mContext; + private RecentsPanelView mRecentsPanel; + + private AsyncTask<Void, Integer, Void> mThumbnailLoader; + private final Handler mHandler; + + private int mIconDpi; + private Bitmap mDefaultThumbnailBackground; + + public RecentTasksLoader(Context context) { + mContext = context; + + final Resources res = context.getResources(); + + // get the icon size we want -- on tablets, we use bigger icons + boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets); + int density = res.getDisplayMetrics().densityDpi; + if (isTablet) { + if (density == DisplayMetrics.DENSITY_LOW) { + mIconDpi = DisplayMetrics.DENSITY_MEDIUM; + } else if (density == DisplayMetrics.DENSITY_MEDIUM) { + mIconDpi = DisplayMetrics.DENSITY_HIGH; + } else if (density == DisplayMetrics.DENSITY_HIGH) { + mIconDpi = DisplayMetrics.DENSITY_XHIGH; + } else if (density == DisplayMetrics.DENSITY_XHIGH) { + // We'll need to use a denser icon, or some sort of a mipmap + mIconDpi = DisplayMetrics.DENSITY_XHIGH; + } + } else { + mIconDpi = res.getDisplayMetrics().densityDpi; + } + mIconDpi = isTablet ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi; + + // Render the default thumbnail background + int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width); + int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height); + int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background); + + mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(mDefaultThumbnailBackground); + c.drawColor(color); + + // If we're using the cache, begin listening to the activity manager for + // updated thumbnails + final ActivityManager am = (ActivityManager) + mContext.getSystemService(Context.ACTIVITY_SERVICE); + + mHandler = new Handler(); + } + + public void setRecentsPanel(RecentsPanelView recentsPanel) { + mRecentsPanel = recentsPanel; + } + + // Create an TaskDescription, returning null if the title or icon is null, or if it's the + // home activity + TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent, + ComponentName origActivity, CharSequence description, ActivityInfo homeInfo) { + Intent intent = new Intent(baseIntent); + if (origActivity != null) { + intent.setComponent(origActivity); + } + final PackageManager pm = mContext.getPackageManager(); + if (homeInfo == null) { + homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) + .resolveActivityInfo(pm, 0); + } + + intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) + | Intent.FLAG_ACTIVITY_NEW_TASK); + final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); + if (resolveInfo != null) { + final ActivityInfo info = resolveInfo.activityInfo; + final String title = info.loadLabel(pm).toString(); + Drawable icon = getFullResIcon(resolveInfo, pm); + + if (title != null && title.length() > 0 && icon != null) { + if (DEBUG) Log.v(TAG, "creating activity desc for id=" + + persistentTaskId + ", label=" + title); + + TaskDescription item = new TaskDescription(taskId, + persistentTaskId, resolveInfo, baseIntent, info.packageName, + description); + item.setLabel(title); + item.setIcon(icon); + + // Don't load the current home activity. + if (homeInfo != null + && homeInfo.packageName.equals(intent.getComponent().getPackageName()) + && homeInfo.name.equals(intent.getComponent().getClassName())) { + return null; + } + + return item; + } else { + if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId); + } + } + return null; + } + + void loadThumbnail(TaskDescription td) { + final ActivityManager am = (ActivityManager) + mContext.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId); + + if (DEBUG) Log.v(TAG, "Loaded bitmap for task " + + td + ": " + thumbs.mainThumbnail); + synchronized (td) { + if (thumbs != null && thumbs.mainThumbnail != null) { + td.setThumbnail(thumbs.mainThumbnail); + } else { + td.setThumbnail(mDefaultThumbnailBackground); + } + } + } + + Drawable getFullResDefaultActivityIcon() { + return getFullResIcon(Resources.getSystem(), + com.android.internal.R.mipmap.sym_def_app_icon); + } + + Drawable getFullResIcon(Resources resources, int iconId) { + try { + return resources.getDrawableForDensity(iconId, mIconDpi); + } catch (Resources.NotFoundException e) { + return getFullResDefaultActivityIcon(); + } + } + + private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) { + Resources resources; + try { + resources = packageManager.getResourcesForApplication( + info.activityInfo.applicationInfo); + } catch (PackageManager.NameNotFoundException e) { + resources = null; + } + if (resources != null) { + int iconId = info.activityInfo.getIconResource(); + if (iconId != 0) { + return getFullResIcon(resources, iconId); + } + } + return getFullResDefaultActivityIcon(); + } + + public void cancelLoadingThumbnails() { + if (mThumbnailLoader != null) { + mThumbnailLoader.cancel(false); + mThumbnailLoader = null; + } + } + + // return a snapshot of the current list of recent apps + ArrayList<TaskDescription> getRecentTasks() { + cancelLoadingThumbnails(); + + ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>(); + final PackageManager pm = mContext.getPackageManager(); + final ActivityManager am = (ActivityManager) + mContext.getSystemService(Context.ACTIVITY_SERVICE); + + final List<ActivityManager.RecentTaskInfo> recentTasks = + am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE); + + ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) + .resolveActivityInfo(pm, 0); + + HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>(); + int numTasks = recentTasks.size(); + + // skip the first task - assume it's either the home screen or the current activity. + final int first = 1; + recentTasksToKeepInCache.add(recentTasks.get(0).persistentId); + for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) { + final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i); + + TaskDescription item = createTaskDescription(recentInfo.id, + recentInfo.persistentId, recentInfo.baseIntent, + recentInfo.origActivity, recentInfo.description, homeInfo); + + if (item != null) { + tasks.add(item); + ++index; + } + } + + // when we're not using the TaskDescription cache, we load the thumbnails in the + // background + loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks)); + return tasks; + } + + private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) { + if (descriptions.size() > 0) { + if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks"); + loadThumbnail(descriptions.get(0)); + if (descriptions.size() > 1) { + mThumbnailLoader = new AsyncTask<Void, Integer, Void>() { + @Override + protected void onProgressUpdate(Integer... values) { + final TaskDescription td = descriptions.get(values[0]); + if (!isCancelled()) { + mRecentsPanel.onTaskThumbnailLoaded(td); + } + // This is to prevent the loader thread from getting ahead + // of our UI updates. + mHandler.post(new Runnable() { + @Override public void run() { + synchronized (td) { + td.notifyAll(); + } + } + }); + } + + @Override + protected Void doInBackground(Void... params) { + final int origPri = Process.getThreadPriority(Process.myTid()); + Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE); + long nextTime = SystemClock.uptimeMillis(); + for (int i=1; i<descriptions.size(); i++) { + TaskDescription td = descriptions.get(i); + loadThumbnail(td); + long now = SystemClock.uptimeMillis(); + nextTime += 150; + if (nextTime > now) { + try { + Thread.sleep(nextTime-now); + } catch (InterruptedException e) { + } + } + + if (isCancelled()) { + break; + } + synchronized (td) { + publishProgress(i); + try { + td.wait(500); + } catch (InterruptedException e) { + } + } + } + Process.setThreadPriority(origPri); + return null; + } + }; + mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index 58af25548dd6..f971d2d50632 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -32,14 +32,14 @@ import android.widget.LinearLayout; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter; +import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter; public class RecentsHorizontalScrollView extends HorizontalScrollView implements SwipeHelper.Callback { private static final String TAG = RecentsPanelView.TAG; private static final boolean DEBUG = RecentsPanelView.DEBUG; private LinearLayout mLinearLayout; - private ActivityDescriptionAdapter mAdapter; + private TaskDescriptionAdapter mAdapter; private RecentsCallback mCallback; protected int mLastScrollPosition; private SwipeHelper mSwipeHelper; @@ -304,7 +304,7 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView } } - public void setAdapter(ActivityDescriptionAdapter adapter) { + public void setAdapter(TaskDescriptionAdapter adapter) { mAdapter = adapter; mAdapter.registerDataSetObserver(new DataSetObserver() { public void onChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index e3f5cdb6d335..5b4c33e1890d 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -17,32 +17,20 @@ package com.android.systemui.recent; import java.util.ArrayList; -import java.util.List; import android.animation.Animator; import android.animation.LayoutTransition; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Process; -import android.os.SystemClock; import android.provider.Settings; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -72,66 +60,24 @@ public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener { static final String TAG = "RecentsPanelView"; static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; - private static final int DISPLAY_TASKS = 20; - private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps + private Context mContext; private StatusBar mBar; - private ArrayList<ActivityDescription> mActivityDescriptions; - private AsyncTask<Void, Integer, Void> mThumbnailLoader; - private int mIconDpi; private View mRecentsScrim; private View mRecentsGlowView; private View mRecentsNoApps; private ViewGroup mRecentsContainer; - private Bitmap mDefaultThumbnailBackground; private boolean mShowing; private Choreographer mChoreo; private View mRecentsDismissButton; - private ActivityDescriptionAdapter mListAdapter; - private final Handler mHandler = new Handler(); - - /* package */ final class ActivityDescription { - final ActivityManager.RecentTaskInfo recentTaskInfo; - final ResolveInfo resolveInfo; - final int taskId; // application task id for curating apps - final int persistentTaskId; // persistent id - final Intent intent; // launch intent for application - final String packageName; // used to override animations (see onClick()) - final int position; // position in list - - Matrix matrix; // arbitrary rotation matrix to correct orientation - - private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail() - private Drawable mIcon; // application package icon - private CharSequence mLabel; // application package label - - public ActivityDescription(ActivityManager.RecentTaskInfo _recentInfo, - ResolveInfo _resolveInfo, Intent _intent, - int _pos, String _packageName) { - recentTaskInfo = _recentInfo; - resolveInfo = _resolveInfo; - intent = _intent; - taskId = _recentInfo.id; - persistentTaskId = _recentInfo.persistentId; - position = _pos; - packageName = _packageName; - } - public CharSequence getLabel() { - return mLabel; - } + private RecentTasksLoader mRecentTasksLoader; + private ArrayList<TaskDescription> mRecentTaskDescriptions; + private TaskDescriptionAdapter mListAdapter; + private int mThumbnailWidth; - public Drawable getIcon() { - return mIcon; - } - - public void setThumbnail(Bitmap thumbnail) { - mThumbnail = thumbnail; - } - - public Bitmap getThumbnail() { - return mThumbnail; - } + public void setRecentTasksLoader(RecentTasksLoader loader) { + mRecentTasksLoader = loader; } private final class OnLongClickDelegate implements View.OnLongClickListener { @@ -150,18 +96,18 @@ public class RecentsPanelView extends RelativeLayout ImageView iconView; TextView labelView; TextView descriptionView; - ActivityDescription activityDescription; + TaskDescription taskDescription; } - /* package */ final class ActivityDescriptionAdapter extends BaseAdapter { + /* package */ final class TaskDescriptionAdapter extends BaseAdapter { private LayoutInflater mInflater; - public ActivityDescriptionAdapter(Context context) { + public TaskDescriptionAdapter(Context context) { mInflater = LayoutInflater.from(context); } public int getCount() { - return mActivityDescriptions != null ? mActivityDescriptions.size() : 0; + return mRecentTaskDescriptions != null ? mRecentTaskDescriptions.size() : 0; } public Object getItem(int position) { @@ -189,18 +135,15 @@ public class RecentsPanelView extends RelativeLayout holder = (ViewHolder) convertView.getTag(); } - // activityId is reverse since most recent appears at the bottom... - final int activityId = mActivityDescriptions.size() - position - 1; + // index is reverse since most recent appears at the bottom... + final int index = mRecentTaskDescriptions.size() - position - 1; - final ActivityDescription activityDescription = mActivityDescriptions.get(activityId); - holder.thumbnailViewImage.setImageBitmap(activityDescription.getThumbnail()); - holder.iconView.setImageDrawable(activityDescription.getIcon()); - holder.labelView.setText(activityDescription.getLabel()); - holder.descriptionView.setText(activityDescription.recentTaskInfo.description); - holder.thumbnailView.setTag(activityDescription); + final TaskDescription taskDescription = mRecentTaskDescriptions.get(index); + applyTaskDescription(holder, taskDescription, false); + + holder.thumbnailView.setTag(taskDescription); holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView)); - holder.thumbnailView.setContentDescription(activityDescription.getLabel()); - holder.activityDescription = activityDescription; + holder.taskDescription = taskDescription; return convertView; } @@ -226,14 +169,19 @@ public class RecentsPanelView extends RelativeLayout } public void show(boolean show, boolean animate) { + show(show, animate, null); + } + + public void show(boolean show, boolean animate, + ArrayList<TaskDescription> recentTaskDescriptions) { if (show) { // Need to update list of recent apps before we set visibility so this view's // content description is updated before it gets focus for TalkBack mode - refreshApplicationList(); + refreshRecentTasksList(recentTaskDescriptions); // if there are no apps, either bring up a "No recent apps" message, or just // quit early - boolean noApps = (mActivityDescriptions.size() == 0); + boolean noApps = (mRecentTaskDescriptions.size() == 0); if (mRecentsNoApps != null) { // doesn't exist on large devices mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE); } else { @@ -242,6 +190,8 @@ public class RecentsPanelView extends RelativeLayout return; } } + } else { + mRecentTasksLoader.cancelLoadingThumbnails(); } if (animate) { if (mShowing != show) { @@ -255,6 +205,7 @@ public class RecentsPanelView extends RelativeLayout mShowing = show; setVisibility(show ? View.VISIBLE : View.GONE); mChoreo.jumpTo(show); + onAnimationEnd(null); } if (show) { setFocusable(true); @@ -298,6 +249,9 @@ public class RecentsPanelView extends RelativeLayout createCustomAnimations(transitioner); } else { ((ViewGroup)mRecentsContainer).setLayoutTransition(null); + // Clear memory used by screenshots + mRecentTaskDescriptions.clear(); + mListAdapter.notifyDataSetInvalidated(); } } @@ -349,21 +303,13 @@ public class RecentsPanelView extends RelativeLayout public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mContext = context; + updateValuesFromResources(); + } - Resources res = context.getResources(); - boolean xlarge = (res.getConfiguration().screenLayout - & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; - - mIconDpi = xlarge ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi; - - int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width); - int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height); - int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background); - - // Render the default thumbnail background - mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(mDefaultThumbnailBackground); - c.drawColor(color); + public void updateValuesFromResources() { + mThumbnailWidth = + (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_thumbnail_width); } @Override @@ -371,7 +317,7 @@ public class RecentsPanelView extends RelativeLayout super.onFinishInflate(); mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container); - mListAdapter = new ActivityDescriptionAdapter(mContext); + mListAdapter = new TaskDescriptionAdapter(mContext); if (mRecentsContainer instanceof RecentsHorizontalScrollView){ RecentsHorizontalScrollView scrollView = (RecentsHorizontalScrollView) mRecentsContainer; @@ -427,122 +373,50 @@ public class RecentsPanelView extends RelativeLayout } } - Drawable getFullResDefaultActivityIcon() { - return getFullResIcon(Resources.getSystem(), - com.android.internal.R.mipmap.sym_def_app_icon); - } - Drawable getFullResIcon(Resources resources, int iconId) { - try { - return resources.getDrawableForDensity(iconId, mIconDpi); - } catch (Resources.NotFoundException e) { - return getFullResDefaultActivityIcon(); - } - } - - private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) { - Resources resources; - try { - resources = packageManager.getResourcesForApplication( - info.activityInfo.applicationInfo); - } catch (PackageManager.NameNotFoundException e) { - resources = null; - } - if (resources != null) { - int iconId = info.activityInfo.getIconResource(); - if (iconId != 0) { - return getFullResIcon(resources, iconId); + void applyTaskDescription(ViewHolder h, TaskDescription td, boolean anim) { + h.iconView.setImageDrawable(td.getIcon()); + if (h.iconView.getVisibility() != View.VISIBLE) { + if (anim) { + h.iconView.setAnimation(AnimationUtils.loadAnimation( + mContext, R.anim.recent_appear)); } - } - return getFullResDefaultActivityIcon(); - } - - private ArrayList<ActivityDescription> getRecentTasks() { - ArrayList<ActivityDescription> activityDescriptions = new ArrayList<ActivityDescription>(); - final PackageManager pm = mContext.getPackageManager(); - final ActivityManager am = (ActivityManager) - mContext.getSystemService(Context.ACTIVITY_SERVICE); - - final List<ActivityManager.RecentTaskInfo> recentTasks = - am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE); - - ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) - .resolveActivityInfo(pm, 0); - - int numTasks = recentTasks.size(); - - // skip the first activity - assume it's either the home screen or the current app. - final int first = 1; - for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) { - final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i); - - Intent intent = new Intent(recentInfo.baseIntent); - if (recentInfo.origActivity != null) { - intent.setComponent(recentInfo.origActivity); - } - - // Skip the current home activity. - if (homeInfo != null - && homeInfo.packageName.equals(intent.getComponent().getPackageName()) - && homeInfo.name.equals(intent.getComponent().getClassName())) { - continue; + h.iconView.setVisibility(View.VISIBLE); + } + h.labelView.setText(td.getLabel()); + h.thumbnailView.setContentDescription(td.getLabel()); + if (h.labelView.getVisibility() != View.VISIBLE) { + if (anim) { + h.labelView.setAnimation(AnimationUtils.loadAnimation( + mContext, R.anim.recent_appear)); } - - intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) - | Intent.FLAG_ACTIVITY_NEW_TASK); - final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); - if (resolveInfo != null) { - final ActivityInfo info = resolveInfo.activityInfo; - final String title = info.loadLabel(pm).toString(); - Drawable icon = getFullResIcon(resolveInfo, pm); - if (title != null && title.length() > 0 && icon != null) { - if (DEBUG) Log.v(TAG, "creating activity desc for id=" - + recentInfo.id + ", label=" + title); - ActivityDescription item = new ActivityDescription(recentInfo, - resolveInfo, intent, index, info.packageName); - activityDescriptions.add(item); - ++index; - } else { - if (DEBUG) Log.v(TAG, "SKIPPING item " + recentInfo.id); + h.labelView.setVisibility(View.VISIBLE); + } + Bitmap thumbnail = td.getThumbnail(); + if (thumbnail != null) { + // Should remove the default image in the frame + // that this now covers, to improve scrolling speed. + // That can't be done until the anim is complete though. + h.thumbnailViewImage.setImageBitmap(thumbnail); + // scale to fill up the full width + Matrix scaleMatrix = new Matrix(); + float scale = mThumbnailWidth / (float) thumbnail.getWidth(); + scaleMatrix.setScale(scale, scale); + h.thumbnailViewImage.setScaleType(ScaleType.MATRIX); + h.thumbnailViewImage.setImageMatrix(scaleMatrix); + if (h.thumbnailViewImage.getVisibility() != View.VISIBLE) { + if (anim) { + h.thumbnailViewImage.setAnimation( + AnimationUtils.loadAnimation( + mContext, R.anim.recent_appear)); } + h.thumbnailViewImage.setVisibility(View.VISIBLE); } } - return activityDescriptions; - } - - ActivityDescription findActivityDescription(int id) - { - ActivityDescription desc = null; - for (int i = 0; i < mActivityDescriptions.size(); i++) { - ActivityDescription item = mActivityDescriptions.get(i); - if (item != null && item.taskId == id) { - desc = item; - break; - } - } - return desc; - } - - void loadActivityDescription(ActivityDescription ad, int index) { - final ActivityManager am = (ActivityManager) - mContext.getSystemService(Context.ACTIVITY_SERVICE); - final PackageManager pm = mContext.getPackageManager(); - ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails( - ad.recentTaskInfo.persistentId); - CharSequence label = ad.resolveInfo.activityInfo.loadLabel(pm); - Drawable icon = getFullResIcon(ad.resolveInfo, pm); - if (DEBUG) Log.v(TAG, "Loaded bitmap for #" + index + " in " - + ad + ": " + thumbs.mainThumbnail); - synchronized (ad) { - ad.mLabel = label; - ad.mIcon = icon; - if (thumbs != null && thumbs.mainThumbnail != null) { - ad.setThumbnail(thumbs.mainThumbnail); - } - } + //h.descriptionView.setText(ad.description); } - void applyActivityDescription(ActivityDescription ad, int index, boolean anim) { + void onTaskThumbnailLoaded(TaskDescription ad) { synchronized (ad) { if (mRecentsContainer != null) { ViewGroup container = mRecentsContainer; @@ -556,44 +430,8 @@ public class RecentsPanelView extends RelativeLayout View v = container.getChildAt(i); if (v.getTag() instanceof ViewHolder) { ViewHolder h = (ViewHolder)v.getTag(); - if (h.activityDescription == ad) { - if (DEBUG) Log.v(TAG, "Updating thumbnail #" + index + " in " - + h.activityDescription - + ": " + ad.getThumbnail()); - h.iconView.setImageDrawable(ad.getIcon()); - if (anim) { - h.iconView.setAnimation(AnimationUtils.loadAnimation( - mContext, R.anim.recent_appear)); - } - h.iconView.setVisibility(View.VISIBLE); - h.labelView.setText(ad.getLabel()); - h.thumbnailView.setContentDescription(ad.getLabel()); - if (anim) { - h.labelView.setAnimation(AnimationUtils.loadAnimation( - mContext, R.anim.recent_appear)); - } - h.labelView.setVisibility(View.VISIBLE); - Bitmap thumbnail = ad.getThumbnail(); - if (thumbnail != null) { - // Should remove the default image in the frame - // that this now covers, to improve scrolling speed. - // That can't be done until the anim is complete though. - h.thumbnailViewImage.setImageBitmap(thumbnail); - - // scale to fill up the full width - Matrix scaleMatrix = new Matrix(); - float thumbnailViewWidth = h.thumbnailViewImage.getWidth(); - float scale = thumbnailViewWidth / thumbnail.getWidth(); - scaleMatrix.setScale(scale, scale); - h.thumbnailViewImage.setScaleType(ScaleType.MATRIX); - h.thumbnailViewImage.setImageMatrix(scaleMatrix); - - if (anim) { - h.thumbnailViewImage.setAnimation(AnimationUtils.loadAnimation( - mContext, R.anim.recent_appear)); - } - h.thumbnailViewImage.setVisibility(View.VISIBLE); - } + if (h.taskDescription == ad) { + applyTaskDescription(h, ad, true); } } } @@ -601,14 +439,28 @@ public class RecentsPanelView extends RelativeLayout } } - private void refreshApplicationList() { - if (mThumbnailLoader != null) { - mThumbnailLoader.cancel(false); - mThumbnailLoader = null; + private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) { + if (recentTasksList != null) { + mRecentTaskDescriptions = recentTasksList; + } else { + mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks(); } + mListAdapter.notifyDataSetInvalidated(); + updateUiElements(getResources().getConfiguration()); + } - mActivityDescriptions = getRecentTasks(); - int numRecentApps = mActivityDescriptions.size(); + public ArrayList<TaskDescription> getRecentTasksList() { + return mRecentTaskDescriptions; + } + + private void updateUiElements(Configuration config) { + final int items = mRecentTaskDescriptions.size(); + + mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE); + mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE); + + // Set description for accessibility + int numRecentApps = mRecentTaskDescriptions.size(); String recentAppsAccessibilityDescription; if (numRecentApps == 0) { recentAppsAccessibilityDescription = @@ -618,81 +470,10 @@ public class RecentsPanelView extends RelativeLayout R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps); } setContentDescription(recentAppsAccessibilityDescription); - for (ActivityDescription ad : mActivityDescriptions) { - ad.setThumbnail(mDefaultThumbnailBackground); - } - mListAdapter.notifyDataSetInvalidated(); - if (mActivityDescriptions.size() > 0) { - if (DEBUG) Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps"); - updateUiElements(getResources().getConfiguration()); - final ArrayList<ActivityDescription> descriptions = - new ArrayList<ActivityDescription>(mActivityDescriptions); - loadActivityDescription(descriptions.get(0), 0); - applyActivityDescription(descriptions.get(0), 0, false); - if (descriptions.size() > 1) { - mThumbnailLoader = new AsyncTask<Void, Integer, Void>() { - @Override - protected void onProgressUpdate(Integer... values) { - final ActivityDescription ad = descriptions.get(values[0]); - if (!isCancelled()) { - applyActivityDescription(ad, values[0], true); - } - // This is to prevent the loader thread from getting ahead - // of our UI updates. - mHandler.post(new Runnable() { - @Override public void run() { - synchronized (ad) { - ad.notifyAll(); - } - } - }); - } - - @Override - protected Void doInBackground(Void... params) { - final int origPri = Process.getThreadPriority(Process.myTid()); - Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE); - long nextTime = SystemClock.uptimeMillis(); - for (int i=1; i<descriptions.size(); i++) { - ActivityDescription ad = descriptions.get(i); - loadActivityDescription(ad, i); - long now = SystemClock.uptimeMillis(); - nextTime += 150; - if (nextTime > now) { - try { - Thread.sleep(nextTime-now); - } catch (InterruptedException e) { - } - } - if (isCancelled()) { - break; - } - synchronized (ad) { - publishProgress(i); - try { - ad.wait(500); - } catch (InterruptedException e) { - } - } - } - Process.setThreadPriority(origPri); - return null; - } - }; - mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - } - - private void updateUiElements(Configuration config) { - final int items = mActivityDescriptions.size(); - - mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE); - mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE); } public void handleOnClick(View view) { - ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription; + TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription; final Context context = view.getContext(); final ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); @@ -714,14 +495,14 @@ public class RecentsPanelView extends RelativeLayout } public void handleSwipe(View view) { - ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription; + TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription; if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel()); - mActivityDescriptions.remove(ad); + mRecentTaskDescriptions.remove(ad); // Handled by widget containers to enable LayoutTransitions properly // mListAdapter.notifyDataSetChanged(); - if (mActivityDescriptions.size() == 0) { + if (mRecentTaskDescriptions.size() == 0) { hide(false); } @@ -751,7 +532,7 @@ public class RecentsPanelView extends RelativeLayout } else if (item.getItemId() == R.id.recent_inspect_item) { ViewHolder viewHolder = (ViewHolder) selectedView.getTag(); if (viewHolder != null) { - final ActivityDescription ad = viewHolder.activityDescription; + final TaskDescription ad = viewHolder.taskDescription; startApplicationDetailsActivity(ad.packageName); mBar.animateCollapse(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 7853402490f9..dc13092c2c3d 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -31,13 +31,13 @@ import android.widget.ScrollView; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter; +import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter; public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback { private static final String TAG = RecentsPanelView.TAG; private static final boolean DEBUG = RecentsPanelView.DEBUG; private LinearLayout mLinearLayout; - private ActivityDescriptionAdapter mAdapter; + private TaskDescriptionAdapter mAdapter; private RecentsCallback mCallback; protected int mLastScrollPosition; private SwipeHelper mSwipeHelper; @@ -319,7 +319,7 @@ public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper } } - public void setAdapter(ActivityDescriptionAdapter adapter) { + public void setAdapter(TaskDescriptionAdapter adapter) { mAdapter = adapter; mAdapter.registerDataSetObserver(new DataSetObserver() { public void onChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java new file mode 100644 index 000000000000..dcfd6d8feb1c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recent; + +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; + +public final class TaskDescription { + final ResolveInfo resolveInfo; + final int taskId; // application task id for curating apps + final int persistentTaskId; // persistent id + final Intent intent; // launch intent for application + final String packageName; // used to override animations (see onClick()) + final CharSequence description; + + private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail() + private Drawable mIcon; // application package icon + private CharSequence mLabel; // application package label + + public TaskDescription(int _taskId, int _persistentTaskId, + ResolveInfo _resolveInfo, Intent _intent, + String _packageName, CharSequence _description) { + resolveInfo = _resolveInfo; + intent = _intent; + taskId = _taskId; + persistentTaskId = _persistentTaskId; + + description = _description; + packageName = _packageName; + } + + // mark all these as locked? + public CharSequence getLabel() { + return mLabel; + } + + public void setLabel(CharSequence label) { + mLabel = label; + } + + public Drawable getIcon() { + return mIcon; + } + + public void setIcon(Drawable icon) { + mIcon = icon; + } + + public void setThumbnail(Bitmap thumbnail) { + mThumbnail = thumbnail; + } + + public Bitmap getThumbnail() { + return mThumbnail; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 09ea6ad4055f..e54de59ced75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -74,7 +74,9 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; +import com.android.systemui.recent.RecentTasksLoader; import com.android.systemui.recent.RecentsPanelView; +import com.android.systemui.recent.TaskDescription; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBar; import com.android.systemui.statusbar.StatusBarIconView; @@ -193,6 +195,7 @@ public class PhoneStatusBar extends StatusBar { // Recent apps private RecentsPanelView mRecentsPanel; + private RecentTasksLoader mRecentTasksLoader; // Tracking finger for opening/closing. int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore @@ -363,6 +366,7 @@ public class PhoneStatusBar extends StatusBar { signalCluster.setNetworkController(mNetworkController); // Recents Panel + mRecentTasksLoader = new RecentTasksLoader(context); updateRecentsPanel(); // receive broadcasts @@ -399,16 +403,21 @@ public class PhoneStatusBar extends StatusBar { protected void updateRecentsPanel() { // Recents Panel boolean visible = false; + ArrayList<TaskDescription> recentTasksList = null; if (mRecentsPanel != null) { visible = mRecentsPanel.isShowing(); WindowManagerImpl.getDefault().removeView(mRecentsPanel); + if (visible) { + recentTasksList = mRecentsPanel.getRecentTasksList(); + } } // Provide RecentsPanelView with a temporary parent to allow layout params to work. LinearLayout tmpRoot = new LinearLayout(mContext); mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_recent_panel, tmpRoot, false); - + mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader); + mRecentTasksLoader.setRecentsPanel(mRecentsPanel); mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL, mRecentsPanel)); mRecentsPanel.setVisibility(View.GONE); @@ -417,7 +426,7 @@ public class PhoneStatusBar extends StatusBar { WindowManagerImpl.getDefault().addView(mRecentsPanel, lp); mRecentsPanel.setBar(this); if (visible) { - mRecentsPanel.show(true, false); + mRecentsPanel.show(true, false, recentTasksList); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index ba52fb8c8f65..271e508bd714 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -77,6 +77,7 @@ import com.android.systemui.statusbar.policy.CompatModeButton; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.Prefs; +import com.android.systemui.recent.RecentTasksLoader; import com.android.systemui.recent.RecentsPanelView; public class TabletStatusBar extends StatusBar implements @@ -179,6 +180,7 @@ public class TabletStatusBar extends StatusBar implements int mDisabled = 0; private RecentsPanelView mRecentsPanel; + private RecentTasksLoader mRecentTasksLoader; private InputMethodsPanel mInputMethodsPanel; private CompatModePanel mCompatModePanel; @@ -299,12 +301,15 @@ public class TabletStatusBar extends StatusBar implements } // Recents Panel + mRecentTasksLoader = new RecentTasksLoader(context); mRecentsPanel = (RecentsPanelView) View.inflate(context, R.layout.status_bar_recent_panel, null); mRecentsPanel.setVisibility(View.GONE); mRecentsPanel.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK); mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL, mRecentsPanel)); + mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader); + mRecentTasksLoader.setRecentsPanel(mRecentsPanel); mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel); lp = new WindowManager.LayoutParams( @@ -392,6 +397,7 @@ public class TabletStatusBar extends StatusBar implements mNotificationPanelParams.height = getNotificationPanelHeight(); WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel, mNotificationPanelParams); + mRecentsPanel.updateValuesFromResources(); } protected void loadDimens() { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 2cd6eab0690e..de8d41a2808b 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1084,8 +1084,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } MenuView menuView = st.isInListMode() - ? st.getListMenuView(mPanelMenuPresenterCallback) - : st.getIconMenuView(mPanelMenuPresenterCallback); + ? st.getListMenuView(getContext(), mPanelMenuPresenterCallback) + : st.getIconMenuView(getContext(), mPanelMenuPresenterCallback); st.shownPanelView = (View) menuView; @@ -3251,11 +3251,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - MenuView getListMenuView(MenuPresenter.Callback cb) { + MenuView getListMenuView(Context context, MenuPresenter.Callback cb) { if (menu == null) return null; if (!isCompact) { - getIconMenuView(cb); // Need this initialized to know where our offset goes + getIconMenuView(context, cb); // Need this initialized to know where our offset goes } if (listMenuPresenter == null) { @@ -3275,11 +3275,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return result; } - MenuView getIconMenuView(MenuPresenter.Callback cb) { + MenuView getIconMenuView(Context context, MenuPresenter.Callback cb) { if (menu == null) return null; if (iconMenuPresenter == null) { - iconMenuPresenter = new IconMenuPresenter(); + iconMenuPresenter = new IconMenuPresenter(context); iconMenuPresenter.setCallback(cb); iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter); menu.addMenuPresenter(iconMenuPresenter); diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java index 5875ee3c436c..3c6b4165821c 100644 --- a/services/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -457,6 +457,9 @@ public class TouchExplorer implements Explorer { case MotionEvent.ACTION_MOVE: { final int activePointerCount = mPointerTracker.getActivePointerCount(); switch (activePointerCount) { + case 1: { + // do nothing + } break; case 2: { if (isDraggingGesture(event)) { // If still dragging send a drag event. @@ -484,10 +487,12 @@ public class TouchExplorer implements Explorer { } } break; case MotionEvent.ACTION_POINTER_UP: { - mCurrentState = STATE_TOUCH_EXPLORING; // Send an event to the end of the drag gesture. sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } break; + case MotionEvent.ACTION_UP: { + mCurrentState = STATE_TOUCH_EXPLORING; + } break; case MotionEvent.ACTION_CANCEL: { clear(); } break; @@ -500,7 +505,7 @@ public class TouchExplorer implements Explorer { * @param event The event to be handled. * @param policyFlags The policy flags associated with the event. */ - public void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { + private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { throw new IllegalStateException("Delegating state can only be reached if " diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index e4c60284dc4f..a42a267a1fda 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -518,6 +518,7 @@ public abstract class SMSDispatcher extends Handler { * @param address the originating address * @param referenceNumber distinguishes concatenated messages from the same sender * @param sequenceNumber the order of this segment in the message + * (starting at 0 for CDMA WDP datagrams and 1 for concatenated messages). * @param messageCount the number of segments in the message * @param timestamp the service center timestamp in millis * @param destPort the destination port for the message, or -1 for no destination port @@ -583,7 +584,11 @@ public abstract class SMSDispatcher extends Handler { for (int i = 0; i < cursorCount; i++) { cursor.moveToNext(); int cursorSequence = cursor.getInt(SEQUENCE_COLUMN); - pdus[cursorSequence - 1] = HexDump.hexStringToByteArray( + // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0 + if (!isCdmaWapPush) { + cursorSequence--; + } + pdus[cursorSequence] = HexDump.hexStringToByteArray( cursor.getString(PDU_COLUMN)); // Read the destination port from the first segment (needed for CDMA WAP PDU). @@ -593,7 +598,12 @@ public abstract class SMSDispatcher extends Handler { } } // This one isn't in the DB, so add it - pdus[sequenceNumber - 1] = pdu; + // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0 + if (isCdmaWapPush) { + pdus[sequenceNumber] = pdu; + } else { + pdus[sequenceNumber - 1] = pdu; + } // Remove the parts from the database mResolver.delete(mRawUri, where, whereArgs); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index fe41e7e61b0c..ca8d9ae11617 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -198,13 +198,18 @@ final class CdmaSMSDispatcher extends SMSDispatcher { protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) { int index = 0; - int msgType = pdu[index++]; + int msgType = (0xFF & pdu[index++]); if (msgType != 0) { Log.w(TAG, "Received a WAP SMS which is not WDP. Discard."); return Intents.RESULT_SMS_HANDLED; } - int totalSegments = pdu[index++]; // >= 1 - int segment = pdu[index++]; // >= 0 + int totalSegments = (0xFF & pdu[index++]); // >= 1 + int segment = (0xFF & pdu[index++]); // >= 0 + + if (segment >= totalSegments) { + Log.e(TAG, "WDP bad segment #" + segment + " expecting 0-" + (totalSegments - 1)); + return Intents.RESULT_SMS_HANDLED; + } // Only the first segment contains sourcePort and destination Port int sourcePort = 0; diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index ea030e6bb047..2da9642d27e0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -300,8 +300,8 @@ public class SmsMessage extends SmsMessageBase { return null; } // TP-Data-Coding-Scheme - // Class 3, UCS-2 encoding, uncompressed - bo.write(0x0b); + // UCS-2 encoding, uncompressed + bo.write(0x08); } // (no TP-Validity-Period) |