diff options
72 files changed, 2154 insertions, 3530 deletions
diff --git a/api/14.txt b/api/14.txt index 5c4732e0e87f..050540bd4a82 100644 --- a/api/14.txt +++ b/api/14.txt @@ -22239,7 +22239,6 @@ package android.view.accessibility { } public class AccessibilityRecord { - ctor protected AccessibilityRecord(); method public int getAddedCount(); method public java.lang.CharSequence getBeforeText(); method public java.lang.CharSequence getClassName(); @@ -22269,17 +22268,6 @@ package android.view.accessibility { method public void setParcelableData(android.os.Parcelable); method public void setPassword(boolean); method public void setRemovedCount(int); - field protected int mAddedCount; - field protected java.lang.CharSequence mBeforeText; - field protected int mBooleanProperties; - field protected java.lang.CharSequence mClassName; - field protected java.lang.CharSequence mContentDescription; - field protected int mCurrentItemIndex; - field protected int mFromIndex; - field protected int mItemCount; - field protected android.os.Parcelable mParcelableData; - field protected int mRemovedCount; - field protected final java.util.List mText; } } diff --git a/api/current.txt b/api/current.txt index f5fb9d88be49..acea3e79e6e0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22723,13 +22723,11 @@ package android.view.accessibility { method public void appendRecord(android.view.accessibility.AccessibilityRecord); method public int describeContents(); method public static java.lang.String eventTypeToString(int); - method public int getAccessibilityWindowId(); method public long getEventTime(); method public int getEventType(); method public java.lang.CharSequence getPackageName(); method public android.view.accessibility.AccessibilityRecord getRecord(int); method public int getRecordCount(); - method public android.view.accessibility.AccessibilityNodeInfo getSource(); method public void initFromParcel(android.os.Parcel); method public static android.view.accessibility.AccessibilityEvent obtain(int); method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent); @@ -22737,7 +22735,6 @@ package android.view.accessibility { method public void setEventTime(long); method public void setEventType(int); method public void setPackageName(java.lang.CharSequence); - method public void setSource(android.view.View); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int INVALID_POSITION = -1; // 0xffffffff @@ -22753,6 +22750,7 @@ package android.view.accessibility { field public static final int TYPE_VIEW_LONG_CLICKED = 2; // 0x2 field public static final int TYPE_VIEW_SELECTED = 4; // 0x4 field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10 + field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800 field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20 } @@ -22780,9 +22778,10 @@ package android.view.accessibility { method public void addAction(int); method public void addChild(android.view.View); method public int describeContents(); - method public int getAccessibilityWindowId(); + method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String); method public int getActions(); - method public void getBounds(android.graphics.Rect); + method public void getBoundsInParent(android.graphics.Rect); + method public void getBoundsInScreen(android.graphics.Rect); method public android.view.accessibility.AccessibilityNodeInfo getChild(int); method public int getChildCount(); method public java.lang.CharSequence getClassName(); @@ -22790,6 +22789,7 @@ package android.view.accessibility { method public java.lang.CharSequence getPackageName(); method public android.view.accessibility.AccessibilityNodeInfo getParent(); method public java.lang.CharSequence getText(); + method public int getWindowId(); method public boolean isCheckable(); method public boolean isChecked(); method public boolean isClickable(); @@ -22803,7 +22803,8 @@ package android.view.accessibility { method public static android.view.accessibility.AccessibilityNodeInfo obtain(); method public boolean performAction(int); method public void recycle(); - method public void setBounds(android.graphics.Rect); + method public void setBoundsInParent(android.graphics.Rect); + method public void setBoundsInScreen(android.graphics.Rect); method public void setCheckable(boolean); method public void setChecked(boolean); method public void setClassName(java.lang.CharSequence); @@ -22828,7 +22829,6 @@ package android.view.accessibility { } public class AccessibilityRecord { - ctor protected AccessibilityRecord(); method public int getAddedCount(); method public java.lang.CharSequence getBeforeText(); method public java.lang.CharSequence getClassName(); @@ -22838,7 +22838,9 @@ package android.view.accessibility { method public int getItemCount(); method public android.os.Parcelable getParcelableData(); method public int getRemovedCount(); + method public android.view.accessibility.AccessibilityNodeInfo getSource(); method public java.util.List<java.lang.CharSequence> getText(); + method public int getWindowId(); method public boolean isChecked(); method public boolean isEnabled(); method public boolean isFullScreen(); @@ -22859,17 +22861,7 @@ package android.view.accessibility { method public void setParcelableData(android.os.Parcelable); method public void setPassword(boolean); method public void setRemovedCount(int); - field protected int mAddedCount; - field protected java.lang.CharSequence mBeforeText; - field protected int mBooleanProperties; - field protected java.lang.CharSequence mClassName; - field protected java.lang.CharSequence mContentDescription; - field protected int mCurrentItemIndex; - field protected int mFromIndex; - field protected int mItemCount; - field protected android.os.Parcelable mParcelableData; - field protected int mRemovedCount; - field protected final java.util.List mText; + method public void setSource(android.view.View); } } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 69c459705e2e..0acba8bc516b 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -313,9 +313,6 @@ bool BootAnimation::android() const GLint yc = (mHeight - mAndroid[0].h) / 2; const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); - // draw and update only what we need - mFlingerSurface->setSwapRectangle(updateRect); - glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), updateRect.height()); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 8bb305d4d55e..164acbcdf71f 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -190,9 +190,9 @@ public abstract class AccessibilityService extends Service { * <li> * Register for all event types with no notification timeout and keep track * for the active window by calling - * {@link AccessibilityEvent#getAccessibilityWindowId()} of the last received + * {@link AccessibilityEvent#getWindowId()} of the last received * event and compare this with the - * {@link AccessibilityNodeInfo#getAccessibilityWindowId()} before calling + * {@link AccessibilityNodeInfo#getWindowId()} before calling * retrieval methods on the latter. * </li> * <li> diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 19f0bf033a78..8b4e7aee7398 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -47,7 +47,9 @@ interface IAccessibilityServiceConnection { /** * Finds {@link AccessibilityNodeInfo}s by View text. The match is case - * insensitive containment. + * insensitive containment. The search is performed in the window whose + * id is specified and starts from the View whose accessibility id is + * specified. * <p> * <strong> * It is a client responsibility to recycle the received infos by @@ -57,12 +59,35 @@ interface IAccessibilityServiceConnection { * </p> * * @param text The searched text. + * @param accessibilityId The id of the view from which to start searching. + * Use {@link android.view.View#NO_ID} to start from the root. * @return A list of node info. */ - List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text); + List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text, + int accessibilityWindowId, int accessibilityViewId); /** - * Finds an {@link AccessibilityNodeInfo} by View id. + * Finds {@link AccessibilityNodeInfo}s by View text. The match is case + * insensitive containment. The search is performed in the currently + * active window and start from the root View in the window. + * <p> + * <strong> + * It is a client responsibility to recycle the received infos by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * + * @param text The searched text. + * @param accessibilityId The id of the view from which to start searching. + * Use {@link android.view.View#NO_ID} to start from the root. + * @return A list of node info. + */ + List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(String text); + + /** + * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed + * in the currently active window and start from the root View in the window. * <p> * <strong> * It is a client responsibility to recycle the received info by @@ -74,7 +99,7 @@ interface IAccessibilityServiceConnection { * @param id The id of the node. * @return The node info. */ - AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId); + AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId); /** * Performs an accessibility action on an {@link AccessibilityNodeInfo}. diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl index b66035ff881e..aa5937ee9deb 100644 --- a/core/java/android/nfc/INfcTag.aidl +++ b/core/java/android/nfc/INfcTag.aidl @@ -43,7 +43,6 @@ interface INfcTag int formatNdef(int nativeHandle, in byte[] key); Tag rediscover(int nativehandle); - void setIsoDepTimeout(int timeout); - void setFelicaTimeout(int timeout); + int setTimeout(int technology, int timeout); void resetTimeouts(); } diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java index 38b2bbd9e7ea..d02086ffcf1f 100644 --- a/core/java/android/nfc/tech/IsoDep.java +++ b/core/java/android/nfc/tech/IsoDep.java @@ -16,6 +16,7 @@ package android.nfc.tech; +import android.nfc.ErrorCodes; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; @@ -90,7 +91,10 @@ public final class IsoDep extends BasicTagTechnology { */ public void setTimeout(int timeout) { try { - mTag.getTagService().setIsoDepTimeout(timeout); + int err = mTag.getTagService().setTimeout(TagTechnology.ISO_DEP, timeout); + if (err != ErrorCodes.SUCCESS) { + throw new IllegalArgumentException("The supplied timeout is not valid"); + } } catch (RemoteException e) { Log.e(TAG, "NFC service dead", e); } diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java index c4b771878ce2..5cafe5ba6126 100644 --- a/core/java/android/nfc/tech/MifareClassic.java +++ b/core/java/android/nfc/tech/MifareClassic.java @@ -16,9 +16,11 @@ package android.nfc.tech; +import android.nfc.ErrorCodes; import android.nfc.Tag; import android.nfc.TagLostException; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; import java.nio.ByteBuffer; @@ -69,6 +71,8 @@ import java.nio.ByteOrder; * require the {@link android.Manifest.permission#NFC} permission. */ public final class MifareClassic extends BasicTagTechnology { + private static final String TAG = "NFC"; + /** * The default factory key. */ @@ -568,6 +572,31 @@ public final class MifareClassic extends BasicTagTechnology { return transceive(data, true); } + /** + * Set the timeout of {@link #transceive} in milliseconds. + * <p>The timeout only applies to MifareUltralight {@link #transceive}, + * and is reset to a default value when {@link #close} is called. + * <p>Setting a longer timeout may be useful when performing + * transactions that require a long processing time on the tag + * such as key generation. + * + * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. + * + * @param timeout timeout value in milliseconds + * @hide + */ + // TODO Unhide for ICS + public void setTimeout(int timeout) { + try { + int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout); + if (err != ErrorCodes.SUCCESS) { + throw new IllegalArgumentException("The supplied timeout is not valid"); + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + } + private static void validateSector(int sector) { // Do not be too strict on upper bounds checking, since some cards // have more addressable memory than they report. For example, diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java index 6c2754bcc05d..3d4cdd1a8dff 100644 --- a/core/java/android/nfc/tech/MifareUltralight.java +++ b/core/java/android/nfc/tech/MifareUltralight.java @@ -16,10 +16,12 @@ package android.nfc.tech; +import android.nfc.ErrorCodes; import android.nfc.Tag; import android.nfc.TagLostException; import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; @@ -57,6 +59,8 @@ import java.io.IOException; * require the {@link android.Manifest.permission#NFC} permission. */ public final class MifareUltralight extends BasicTagTechnology { + private static final String TAG = "NFC"; + /** A MIFARE Ultralight compatible tag of unknown type */ public static final int TYPE_UNKNOWN = -1; /** A MIFARE Ultralight tag */ @@ -208,6 +212,32 @@ public final class MifareUltralight extends BasicTagTechnology { return transceive(data, true); } + /** + * Set the timeout of {@link #transceive} in milliseconds. + * <p>The timeout only applies to MifareUltralight {@link #transceive}, + * and is reset to a default value when {@link #close} is called. + * <p>Setting a longer timeout may be useful when performing + * transactions that require a long processing time on the tag + * such as key generation. + * + * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. + * + * @param timeout timeout value in milliseconds + * @hide + */ + // TODO Unhide for ICS + public void setTimeout(int timeout) { + try { + int err = mTag.getTagService().setTimeout( + TagTechnology.MIFARE_ULTRALIGHT, timeout); + if (err != ErrorCodes.SUCCESS) { + throw new IllegalArgumentException("The supplied timeout is not valid"); + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + } + private static void validatePageIndex(int pageIndex) { // Do not be too strict on upper bounds checking, since some cards // may have more addressable memory than they report. diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java index 1843eaed24db..08095e6cb163 100644 --- a/core/java/android/nfc/tech/NfcA.java +++ b/core/java/android/nfc/tech/NfcA.java @@ -16,9 +16,11 @@ package android.nfc.tech; +import android.nfc.ErrorCodes; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; @@ -33,6 +35,8 @@ import java.io.IOException; * require the {@link android.Manifest.permission#NFC} permission. */ public final class NfcA extends BasicTagTechnology { + private static final String TAG = "NFC"; + /** @hide */ public static final String EXTRA_SAK = "sak"; /** @hide */ @@ -112,4 +116,29 @@ public final class NfcA extends BasicTagTechnology { public byte[] transceive(byte[] data) throws IOException { return transceive(data, true); } + + /** + * Set the timeout of {@link #transceive} in milliseconds. + * <p>The timeout only applies to NfcA {@link #transceive}, and is + * reset to a default value when {@link #close} is called. + * <p>Setting a longer timeout may be useful when performing + * transactions that require a long processing time on the tag + * such as key generation. + * + * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. + * + * @param timeout timeout value in milliseconds + * @hide + */ + // TODO Unhide for ICS + public void setTimeout(int timeout) { + try { + int err = mTag.getTagService().setTimeout(TagTechnology.NFC_A, timeout); + if (err != ErrorCodes.SUCCESS) { + throw new IllegalArgumentException("The supplied timeout is not valid"); + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + } } diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java index 250c9b31cf0d..85abf89e6dfd 100644 --- a/core/java/android/nfc/tech/NfcF.java +++ b/core/java/android/nfc/tech/NfcF.java @@ -16,6 +16,7 @@ package android.nfc.tech; +import android.nfc.ErrorCodes; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; @@ -131,7 +132,10 @@ public final class NfcF extends BasicTagTechnology { // TODO Unhide for ICS public void setTimeout(int timeout) { try { - mTag.getTagService().setFelicaTimeout(timeout); + int err = mTag.getTagService().setTimeout(TagTechnology.NFC_F, timeout); + if (err != ErrorCodes.SUCCESS) { + throw new IllegalArgumentException("The supplied timeout is not valid"); + } } catch (RemoteException e) { Log.e(TAG, "NFC service dead", e); } diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index bb6ed9c82b7c..3b2e6b644084 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -178,9 +178,8 @@ public final class Calendar { * have specific uses which are expected to be consistent by the app and * sync adapter. * - * @hide */ - public interface SyncColumns extends CalendarSyncColumns { + protected interface SyncColumns extends CalendarSyncColumns { /** * The account that was used to sync the entry to the device. If the * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and @@ -223,7 +222,7 @@ public final class Calendar { /** * If set to 1 this causes events on this calendar to be duplicated with - * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event + * {@link Events#LAST_SYNCED} set to 1 whenever the event * transitions from non-dirty to dirty. The duplicated event will not be * expanded in the instances table and will only show up in sync adapter * queries of the events table. It will also be deleted when the @@ -236,7 +235,7 @@ public final class Calendar { /** * Columns specific to the Calendars Uri that other Uris can query. */ - private interface CalendarsColumns { + protected interface CalendarsColumns { /** * The color of the calendar * <P>Type: INTEGER (color value)</P> @@ -549,7 +548,7 @@ public final class Calendar { /** * Columns from the Attendees table that other tables join into themselves. */ - private interface AttendeesColumns { + protected interface AttendeesColumns { /** * The id of the event. Column name. @@ -639,7 +638,7 @@ public final class Calendar { /** * Columns from the Events table that other tables join into themselves. */ - private interface EventsColumns { + protected interface EventsColumns { /** * The {@link Calendars#_ID} of the calendar the event belongs to. @@ -1525,7 +1524,7 @@ public final class Calendar { * time zone for the instaces. These settings are stored using a key/value * scheme. */ - private interface CalendarCacheColumns { + protected interface CalendarCacheColumns { /** * The key for the setting. Keys are defined in {@link CalendarCache}. */ @@ -1597,7 +1596,7 @@ public final class Calendar { * the Instances table and these are all stored in the first (and only) * row of the CalendarMetaData table. */ - private interface CalendarMetaDataColumns { + protected interface CalendarMetaDataColumns { /** * The local timezone that was used for precomputing the fields * in the Instances table. @@ -1637,7 +1636,7 @@ public final class Calendar { public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns { } - private interface EventDaysColumns { + protected interface EventDaysColumns { /** * The Julian starting day number. Column name. * <P>Type: INTEGER (int)</P> @@ -1655,7 +1654,7 @@ public final class Calendar { * Fields and helpers for querying for a list of days that contain events. */ public static final class EventDays implements EventDaysColumns { - private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/instances/groupbyday"); /** @@ -1690,7 +1689,7 @@ public final class Calendar { } } - private interface RemindersColumns { + protected interface RemindersColumns { /** * The event the reminder belongs to. Column name. * <P>Type: INTEGER (foreign key to the Events table)</P> @@ -1755,7 +1754,7 @@ public final class Calendar { } } - private interface CalendarAlertsColumns { + protected interface CalendarAlertsColumns { /** * The event that the alert belongs to. Column name. * <P>Type: INTEGER (foreign key to the Events table)</P> @@ -2069,7 +2068,7 @@ public final class Calendar { } } - private interface ExtendedPropertiesColumns { + protected interface ExtendedPropertiesColumns { /** * The event the extended property belongs to. Column name. * <P>Type: INTEGER (foreign key to the Events table)</P> @@ -2128,7 +2127,7 @@ public final class Calendar { /** * Columns from the EventsRawTimes table */ - private interface EventsRawTimesColumns { + protected interface EventsRawTimesColumns { /** * The corresponding event id. Column name. * <P>Type: INTEGER (long)</P> diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 622ab0fd62c1..071905c1299d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3704,7 +3704,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * The base implementation sets: * <ul> * <li>{@link AccessibilityNodeInfo#setParent(View)},</li> - * <li>{@link AccessibilityNodeInfo#setBounds(Rect)},</li> + * <li>{@link AccessibilityNodeInfo#setBoundsInParent(Rect)},</li> + * <li>{@link AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li> * <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li> * <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li> * <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li> @@ -3724,7 +3725,12 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { Rect bounds = mAttachInfo.mTmpInvalRect; getDrawingRect(bounds); - info.setBounds(bounds); + info.setBoundsInParent(bounds); + + int[] locationOnScreen = mAttachInfo.mInvalidateChildLocation; + getLocationOnScreen(locationOnScreen); + bounds.offset(locationOnScreen[0], locationOnScreen[1]); + info.setBoundsInScreen(bounds); ViewParent parent = getParent(); if (parent instanceof View) { diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 17d745485130..914973ed41b1 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -136,6 +136,13 @@ public final class ViewAncestor extends Handler implements ViewParent, static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList<ComponentCallbacks>(); + /** + * Delay before dispatching an accessibility event that the window + * content has changed. The window content is considered changed + * after a layout pass. + */ + private static final long SEND_WINDOW_CONTENT_CHANGED_DELAY_MILLIS = 500; + long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); @@ -277,6 +284,8 @@ public final class ViewAncestor extends Handler implements ViewParent, AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; + SendWindowContentChanged mSendWindowContentChanged; + private final int mDensity; /** @@ -1427,6 +1436,10 @@ public final class ViewAncestor extends Handler implements ViewParent, if (triggerGlobalLayoutListener) { attachInfo.mRecomputeGlobalAttributes = false; attachInfo.mTreeObserver.dispatchOnGlobalLayout(); + + if (AccessibilityManager.getInstance(host.mContext).isEnabled()) { + postSendWindowContentChangedCallback(); + } } if (computesInternalInsets) { @@ -2086,6 +2099,7 @@ public final class ViewAncestor extends Handler implements ViewParent, mAccessibilityInteractionConnectionManager.ensureNoConnection(); mAccessibilityManager.removeAccessibilityStateChangeListener( mAccessibilityInteractionConnectionManager); + removeSendWindowContentChangedCallback(); mView = null; mAttachInfo.mRootView = null; @@ -3671,6 +3685,29 @@ public final class ViewAncestor extends Handler implements ViewParent, } } + /** + * Post a callback to send a + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. + */ + private void postSendWindowContentChangedCallback() { + if (mSendWindowContentChanged == null) { + mSendWindowContentChanged = new SendWindowContentChanged(); + } else { + removeCallbacks(mSendWindowContentChanged); + } + postDelayed(mSendWindowContentChanged, SEND_WINDOW_CONTENT_CHANGED_DELAY_MILLIS); + } + + /** + * Remove a posted callback to send a + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. + */ + private void removeSendWindowContentChangedCallback() { + if (mSendWindowContentChanged != null) { + removeCallbacks(mSendWindowContentChanged); + } + } + public boolean showContextMenuForChild(View originalView) { return false; } @@ -4268,12 +4305,12 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - public void findAccessibilityNodeInfosByViewText(String text, int interactionId, - IAccessibilityInteractionConnectionCallback callback) { + public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId, + int interactionId, IAccessibilityInteractionConnectionCallback callback) { if (mViewAncestor.get() != null) { getAccessibilityInteractionController() - .findAccessibilityNodeInfosByViewTextClientThread(text, interactionId, - callback); + .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId, + interactionId, callback); } } } @@ -4414,13 +4451,15 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - public void findAccessibilityNodeInfosByViewTextClientThread(String text, int interactionId, + public void findAccessibilityNodeInfosByViewTextClientThread(String text, + int accessibilityViewId, int interactionId, IAccessibilityInteractionConnectionCallback callback) { Message message = Message.obtain(); message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT; SomeArgs args = mPool.acquire(); args.arg1 = text; - args.argi1 = interactionId; + args.argi1 = accessibilityViewId; + args.argi2 = interactionId; args.arg2 = callback; message.obj = args; sendMessage(message); @@ -4429,18 +4468,28 @@ public final class ViewAncestor extends Handler implements ViewParent, public void findAccessibilityNodeInfosByViewTextUiThread(Message message) { SomeArgs args = (SomeArgs) message.obj; final String text = (String) args.arg1; - final int interactionId = args.argi1; + final int accessibilityViewId = args.argi1; + final int interactionId = args.argi2; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg2; mPool.release(args); List<AccessibilityNodeInfo> infos = null; try { - View root = ViewAncestor.this.mView; - ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList; foundViews.clear(); + View root = null; + if (accessibilityViewId != View.NO_ID) { + root = findViewByAccessibilityId(accessibilityViewId); + } else { + root = ViewAncestor.this.mView; + } + + if (root == null) { + return; + } + root.findViewsWithText(foundViews, text); if (foundViews.isEmpty()) { return; @@ -4577,4 +4626,12 @@ public final class ViewAncestor extends Handler implements ViewParent, } } } + + private class SendWindowContentChanged implements Runnable { + public void run() { + if (mView != null) { + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + } + } + } } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 06e4827ae371..5ef7763bff73 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -19,11 +19,10 @@ package android.view.accessibility; import android.accessibilityservice.IAccessibilityServiceConnection; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import android.text.TextUtils; -import android.view.View; import java.util.ArrayList; +import java.util.List; /** * This class represents accessibility events that are sent by the system when @@ -130,7 +129,7 @@ import java.util.ArrayList; * <p> * <b>TRANSITION TYPES</b> <br> * <p> - * <b>Window state changed</b> - represents the event of opening/closing a + * <b>Window state changed</b> - represents the event of opening a * {@link android.widget.PopupWindow}, {@link android.view.Menu}, * {@link android.app.Dialog}, etc. <br> * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br> @@ -140,6 +139,16 @@ import java.util.ArrayList; * {@link #getEventTime()}, * {@link #getText()} * <p> + * <b>Window content changed</b> - represents the event of change in the + * content of a window. This change can be adding/removing view, changing + * a view size, etc.<br> + * Type: {@link #TYPE_WINDOW_CONTENT_CHANGED} <br> + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()} + * <p> * <b>NOTIFICATION TYPES</b> <br> * <p> * <b>Notification state changed</b> - represents the event showing/hiding @@ -244,6 +253,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400; /** + * Represents the event of changing the content of a window. + */ + public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800; + + /** * Mask for {@link AccessibilityEvent} all types. * * @see #TYPE_VIEW_CLICKED @@ -264,15 +278,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private boolean mIsInPool; private int mEventType; - private int mSourceAccessibilityViewId = View.NO_ID; - private int mSourceAccessibilityWindowId = View.NO_ID; private CharSequence mPackageName; private long mEventTime; private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); - private IAccessibilityServiceConnection mConnection; - /* * Hide constructor from clients. */ @@ -288,10 +298,43 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par super.init(event); mEventType = event.mEventType; mEventTime = event.mEventTime; - mSourceAccessibilityWindowId = event.mSourceAccessibilityWindowId; - mSourceAccessibilityViewId = event.mSourceAccessibilityViewId; mPackageName = event.mPackageName; - mConnection = event.mConnection; + } + + /** + * Sets the connection for interacting with the AccessibilityManagerService. + * + * @param connection The connection. + * + * @hide + */ + @Override + public void setConnection(IAccessibilityServiceConnection connection) { + super.setConnection(connection); + List<AccessibilityRecord> records = mRecords; + final int recordCount = records.size(); + for (int i = 0; i < recordCount; i++) { + AccessibilityRecord record = records.get(i); + record.setConnection(connection); + } + } + + /** + * Sets if this instance is sealed. + * + * @param sealed Whether is sealed. + * + * @hide + */ + @Override + public void setSealed(boolean sealed) { + super.setSealed(sealed); + List<AccessibilityRecord> records = mRecords; + final int recordCount = records.size(); + for (int i = 0; i < recordCount; i++) { + AccessibilityRecord record = records.get(i); + record.setSealed(sealed); + } } /** @@ -335,81 +378,6 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Sets the event source. - * - * @param source The source. - * - * @throws IllegalStateException If called from an AccessibilityService. - */ - public void setSource(View source) { - enforceNotSealed(); - if (source != null) { - mSourceAccessibilityWindowId = source.getAccessibilityWindowId(); - mSourceAccessibilityViewId = source.getAccessibilityViewId(); - } else { - mSourceAccessibilityWindowId = View.NO_ID; - mSourceAccessibilityViewId = View.NO_ID; - } - } - - /** - * Gets the {@link AccessibilityNodeInfo} of the event source. - * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> - * </p> - * @return The info. - */ - public AccessibilityNodeInfo getSource() { - enforceSealed(); - if (mSourceAccessibilityWindowId == View.NO_ID - || mSourceAccessibilityViewId == View.NO_ID) { - return null; - } - try { - return mConnection.findAccessibilityNodeInfoByAccessibilityId( - mSourceAccessibilityWindowId, mSourceAccessibilityViewId); - } catch (RemoteException e) { - return null; - } - } - - /** - * Gets the id of the window from which the event comes from. - * - * @return The window id. - */ - public int getAccessibilityWindowId() { - return mSourceAccessibilityWindowId; - } - - /** - * Sets the client token for the accessibility service that - * provided this node info. - * - * @param connection The connection. - * - * @hide - */ - public final void setConnection(IAccessibilityServiceConnection connection) { - mConnection = connection; - } - - /** - * Gets the accessibility window id of the source window. - * - * @return The id. - * - * @hide - */ - public int getSourceAccessibilityWindowId() { - return mSourceAccessibilityWindowId; - } - - /** * Sets the event type. * * @param eventType The event type. @@ -548,10 +516,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par @Override protected void clear() { super.clear(); - mConnection = null; mEventType = 0; - mSourceAccessibilityViewId = View.NO_ID; - mSourceAccessibilityWindowId = View.NO_ID; mPackageName = null; mEventTime = 0; while (!mRecords.isEmpty()) { @@ -572,8 +537,6 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } setSealed(parcel.readInt() == 1); mEventType = parcel.readInt(); - mSourceAccessibilityWindowId = parcel.readInt(); - mSourceAccessibilityViewId = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); readAccessibilityRecordFromParcel(this, parcel); @@ -583,6 +546,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par for (int i = 0; i < recordCount; i++) { AccessibilityRecord record = AccessibilityRecord.obtain(); readAccessibilityRecordFromParcel(record, parcel); + // Do this to write the connection only once. + record.setConnection(mConnection); mRecords.add(record); } } @@ -606,6 +571,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); record.mParcelableData = parcel.readParcelable(null); parcel.readList(record.mText, null); + record.mSourceWindowId = parcel.readInt(); + record.mSourceViewId = parcel.readInt(); + record.mSealed = (parcel.readInt() == 1); } /** @@ -620,8 +588,6 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); - parcel.writeInt(mSourceAccessibilityWindowId); - parcel.writeInt(mSourceAccessibilityViewId); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); writeAccessibilityRecordToParcel(this, parcel, flags); @@ -654,6 +620,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par TextUtils.writeToParcel(record.mBeforeText, parcel, flags); parcel.writeParcelable(record.mParcelableData, flags); parcel.writeList(record.mText); + parcel.writeInt(record.mSourceWindowId); + parcel.writeInt(record.mSourceViewId); + parcel.writeInt(record.mSealed ? 1 : 0); } /** @@ -672,8 +641,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append(super.toString()); if (DEBUG) { builder.append("\n"); - builder.append("; sourceAccessibilityWindowId: ").append(mSourceAccessibilityWindowId); - builder.append("; sourceAccessibilityViewId: ").append(mSourceAccessibilityViewId); + builder.append("; sourceWindowId: ").append(mSourceWindowId); + builder.append("; sourceViewId: ").append(mSourceViewId); for (int i = 0; i < mRecords.size(); i++) { AccessibilityRecord record = mRecords.get(i); builder.append(" Record "); @@ -733,6 +702,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par return "TYPE_TOUCH_EXPLORATION_GESTURE_START"; case TYPE_TOUCH_EXPLORATION_GESTURE_END: return "TYPE_TOUCH_EXPLORATION_GESTURE_END"; + case TYPE_WINDOW_CONTENT_CHANGED: + return "TYPE_WINDOW_CONTENT_CHANGED"; default: return null; } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 5fa65b450e42..18ef38af7476 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -26,6 +26,9 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.view.View; +import java.util.Collections; +import java.util.List; + /** * This class represents a node of the screen content. From the point of * view of an accessibility service the screen content is presented as tree @@ -97,7 +100,8 @@ public class AccessibilityNodeInfo implements Parcelable { private int mAccessibilityWindowId = View.NO_ID; private int mParentAccessibilityViewId = View.NO_ID; private int mBooleanProperties; - private final Rect mBounds = new Rect(); + private final Rect mBoundsInParent = new Rect(); + private final Rect mBoundsInScreen = new Rect(); private CharSequence mPackageName; private CharSequence mClassName; @@ -132,7 +136,7 @@ public class AccessibilityNodeInfo implements Parcelable { * * @return The window id. */ - public int getAccessibilityWindowId() { + public int getWindowId() { return mAccessibilityWindowId; } @@ -163,12 +167,16 @@ public class AccessibilityNodeInfo implements Parcelable { public AccessibilityNodeInfo getChild(int index) { enforceSealed(); final int childAccessibilityViewId = mChildAccessibilityIds.get(index); + if (!canPerformRequestOverConnection(childAccessibilityViewId)) { + return null; + } try { return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, childAccessibilityViewId); - } catch (RemoteException e) { - return null; + } catch (RemoteException re) { + /* ignore*/ } + return null; } /** @@ -230,12 +238,37 @@ public class AccessibilityNodeInfo implements Parcelable { */ public boolean performAction(int action) { enforceSealed(); + if (!canPerformRequestOverConnection(mAccessibilityViewId)) { + return false; + } try { return mConnection.performAccessibilityAction(mAccessibilityWindowId, mAccessibilityViewId, action); } catch (RemoteException e) { - return false; + /* ignore */ } + return false; + } + + /** + * Finds {@link AccessibilityNodeInfo}s by text. The match is case + * insensitive containment. + * + * @param text The searched text. + * @return A list of node info. + */ + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { + enforceSealed(); + if (!canPerformRequestOverConnection(mAccessibilityViewId)) { + return null; + } + try { + return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId, + mAccessibilityViewId); + } catch (RemoteException e) { + /* ignore */ + } + return Collections.emptyList(); } /** @@ -251,12 +284,16 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getParent() { enforceSealed(); + if (!canPerformRequestOverConnection(mAccessibilityViewId)) { + return null; + } try { - return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, - mParentAccessibilityViewId); + return mConnection.findAccessibilityNodeInfoByAccessibilityId( + mAccessibilityWindowId, mParentAccessibilityViewId); } catch (RemoteException e) { - return null; + /* ignore */ } + return null; } /** @@ -279,8 +316,9 @@ public class AccessibilityNodeInfo implements Parcelable { * * @param outBounds The output node bounds. */ - public void getBounds(Rect outBounds) { - outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom); + public void getBoundsInParent(Rect outBounds) { + outBounds.set(mBoundsInParent.left, mBoundsInParent.top, + mBoundsInParent.right, mBoundsInParent.bottom); } /** @@ -293,9 +331,34 @@ public class AccessibilityNodeInfo implements Parcelable { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setBounds(Rect bounds) { + public void setBoundsInParent(Rect bounds) { enforceNotSealed(); - mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom); + mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + /** + * Gets the node bounds in screen coordinates. + * + * @param outBounds The output node bounds. + */ + public void getBoundsInScreen(Rect outBounds) { + outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top, + mBoundsInScreen.right, mBoundsInScreen.bottom); + } + + /** + * Sets the node bounds in screen coordinates. + * <p> + * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * @param bounds The node bounds. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setBoundsInScreen(Rect bounds) { + enforceNotSealed(); + mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom); } /** @@ -636,6 +699,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @hide */ public final void setConnection(IAccessibilityServiceConnection connection) { + enforceNotSealed(); mConnection = connection; } @@ -777,10 +841,15 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeInt(childIds.valueAt(i)); } - parcel.writeInt(mBounds.top); - parcel.writeInt(mBounds.bottom); - parcel.writeInt(mBounds.left); - parcel.writeInt(mBounds.right); + parcel.writeInt(mBoundsInParent.top); + parcel.writeInt(mBoundsInParent.bottom); + parcel.writeInt(mBoundsInParent.left); + parcel.writeInt(mBoundsInParent.right); + + parcel.writeInt(mBoundsInScreen.top); + parcel.writeInt(mBoundsInScreen.bottom); + parcel.writeInt(mBoundsInScreen.left); + parcel.writeInt(mBoundsInScreen.right); parcel.writeInt(mActions); @@ -818,10 +887,15 @@ public class AccessibilityNodeInfo implements Parcelable { childIds.put(i, childId); } - mBounds.top = parcel.readInt(); - mBounds.bottom = parcel.readInt(); - mBounds.left = parcel.readInt(); - mBounds.right = parcel.readInt(); + mBoundsInParent.top = parcel.readInt(); + mBoundsInParent.bottom = parcel.readInt(); + mBoundsInParent.left = parcel.readInt(); + mBoundsInParent.right = parcel.readInt(); + + mBoundsInScreen.top = parcel.readInt(); + mBoundsInScreen.bottom = parcel.readInt(); + mBoundsInScreen.left = parcel.readInt(); + mBoundsInScreen.right = parcel.readInt(); mActions = parcel.readInt(); @@ -842,7 +916,8 @@ public class AccessibilityNodeInfo implements Parcelable { mAccessibilityViewId = View.NO_ID; mParentAccessibilityViewId = View.NO_ID; mChildAccessibilityIds.clear(); - mBounds.set(0, 0, 0, 0); + mBoundsInParent.set(0, 0, 0, 0); + mBoundsInScreen.set(0, 0, 0, 0); mBooleanProperties = 0; mPackageName = null; mClassName = null; @@ -869,6 +944,12 @@ public class AccessibilityNodeInfo implements Parcelable { return actionSymbolicNames.get(action); } + private boolean canPerformRequestOverConnection(int accessibilityViewId) { + return (mAccessibilityWindowId != View.NO_ID + && accessibilityViewId != View.NO_ID + && mConnection != null); + } + @Override public boolean equals(Object object) { if (this == object) { @@ -918,7 +999,8 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("]"); } - builder.append("; bounds: " + mBounds); + builder.append("; boundsInParent: " + mBoundsInParent); + builder.append("; boundsInScreen: " + mBoundsInScreen); builder.append("; packageName: ").append(mPackageName); builder.append("; className: ").append(mClassName); diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index 4bf03a7bed8e..9c495e2126cb 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -16,7 +16,10 @@ package android.view.accessibility; +import android.accessibilityservice.IAccessibilityServiceConnection; import android.os.Parcelable; +import android.os.RemoteException; +import android.view.View; import java.util.ArrayList; import java.util.List; @@ -45,26 +48,29 @@ public class AccessibilityRecord { private static int sPoolSize; private AccessibilityRecord mNext; private boolean mIsInPool; - private boolean mSealed; - protected int mBooleanProperties; - protected int mCurrentItemIndex; - protected int mItemCount; - protected int mFromIndex; - protected int mAddedCount; - protected int mRemovedCount; + boolean mSealed; + int mBooleanProperties; + int mCurrentItemIndex; + int mItemCount; + int mFromIndex; + int mAddedCount; + int mRemovedCount; + int mSourceViewId = View.NO_ID; + int mSourceWindowId = View.NO_ID; - protected CharSequence mClassName; - protected CharSequence mContentDescription; - protected CharSequence mBeforeText; - protected Parcelable mParcelableData; + CharSequence mClassName; + CharSequence mContentDescription; + CharSequence mBeforeText; + Parcelable mParcelableData; - protected final List<CharSequence> mText = new ArrayList<CharSequence>(); + final List<CharSequence> mText = new ArrayList<CharSequence>(); + IAccessibilityServiceConnection mConnection; /* * Hide constructor. */ - protected AccessibilityRecord() { + AccessibilityRecord() { } @@ -74,7 +80,7 @@ public class AccessibilityRecord { * @param record The to initialize from. */ void init(AccessibilityRecord record) { - mSealed = record.isSealed(); + mSealed = record.mSealed; mBooleanProperties = record.mBooleanProperties; mCurrentItemIndex = record.mCurrentItemIndex; mItemCount = record.mItemCount; @@ -86,6 +92,73 @@ public class AccessibilityRecord { mBeforeText = record.mBeforeText; mParcelableData = record.mParcelableData; mText.addAll(record.mText); + mSourceWindowId = record.mSourceWindowId; + mSourceViewId = record.mSourceViewId; + mConnection = record.mConnection; + } + + /** + * Sets the event source. + * + * @param source The source. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void setSource(View source) { + enforceNotSealed(); + if (source != null) { + mSourceWindowId = source.getAccessibilityWindowId(); + mSourceViewId = source.getAccessibilityViewId(); + } else { + mSourceWindowId = View.NO_ID; + mSourceViewId = View.NO_ID; + } + } + + /** + * Gets the {@link AccessibilityNodeInfo} of the event source. + * <p> + * <strong> + * It is a client responsibility to recycle the received info by + * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating + * of multiple instances. + * </strong> + * </p> + * @return The info. + */ + public AccessibilityNodeInfo getSource() { + enforceSealed(); + if (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) { + return null; + } + try { + return mConnection.findAccessibilityNodeInfoByAccessibilityId(mSourceWindowId, + mSourceViewId); + } catch (RemoteException e) { + /* ignore */ + } + return null; + } + + /** + * Sets the connection for interacting with the AccessibilityManagerService. + * + * @param connection The connection. + * + * @hide + */ + public void setConnection(IAccessibilityServiceConnection connection) { + enforceNotSealed(); + mConnection = connection; + } + + /** + * Gets the id of the window from which the event comes from. + * + * @return The window id. + */ + public int getWindowId() { + return mSourceWindowId; } /** @@ -386,10 +459,8 @@ public class AccessibilityRecord { * Gets if this instance is sealed. * * @return Whether is sealed. - * - * @hide */ - public boolean isSealed() { + boolean isSealed() { return mSealed; } @@ -397,10 +468,8 @@ public class AccessibilityRecord { * Enforces that this instance is sealed. * * @throws IllegalStateException If this instance is not sealed. - * - * @hide */ - protected void enforceSealed() { + void enforceSealed() { if (!isSealed()) { throw new IllegalStateException("Cannot perform this " + "action on a not sealed instance."); @@ -411,10 +480,8 @@ public class AccessibilityRecord { * Enforces that this instance is not sealed. * * @throws IllegalStateException If this instance is sealed. - * - * @hide */ - protected void enforceNotSealed() { + void enforceNotSealed() { if (isSealed()) { throw new IllegalStateException("Cannot perform this " + "action on an sealed instance."); @@ -502,10 +569,8 @@ public class AccessibilityRecord { /** * Clears the state of this instance. - * - * @hide */ - protected void clear() { + void clear() { mSealed = false; mBooleanProperties = 0; mCurrentItemIndex = INVALID_POSITION; @@ -518,6 +583,8 @@ public class AccessibilityRecord { mBeforeText = null; mParcelableData = null; mText.clear(); + mSourceViewId = View.NO_ID; + mSourceWindowId = View.NO_ID; } @Override diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 77dcd0747ed8..d35186b823ce 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -33,8 +33,8 @@ oneway interface IAccessibilityInteractionConnection { void findAccessibilityNodeInfoByViewId(int id, int interactionId, IAccessibilityInteractionConnectionCallback callback); - void findAccessibilityNodeInfosByViewText(String text, int interactionId, - IAccessibilityInteractionConnectionCallback callback); + void findAccessibilityNodeInfosByViewText(String text, int accessibilityViewId, + int interactionId, IAccessibilityInteractionConnectionCallback callback); void performAccessibilityAction(int accessibilityId, int action, int interactionId, IAccessibilityInteractionConnectionCallback callback); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 10c7390d0da2..bfab8a995bab 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1395,6 +1395,10 @@ public class WebView extends AbsoluteLayout */ public int getVisibleTitleHeight() { checkThread(); + return getVisibleTitleHeightImpl(); + } + + private int getVisibleTitleHeightImpl() { // need to restrict mScrollY due to over scroll return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0); } @@ -1405,7 +1409,7 @@ public class WebView extends AbsoluteLayout * Note: this can be called from WebCoreThread. */ /* package */ int getViewHeight() { - return getViewHeightWithTitle() - getVisibleTitleHeight(); + return getViewHeightWithTitle() - getVisibleTitleHeightImpl(); } int getViewHeightWithTitle() { @@ -2768,7 +2772,7 @@ public class WebView extends AbsoluteLayout // the visible height back in to account for the fact that if the title // bar is partially visible, the part of the visible rect which is // displaying our content is displaced by that amount. - r.top = viewToContentY(r.top + getVisibleTitleHeight()); + r.top = viewToContentY(r.top + getVisibleTitleHeightImpl()); r.right = viewToContentX(r.right); r.bottom = viewToContentY(r.bottom); } @@ -2785,7 +2789,7 @@ public class WebView extends AbsoluteLayout // the visible height back in to account for the fact that if the title // bar is partially visible, the part of the visible rect which is // displaying our content is displaced by that amount. - r.top = viewToContentYf(ri.top + getVisibleTitleHeight()); + r.top = viewToContentYf(ri.top + getVisibleTitleHeightImpl()); r.right = viewToContentXf(ri.right); r.bottom = viewToContentYf(ri.bottom); } @@ -2934,7 +2938,7 @@ public class WebView extends AbsoluteLayout if (mScrollY < 0) { t -= mScrollY; } - scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b); + scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b); scrollBar.draw(canvas); } @@ -5179,7 +5183,7 @@ public class WebView extends AbsoluteLayout Rect rect = nativeCursorNodeBounds(); mSelectX = contentToViewX(rect.left); mSelectY = contentToViewY(rect.top); - } else if (mLastTouchY > getVisibleTitleHeight()) { + } else if (mLastTouchY > getVisibleTitleHeightImpl()) { mSelectX = mScrollX + mLastTouchX; mSelectY = mScrollY + mLastTouchY; } else { @@ -5497,7 +5501,7 @@ public class WebView extends AbsoluteLayout int rootViewHeight = rootView.getHeight(); mViewRectViewport.set(mGLRectViewport); int savedWebViewBottom = mGLRectViewport.bottom; - mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeight(); + mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeightImpl(); mGLRectViewport.top = rootViewHeight - savedWebViewBottom; mGLViewportEmpty = false; } else { @@ -5554,7 +5558,7 @@ public class WebView extends AbsoluteLayout if (!mInOverScrollMode) { sendOurVisibleRect(); // update WebKit if visible title bar height changed. The logic is same - // as getVisibleTitleHeight. + // as getVisibleTitleHeightImpl. int titleHeight = getTitleHeight(); if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) { sendViewSizeZoom(false); @@ -8324,7 +8328,7 @@ public class WebView extends AbsoluteLayout (Math.min(maxHeight, y + viewHeight) - viewHeight)); // We need to take into account the visible title height // when scrolling since y is an absolute view position. - y = Math.max(0, y - getVisibleTitleHeight()); + y = Math.max(0, y - getVisibleTitleHeightImpl()); scrollTo(x, y); } break; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 82dd5dbba4b2..3fe81498aad9 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -55,7 +55,6 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewTreeObserver; -import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index c4d05e91662b..755d4e09c039 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -904,8 +904,10 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { // Add a record for ourselves as well. AccessibilityEvent record = AccessibilityEvent.obtain(); + record.setSource(this); // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent record.setClassName(getClass().getName()); + child.onInitializeAccessibilityEvent(record); child.dispatchPopulateAccessibilityEvent(record); event.appendRecord(record); return true; diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 5c6a26f3fb10..b7565f3129d5 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -387,7 +387,7 @@ public class Switch extends CompoundButton { case MotionEvent.ACTION_DOWN: { final float x = ev.getX(); final float y = ev.getY(); - if (hitThumb(x, y)) { + if (isEnabled() && hitThumb(x, y)) { mTouchMode = TOUCH_MODE_DOWN; mTouchX = x; mTouchY = y; @@ -460,7 +460,8 @@ public class Switch extends CompoundButton { */ private void stopDrag(MotionEvent ev) { mTouchMode = TOUCH_MODE_IDLE; - boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP; + // Up and not canceled, also checks the switch has not been disabled during the drag + boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled(); cancelSuperTouch(ev); diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 18d6caa710a8..860a08c3e538 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -54,6 +54,8 @@ public class WrapperInit { */ public static void main(String[] args) { try { + // Tell the Zygote what our actual PID is (since it only knows about the + // wrapper that it directly forked). int fdNum = Integer.parseInt(args[0], 10); if (fdNum != 0) { try { @@ -67,6 +69,10 @@ public class WrapperInit { } } + // Mimic Zygote preloading. + ZygoteInit.preload(); + + // Launch the application. String[] runtimeArgs = new String[args.length - 1]; System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length); RuntimeInit.wrapperInit(runtimeArgs); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 157c0bf0ec13..b4a7e52c6bb3 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -228,6 +228,11 @@ public class ZygoteInit { } } + static void preload() { + preloadClasses(); + preloadResources(); + } + /** * Performs Zygote process initialization. Loads and initializes * commonly used classes. @@ -509,8 +514,7 @@ public class ZygoteInit { registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); - preloadClasses(); - preloadResources(); + preload(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java index 32606168c61e..99d534c7950d 100644 --- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java @@ -29,6 +29,7 @@ import android.os.SystemClock; import android.provider.Settings; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -49,6 +50,9 @@ import java.util.Queue; */ public class InterrogationActivityTest extends ActivityInstrumentationTestCase2<InterrogationActivity> { + private static final boolean DEBUG = true; + + private static String LOG_TAG = "InterrogationActivityTest"; // Timeout before give up wait for the system to process an accessibility setting change. private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000; @@ -62,7 +66,7 @@ public class InterrogationActivityTest private static IAccessibilityServiceConnection sConnection; // The last received accessibility event - private static volatile AccessibilityEvent sLastAccessibilityEvent; + private static volatile AccessibilityEvent sLastFocusAccessibilityEvent; public InterrogationActivityTest() { super(InterrogationActivity.class); @@ -72,18 +76,19 @@ public class InterrogationActivityTest @LargeTest public void testFindAccessibilityNodeInfoByViewId() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); - AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( - R.id.button5); + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertNotNull(button); assertEquals(0, button.getChildCount()); // bounds Rect bounds = new Rect(); - button.getBounds(bounds); + button.getBoundsInParent(bounds); assertEquals(0, bounds.left); assertEquals(0, bounds.top); assertEquals(73, bounds.right); @@ -111,28 +116,40 @@ public class InterrogationActivityTest button.getActions()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewId: " + + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testFindAccessibilityNodeInfoByViewText() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); // find a view by text List<AccessibilityNodeInfo> buttons = - getConnection().findAccessibilityNodeInfosByViewText("butto"); + getConnection().findAccessibilityNodeInfosByViewTextInActiveWindow("butto"); assertEquals(9, buttons.size()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewText: " + + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testTraverseAllViews() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); @@ -153,8 +170,8 @@ public class InterrogationActivityTest classNameAndTextList.add("android.widget.ButtonButton8"); classNameAndTextList.add("android.widget.ButtonButton9"); - AccessibilityNodeInfo root = getConnection().findAccessibilityNodeInfoByViewId( - R.id.root); + AccessibilityNodeInfo root = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root); assertNotNull("We must find the existing root.", root); Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>(); @@ -181,125 +198,152 @@ public class InterrogationActivityTest } } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testTraverseAllViews: " + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testPerformAccessibilityActionFocus() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); // find a view and make sure it is not focused - AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( - R.id.button5); + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isFocused()); // focus the view assertTrue(button.performAction(ACTION_FOCUS)); // find the view again and make sure it is focused - button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertTrue(button.isFocused()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testPerformAccessibilityActionFocus: " + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testPerformAccessibilityActionClearFocus() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); // find a view and make sure it is not focused - AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( - R.id.button5); + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isFocused()); // focus the view assertTrue(button.performAction(ACTION_FOCUS)); // find the view again and make sure it is focused - button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertTrue(button.isFocused()); // unfocus the view assertTrue(button.performAction(ACTION_CLEAR_FOCUS)); // find the view again and make sure it is not focused - button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isFocused()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testPerformAccessibilityActionClearFocus: " + + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testPerformAccessibilityActionSelect() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); // find a view and make sure it is not selected - AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( - R.id.button5); + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isSelected()); // select the view assertTrue(button.performAction(ACTION_SELECT)); // find the view again and make sure it is selected - button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertTrue(button.isSelected()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testPerformAccessibilityActionSelect: " + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testPerformAccessibilityActionClearSelection() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); // find a view and make sure it is not selected - AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( - R.id.button5); + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isSelected()); // select the view assertTrue(button.performAction(ACTION_SELECT)); // find the view again and make sure it is selected - button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertTrue(button.isSelected()); // unselect the view assertTrue(button.performAction(ACTION_CLEAR_SELECTION)); // find the view again and make sure it is not selected - button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5); + button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isSelected()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testPerformAccessibilityActionClearSelection: " + + elapsedTimeMillis + "ms"); + } } } @LargeTest public void testAccessibilityEventGetSource() throws Exception { beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); try { // bring up the activity getActivity(); // find a view and make sure it is not focused - AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId( - R.id.button5); + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); assertFalse(button.isSelected()); // focus the view @@ -314,14 +358,14 @@ public class InterrogationActivityTest } // check that last event source - AccessibilityNodeInfo source = sLastAccessibilityEvent.getSource(); + AccessibilityNodeInfo source = sLastFocusAccessibilityEvent.getSource(); assertNotNull(source); // bounds Rect buttonBounds = new Rect(); - button.getBounds(buttonBounds); + button.getBoundsInParent(buttonBounds); Rect sourceBounds = new Rect(); - source.getBounds(sourceBounds); + source.getBoundsInParent(sourceBounds); assertEquals(buttonBounds.left, sourceBounds.left); assertEquals(buttonBounds.right, sourceBounds.right); @@ -346,6 +390,42 @@ public class InterrogationActivityTest assertSame(button.isChecked(), source.isChecked()); } finally { afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testAccessibilityEventGetSource: " + elapsedTimeMillis + "ms"); + } + } + } + + @LargeTest + public void testObjectContract() throws Exception { + beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); + try { + // bring up the activity + getActivity(); + + // find a view and make sure it is not focused + AccessibilityNodeInfo button = + getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5); + AccessibilityNodeInfo parent = button.getParent(); + final int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + AccessibilityNodeInfo child = parent.getChild(i); + assertNotNull(child); + if (child.equals(button)) { + assertEquals("Equal objects must have same hasCode.", button.hashCode(), + child.hashCode()); + return; + } + } + fail("Parent's children do not have the info whose parent is the parent."); + } finally { + afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testObjectContract: " + elapsedTimeMillis + "ms"); + } } } @@ -442,7 +522,9 @@ public class InterrogationActivityTest public void onInterrupt() {} public void onAccessibilityEvent(AccessibilityEvent event) { - sLastAccessibilityEvent = AccessibilityEvent.obtain(event); + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + sLastFocusAccessibilityEvent = AccessibilityEvent.obtain(event); + } synchronized (sConnection) { sConnection.notifyAll(); } diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index 717f8370cb98..0da03d1343c7 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -20,355 +20,12 @@ #include <stdint.h> #include <sys/types.h> -#include <cutils/compiler.h> - #include <utils/Debug.h> -#include <utils/threads.h> -#include <utils/String8.h> - -#include <ui/Rect.h> namespace android { // --------------------------------------------------------------------------- -/* - * These classes manage a stack of buffers in shared memory. - * - * SharedClient: represents a client with several stacks - * SharedBufferStack: represents a stack of buffers - * SharedBufferClient: manipulates the SharedBufferStack from the client side - * SharedBufferServer: manipulates the SharedBufferStack from the server side - * - * Buffers can be dequeued until there are none available, they can be locked - * unless they are in use by the server, which is only the case for the last - * dequeue-able buffer. When these various conditions are not met, the caller - * waits until the condition is met. - * - */ - -// ---------------------------------------------------------------------------- - -class Region; -class SharedBufferStack; -class SharedClient; - -// ---------------------------------------------------------------------------- - -class SharedBufferStack -{ - friend class SharedClient; - friend class SharedBufferBase; - friend class SharedBufferClient; - friend class SharedBufferServer; - -public: - // When changing these values, the COMPILE_TIME_ASSERT at the end of this - // file need to be updated. - static const unsigned int NUM_LAYERS_MAX = 31; - static const unsigned int NUM_BUFFER_MAX = 32; - static const unsigned int NUM_BUFFER_MIN = 2; - static const unsigned int NUM_DISPLAY_MAX = 4; - - struct Statistics { // 4 longs - typedef int32_t usecs_t; - usecs_t totalTime; - usecs_t reserved[3]; - }; - - struct SmallRect { - uint16_t l, t, r, b; - }; - - struct FlatRegion { // 52 bytes = 4 * (1 + 2*N) - static const unsigned int NUM_RECT_MAX = 5; - uint32_t count; - SmallRect rects[NUM_RECT_MAX]; - }; - - struct BufferData { - FlatRegion dirtyRegion; - SmallRect crop; - uint8_t transform; - uint8_t reserved[3]; - }; - - SharedBufferStack(); - void init(int32_t identity); - status_t setDirtyRegion(int buffer, const Region& reg); - status_t setCrop(int buffer, const Rect& reg); - status_t setTransform(int buffer, uint8_t transform); - Region getDirtyRegion(int buffer) const; - Rect getCrop(int buffer) const; - uint32_t getTransform(int buffer) const; - - // these attributes are part of the conditions/updates - volatile int32_t head; // server's current front buffer - volatile int32_t available; // number of dequeue-able buffers - volatile int32_t queued; // number of buffers waiting for post - volatile int32_t reserved1; - volatile status_t status; // surface's status code - - // not part of the conditions - volatile int32_t reallocMask; - volatile int8_t index[NUM_BUFFER_MAX]; - - int32_t identity; // surface's identity (const) - int32_t token; // surface's token (for debugging) - Statistics stats; - int8_t headBuf; // last retired buffer - uint8_t reservedBytes[3]; - int32_t reserved; - BufferData buffers[NUM_BUFFER_MAX]; // 1024 bytes -}; - -// ---------------------------------------------------------------------------- - -// 64 KB max -class SharedClient -{ -public: - SharedClient(); - ~SharedClient(); - status_t validate(size_t token) const; - -private: - friend class SharedBufferBase; - friend class SharedBufferClient; - friend class SharedBufferServer; - - // FIXME: this should be replaced by a lock-less primitive - Mutex lock; - Condition cv; - SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ]; -}; - -// ============================================================================ - -class SharedBufferBase -{ -public: - SharedBufferBase(SharedClient* sharedClient, int surface, - int32_t identity); - ~SharedBufferBase(); - status_t getStatus() const; - int32_t getIdentity() const; - String8 dump(char const* prefix) const; - -protected: - SharedClient* const mSharedClient; - SharedBufferStack* const mSharedStack; - const int mIdentity; - - friend struct Update; - friend struct QueueUpdate; - - struct ConditionBase { - SharedBufferStack& stack; - inline ConditionBase(SharedBufferBase* sbc) - : stack(*sbc->mSharedStack) { } - virtual ~ConditionBase() { }; - virtual bool operator()() const = 0; - virtual const char* name() const = 0; - }; - status_t waitForCondition(const ConditionBase& condition); - - struct UpdateBase { - SharedBufferStack& stack; - inline UpdateBase(SharedBufferBase* sbb) - : stack(*sbb->mSharedStack) { } - }; - template <typename T> - status_t updateCondition(T update); -}; - -template <typename T> -status_t SharedBufferBase::updateCondition(T update) { - SharedClient& client( *mSharedClient ); - Mutex::Autolock _l(client.lock); - ssize_t result = update(); - client.cv.broadcast(); - return result; -} - -// ---------------------------------------------------------------------------- - -class SharedBufferClient : public SharedBufferBase -{ -public: - SharedBufferClient(SharedClient* sharedClient, int surface, int num, - int32_t identity); - - ssize_t dequeue(); - status_t undoDequeue(int buf); - - status_t lock(int buf); - status_t cancel(int buf); - status_t queue(int buf); - bool needNewBuffer(int buffer) const; - status_t setDirtyRegion(int buffer, const Region& reg); - status_t setCrop(int buffer, const Rect& reg); - status_t setTransform(int buffer, uint32_t transform); - - class SetBufferCountCallback { - friend class SharedBufferClient; - virtual status_t operator()(int bufferCount) const = 0; - protected: - virtual ~SetBufferCountCallback() { } - }; - status_t setBufferCount(int bufferCount, const SetBufferCountCallback& ipc); - -private: - friend struct Condition; - friend struct DequeueCondition; - friend struct LockCondition; - - struct QueueUpdate : public UpdateBase { - inline QueueUpdate(SharedBufferBase* sbb); - inline ssize_t operator()(); - }; - - struct DequeueUpdate : public UpdateBase { - inline DequeueUpdate(SharedBufferBase* sbb); - inline ssize_t operator()(); - }; - - struct CancelUpdate : public UpdateBase { - int tail, buf; - inline CancelUpdate(SharedBufferBase* sbb, int tail, int buf); - inline ssize_t operator()(); - }; - - // -- - - struct DequeueCondition : public ConditionBase { - inline DequeueCondition(SharedBufferClient* sbc); - inline bool operator()() const; - inline const char* name() const { return "DequeueCondition"; } - }; - - struct LockCondition : public ConditionBase { - int buf; - inline LockCondition(SharedBufferClient* sbc, int buf); - inline bool operator()() const; - inline const char* name() const { return "LockCondition"; } - }; - - int32_t computeTail() const; - - mutable RWLock mLock; - int mNumBuffers; - - int32_t tail; - int32_t queued_head; - // statistics... - nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX]; -}; - -// ---------------------------------------------------------------------------- - -class SharedBufferServer - : public SharedBufferBase, - public LightRefBase<SharedBufferServer> -{ -public: - SharedBufferServer(SharedClient* sharedClient, int surface, int num, - int32_t identity); - - ssize_t retireAndLock(); - void setStatus(status_t status); - status_t reallocateAll(); - status_t reallocateAllExcept(int buffer); - int32_t getQueuedCount() const; - Region getDirtyRegion(int buffer) const; - Rect getCrop(int buffer) const; - uint32_t getTransform(int buffer) const; - - status_t resize(int newNumBuffers); - status_t grow(int newNumBuffers); - status_t shrink(int newNumBuffers); - - SharedBufferStack::Statistics getStats() const; - - -private: - friend class LightRefBase<SharedBufferServer>; - ~SharedBufferServer(); - - /* - * BufferList is basically a fixed-capacity sorted-vector of - * unsigned 5-bits ints using a 32-bits int as storage. - * it has efficient iterators to find items in the list and not in the list. - */ - class BufferList { - size_t mCapacity; - uint32_t mList; - public: - BufferList(size_t c = SharedBufferStack::NUM_BUFFER_MAX) - : mCapacity(c), mList(0) { } - status_t add(int value); - status_t remove(int value); - uint32_t getMask() const { return mList; } - - class const_iterator { - friend class BufferList; - uint32_t mask, curr; - const_iterator(uint32_t mask) : - mask(mask), curr(__builtin_clz(mask)) { - } - public: - inline bool operator == (const const_iterator& rhs) const { - return mask == rhs.mask; - } - inline bool operator != (const const_iterator& rhs) const { - return mask != rhs.mask; - } - inline int operator *() const { return curr; } - inline const const_iterator& operator ++() { - mask &= ~(1<<(31-curr)); - curr = __builtin_clz(mask); - return *this; - } - }; - - inline const_iterator begin() const { - return const_iterator(mList); - } - inline const_iterator end() const { - return const_iterator(0); - } - inline const_iterator free_begin() const { - uint32_t mask = (1 << (32-mCapacity)) - 1; - return const_iterator( ~(mList | mask) ); - } - }; - - // this protects mNumBuffers and mBufferList - mutable RWLock mLock; - int mNumBuffers; - BufferList mBufferList; - - struct BuffersAvailableCondition : public ConditionBase { - int mNumBuffers; - inline BuffersAvailableCondition(SharedBufferServer* sbs, - int numBuffers); - inline bool operator()() const; - inline const char* name() const { return "BuffersAvailableCondition"; } - }; - - struct RetireUpdate : public UpdateBase { - const int numBuffers; - inline RetireUpdate(SharedBufferBase* sbb, int numBuffers); - inline ssize_t operator()(); - }; - - struct StatusUpdate : public UpdateBase { - const status_t status; - inline StatusUpdate(SharedBufferBase* sbb, status_t status); - inline ssize_t operator()(); - }; -}; - -// =========================================================================== +#define NUM_DISPLAY_MAX 4 struct display_cblk_t { @@ -389,12 +46,11 @@ struct surface_flinger_cblk_t // 4KB max uint8_t connected; uint8_t reserved[3]; uint32_t pad[7]; - display_cblk_t displays[SharedBufferStack::NUM_DISPLAY_MAX]; + display_cblk_t displays[NUM_DISPLAY_MAX]; }; // --------------------------------------------------------------------------- -COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 65536) COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096) // --------------------------------------------------------------------------- diff --git a/include/surfaceflinger/IGraphicBufferAlloc.h b/include/surfaceflinger/IGraphicBufferAlloc.h index 01e4bd9ff052..e1b6b577b58f 100644 --- a/include/surfaceflinger/IGraphicBufferAlloc.h +++ b/include/surfaceflinger/IGraphicBufferAlloc.h @@ -27,6 +27,8 @@ namespace android { // ---------------------------------------------------------------------------- +class GraphicBuffer; + class IGraphicBufferAlloc : public IInterface { public: diff --git a/include/surfaceflinger/ISurface.h b/include/surfaceflinger/ISurface.h index cd0ee400e120..5fdf234e5fac 100644 --- a/include/surfaceflinger/ISurface.h +++ b/include/surfaceflinger/ISurface.h @@ -27,42 +27,23 @@ #include <ui/PixelFormat.h> -#include <hardware/hardware.h> -#include <hardware/gralloc.h> - namespace android { typedef int32_t SurfaceID; -class GraphicBuffer; +class ISurfaceTexture; class ISurface : public IInterface { protected: enum { - RESERVED0 = IBinder::FIRST_CALL_TRANSACTION, - RESERVED1, - RESERVED2, - REQUEST_BUFFER, - SET_BUFFER_COUNT, + GET_SURFACE_TEXTURE = IBinder::FIRST_CALL_TRANSACTION, }; public: DECLARE_META_INTERFACE(Surface); - /* - * requests a new buffer for the given index. If w, h, or format are - * null the buffer is created with the parameters assigned to the - * surface it is bound to. Otherwise the buffer's parameters are - * set to those specified. - */ - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0; - - /* - * sets the number of buffers dequeuable for this surface. - */ - virtual status_t setBufferCount(int bufferCount) = 0; + virtual sp<ISurfaceTexture> getSurfaceTexture() const = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index dea1b101e6ac..03fd01b31850 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -33,6 +33,8 @@ namespace android { // ---------------------------------------------------------------------------- +class IMemoryHeap; + class ISurfaceComposer : public IInterface { public: @@ -95,10 +97,6 @@ public: */ virtual sp<ISurfaceComposerClient> createConnection() = 0; - /* create a client connection with surface flinger - */ - virtual sp<ISurfaceComposerClient> createClientConnection() = 0; - /* create a graphic buffer allocator */ virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() = 0; @@ -134,11 +132,6 @@ public: virtual status_t turnElectronBeamOff(int32_t mode) = 0; virtual status_t turnElectronBeamOn(int32_t mode) = 0; - /* Signal surfaceflinger that there might be some work to do - * This is an ASYNCHRONOUS call. - */ - virtual void signal() const = 0; - /* verify that an ISurface was created by SurfaceFlinger. */ virtual bool authenticateSurface(const sp<ISurface>& surface) const = 0; @@ -154,7 +147,6 @@ public: // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, - CREATE_CLIENT_CONNECTION, CREATE_GRAPHIC_BUFFER_ALLOC, GET_CBLK, OPEN_GLOBAL_TRANSACTION, @@ -162,7 +154,6 @@ public: SET_ORIENTATION, FREEZE_DISPLAY, UNFREEZE_DISPLAY, - SIGNAL, CAPTURE_SCREEN, TURN_ELECTRON_BEAM_OFF, TURN_ELECTRON_BEAM_ON, diff --git a/include/surfaceflinger/ISurfaceComposerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h index 46b1bb7e9fe1..2e75a0e4cfd9 100644 --- a/include/surfaceflinger/ISurfaceComposerClient.h +++ b/include/surfaceflinger/ISurfaceComposerClient.h @@ -33,9 +33,6 @@ namespace android { // ---------------------------------------------------------------------------- -class IMemoryHeap; - -typedef int32_t ClientID; typedef int32_t DisplayID; // ---------------------------------------------------------------------------- @@ -57,9 +54,6 @@ public: status_t writeToParcel(Parcel* parcel) const; }; - virtual sp<IMemoryHeap> getControlBlock() const = 0; - virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const = 0; - /* * Requires ACCESS_SURFACE_FLINGER permission */ diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index ab30f45dd541..8845dc9e1cb0 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -43,9 +43,7 @@ class IOMX; class Rect; class Surface; class SurfaceComposerClient; -class SharedClient; -class SharedBufferClient; -class SurfaceClient; +class SurfaceTextureClient; // --------------------------------------------------------------------------- @@ -162,9 +160,6 @@ public: status_t lock(SurfaceInfo* info, Region* dirty, bool blocking = true); status_t unlockAndPost(); - // setSwapRectangle() is intended to be used by GL ES clients - void setSwapRectangle(const Rect& r); - sp<IBinder> asBinder() const; private: @@ -209,6 +204,7 @@ private: static int query(const ANativeWindow* window, int what, int* value); static int perform(ANativeWindow* window, int operation, ...); + int setSwapInterval(int interval); int dequeueBuffer(ANativeWindowBuffer** buffer); int lockBuffer(ANativeWindowBuffer* buffer); int queueBuffer(ANativeWindowBuffer* buffer); @@ -216,83 +212,23 @@ private: int query(int what, int* value) const; int perform(int operation, va_list args); - void dispatch_setUsage(va_list args); - int dispatch_connect(va_list args); - int dispatch_disconnect(va_list args); - int dispatch_crop(va_list args); - int dispatch_set_buffer_count(va_list args); - int dispatch_set_buffers_geometry(va_list args); - int dispatch_set_buffers_transform(va_list args); - int dispatch_set_buffers_timestamp(va_list args); - - void setUsage(uint32_t reqUsage); - int connect(int api); - int disconnect(int api); - int crop(Rect const* rect); - int setBufferCount(int bufferCount); - int setBuffersGeometry(int w, int h, int format); - int setBuffersTransform(int transform); - int setBuffersTimestamp(int64_t timestamp); - /* * private stuff... */ void init(); status_t validate(bool inCancelBuffer = false) const; - // When the buffer pool is a fixed size we want to make sure SurfaceFlinger - // won't stall clients, so we require an extra buffer. - enum { MIN_UNDEQUEUED_BUFFERS = 2 }; - - inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } - inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } - - status_t getBufferLocked(int index, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage); - int getBufferIndex(const sp<GraphicBuffer>& buffer) const; - int getConnectedApi() const; - bool needNewBuffer(int bufIdx, - uint32_t *pWidth, uint32_t *pHeight, - uint32_t *pFormat, uint32_t *pUsage) const; - static void cleanCachedSurfacesLocked(); - class BufferInfo { - uint32_t mWidth; - uint32_t mHeight; - uint32_t mFormat; - uint32_t mUsage; - mutable uint32_t mDirty; - enum { - GEOMETRY = 0x01 - }; - public: - BufferInfo(); - void set(uint32_t w, uint32_t h, uint32_t format); - void set(uint32_t usage); - void get(uint32_t *pWidth, uint32_t *pHeight, - uint32_t *pFormat, uint32_t *pUsage) const; - bool validateBuffer(const sp<GraphicBuffer>& buffer) const; - }; - // constants - GraphicBufferMapper& mBufferMapper; - SurfaceClient& mClient; - SharedBufferClient* mSharedBufferClient; status_t mInitCheck; sp<ISurface> mSurface; + sp<SurfaceTextureClient> mSurfaceTextureClient; uint32_t mIdentity; PixelFormat mFormat; uint32_t mFlags; - - // protected by mSurfaceLock - Rect mSwapRectangle; - int mConnected; - Rect mNextBufferCrop; - uint32_t mNextBufferTransform; - BufferInfo mBufferInfo; // protected by mSurfaceLock. These are also used from lock/unlock // but in that case, they must be called form the same thread. @@ -304,9 +240,6 @@ private: mutable Region mOldDirtyRegion; bool mReserved; - // only used from dequeueBuffer() - Vector< sp<GraphicBuffer> > mBuffers; - // query() must be called from dequeueBuffer() thread uint32_t mWidth; uint32_t mHeight; diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index c61a5bff2a12..140b9f860bfd 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -36,10 +36,10 @@ namespace android { // --------------------------------------------------------------------------- -class Region; -class SharedClient; -class ISurfaceComposer; class DisplayInfo; +class IMemoryHeap; +class ISurfaceComposer; +class Region; class surface_flinger_cblk_t; // --------------------------------------------------------------------------- diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h new file mode 100644 index 000000000000..8f76d72c18a3 --- /dev/null +++ b/include/utils/BlobCache.h @@ -0,0 +1,181 @@ +/* + ** Copyright 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. + */ + +#ifndef ANDROID_BLOB_CACHE_H +#define ANDROID_BLOB_CACHE_H + +#include <stddef.h> + +#include <utils/RefBase.h> +#include <utils/SortedVector.h> +#include <utils/threads.h> + +namespace android { + +// A BlobCache is an in-memory cache for binary key/value pairs. All the public +// methods are thread-safe. +// +// The cache contents can be serialized to a file and reloaded in a subsequent +// execution of the program. This serialization is non-portable and should only +// be loaded by the device that generated it. +class BlobCache : public RefBase { +public: + + // Create an empty blob cache. The blob cache will cache key/value pairs + // with key and value sizes less than or equal to maxKeySize and + // maxValueSize, respectively. The total combined size of ALL cache entries + // (key sizes plus value sizes) will not exceed maxTotalSize. + BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize); + + // set inserts a new binary value into the cache and associates it with the + // given binary key. If the key or value are too large for the cache then + // the cache remains unchanged. This includes the case where a different + // value was previously associated with the given key - the old value will + // remain in the cache. If the given key and value are small enough to be + // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize + // values specified to the BlobCache constructor), then the key/value pair + // will be in the cache after set returns. Note, however, that a subsequent + // call to set may evict old key/value pairs from the cache. + // + // Preconditions: + // key != NULL + // 0 < keySize + // value != NULL + // 0 < valueSize + void set(const void* key, size_t keySize, const void* value, + size_t valueSize); + + // The get function retrieves from the cache the binary value associated + // with a given binary key. If the key is present in the cache then the + // length of the binary value associated with that key is returned. If the + // value argument is non-NULL and the size of the cached value is less than + // valueSize bytes then the cached value is copied into the buffer pointed + // to by the value argument. If the key is not present in the cache then 0 + // is returned and the buffer pointed to by the value argument is not + // modified. + // + // Note that when calling get multiple times with the same key, the later + // calls may fail, returning 0, even if earlier calls succeeded. The return + // value must be checked for each call. + // + // Preconditions: + // key != NULL + // 0 < keySize + // 0 <= valueSize + size_t get(const void* key, size_t keySize, void* value, size_t valueSize); + +private: + // Copying is disallowed. + BlobCache(const BlobCache&); + void operator=(const BlobCache&); + + // clean evicts a randomly chosen set of entries from the cache such that + // the total size of all remaining entries is less than mMaxTotalSize/2. + void clean(); + + // isCleanable returns true if the cache is full enough for the clean method + // to have some effect, and false otherwise. + bool isCleanable() const; + + // A Blob is an immutable sized unstructured data blob. + class Blob : public RefBase { + public: + Blob(const void* data, size_t size, bool copyData); + ~Blob(); + + bool operator<(const Blob& rhs) const; + + const void* getData() const; + size_t getSize() const; + + private: + // Copying is not allowed. + Blob(const Blob&); + void operator=(const Blob&); + + // mData points to the buffer containing the blob data. + const void* mData; + + // mSize is the size of the blob data in bytes. + size_t mSize; + + // mOwnsData indicates whether or not this Blob object should free the + // memory pointed to by mData when the Blob gets destructed. + bool mOwnsData; + }; + + // A CacheEntry is a single key/value pair in the cache. + class CacheEntry { + public: + CacheEntry(); + CacheEntry(const sp<Blob>& key, const sp<Blob>& value); + CacheEntry(const CacheEntry& ce); + + bool operator<(const CacheEntry& rhs) const; + const CacheEntry& operator=(const CacheEntry&); + + sp<Blob> getKey() const; + sp<Blob> getValue() const; + + void setValue(const sp<Blob>& value); + + private: + + // mKey is the key that identifies the cache entry. + sp<Blob> mKey; + + // mValue is the cached data associated with the key. + sp<Blob> mValue; + }; + + // mMaxKeySize is the maximum key size that will be cached. Calls to + // BlobCache::set with a keySize parameter larger than mMaxKeySize will + // simply not add the key/value pair to the cache. + const size_t mMaxKeySize; + + // mMaxValueSize is the maximum value size that will be cached. Calls to + // BlobCache::set with a valueSize parameter larger than mMaxValueSize will + // simply not add the key/value pair to the cache. + const size_t mMaxValueSize; + + // mMaxTotalSize is the maximum size that all cache entries can occupy. This + // includes space for both keys and values. When a call to BlobCache::set + // would otherwise cause this limit to be exceeded, either the key/value + // pair passed to BlobCache::set will not be cached or other cache entries + // will be evicted from the cache to make room for the new entry. + const size_t mMaxTotalSize; + + // mTotalSize is the total combined size of all keys and values currently in + // the cache. + size_t mTotalSize; + + // mRandState is the pseudo-random number generator state. It is passed to + // nrand48 to generate random numbers when needed. It must be protected by + // mMutex. + unsigned short mRandState[3]; + + // mCacheEntries stores all the cache entries that are resident in memory. + // Cache entries are added to it by the 'set' method. + SortedVector<CacheEntry> mCacheEntries; + + // mMutex is used to synchronize access to all member variables. It must be + // locked any time the member variables are written or read. + Mutex mMutex; +}; + +} + +#endif // ANDROID_BLOB_CACHE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index 412622596c85..f8c3216b4618 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -116,16 +116,22 @@ public: typedef RefBase basetype; -protected: - RefBase(); - virtual ~RefBase(); + // used to override the RefBase destruction. + class Destroyer { + friend class RefBase; + public: + virtual ~Destroyer(); + private: + virtual void destroy(RefBase const* base) = 0; + }; - // called when the last reference goes away. this is responsible for - // calling the destructor. The default implementation just does - // "delete this;". // Make sure to never acquire a strong reference from this function. The // same restrictions than for destructors apply. - virtual void destroy() const; + void setDestroyer(Destroyer* destroyer); + +protected: + RefBase(); + virtual ~RefBase(); //! Flags for extendObjectLifetime() enum { diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index b5737ffff18f..4070eba736ae 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -16,7 +16,6 @@ LOCAL_SRC_FILES:= \ ISurfaceComposerClient.cpp \ IGraphicBufferAlloc.cpp \ LayerState.cpp \ - SharedBufferStack.cpp \ Surface.cpp \ SurfaceComposerClient.cpp \ diff --git a/libs/gui/ISurface.cpp b/libs/gui/ISurface.cpp index 23b90af31143..96155d7458b1 100644 --- a/libs/gui/ISurface.cpp +++ b/libs/gui/ISurface.cpp @@ -22,9 +22,7 @@ #include <binder/Parcel.h> -#include <ui/GraphicBuffer.h> - -#include <surfaceflinger/Surface.h> +#include <gui/ISurfaceTexture.h> #include <surfaceflinger/ISurface.h> namespace android { @@ -39,30 +37,11 @@ public: { } - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) - { + virtual sp<ISurfaceTexture> getSurfaceTexture() const { Parcel data, reply; data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(bufferIdx); - data.writeInt32(w); - data.writeInt32(h); - data.writeInt32(format); - data.writeInt32(usage); - remote()->transact(REQUEST_BUFFER, data, &reply); - sp<GraphicBuffer> buffer = new GraphicBuffer(); - reply.read(*buffer); - return buffer; - } - - virtual status_t setBufferCount(int bufferCount) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(bufferCount); - remote()->transact(SET_BUFFER_COUNT, data, &reply); - status_t err = reply.readInt32(); - return err; + remote()->transact(GET_SURFACE_TEXTURE, data, &reply); + return interface_cast<ISurfaceTexture>(reply.readStrongBinder()); } }; @@ -74,23 +53,9 @@ status_t BnSurface::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { - case REQUEST_BUFFER: { - CHECK_INTERFACE(ISurface, data, reply); - int bufferIdx = data.readInt32(); - uint32_t w = data.readInt32(); - uint32_t h = data.readInt32(); - uint32_t format = data.readInt32(); - uint32_t usage = data.readInt32(); - sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage)); - if (buffer == NULL) - return BAD_VALUE; - return reply->write(*buffer); - } - case SET_BUFFER_COUNT: { + case GET_SURFACE_TEXTURE: { CHECK_INTERFACE(ISurface, data, reply); - int bufferCount = data.readInt32(); - status_t err = setBufferCount(bufferCount); - reply->writeInt32(err); + reply->writeStrongBinder( getSurfaceTexture()->asBinder() ); return NO_ERROR; } default: diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 8951c3f0453a..40450a3d0750 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -57,15 +57,6 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } - virtual sp<ISurfaceComposerClient> createClientConnection() - { - uint32_t n; - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply); - return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); - } - virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() { uint32_t n; @@ -174,13 +165,6 @@ public: return reply.readInt32(); } - virtual void signal() const - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); - } - virtual bool authenticateSurface(const sp<ISurface>& surface) const { Parcel data, reply; @@ -229,11 +213,6 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); } break; - case CREATE_CLIENT_CONNECTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> b = createClientConnection()->asBinder(); - reply->writeStrongBinder(b); - } break; case CREATE_GRAPHIC_BUFFER_ALLOC: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); @@ -270,10 +249,6 @@ status_t BnSurfaceComposer::onTransact( CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; - case SIGNAL: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - signal(); - } break; case GET_CBLK: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = getCblk()->asBinder(); diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index ea38e08a555f..8d8339258860 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -50,9 +50,7 @@ namespace android { enum { - GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, - GET_TOKEN, - CREATE_SURFACE, + CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, DESTROY_SURFACE, SET_STATE }; @@ -65,23 +63,6 @@ public: { } - virtual sp<IMemoryHeap> getControlBlock() const - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - remote()->transact(GET_CBLK, data, &reply); - return interface_cast<IMemoryHeap>(reply.readStrongBinder()); - } - - virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeStrongBinder(sur->asBinder()); - remote()->transact(GET_TOKEN, data, &reply); - return reply.readInt32(); - } - virtual sp<ISurface> createSurface( surface_data_t* params, const String8& name, DisplayID display, @@ -131,41 +112,6 @@ IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClie status_t BnSurfaceComposerClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - // codes that don't require permission check - - switch(code) { - case GET_CBLK: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<IMemoryHeap> ctl(getControlBlock()); - reply->writeStrongBinder(ctl->asBinder()); - return NO_ERROR; - } break; - case GET_TOKEN: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder()); - ssize_t token = getTokenForSurface(sur); - reply->writeInt32(token); - return NO_ERROR; - } break; - } - - // these must be checked - - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.ACCESS_SURFACE_FLINGER"))) - { - LOGE("Permission Denial: " - "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - } - switch(code) { case CREATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposerClient, data, reply); diff --git a/libs/gui/SharedBufferStack.cpp b/libs/gui/SharedBufferStack.cpp deleted file mode 100644 index 7505d530e0c5..000000000000 --- a/libs/gui/SharedBufferStack.cpp +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "SharedBufferStack" - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Debug.h> -#include <utils/Log.h> -#include <utils/threads.h> - -#include <private/surfaceflinger/SharedBufferStack.h> - -#include <ui/Rect.h> -#include <ui/Region.h> - -#define DEBUG_ATOMICS 0 - -namespace android { -// ---------------------------------------------------------------------------- - -SharedClient::SharedClient() - : lock(Mutex::SHARED), cv(Condition::SHARED) -{ -} - -SharedClient::~SharedClient() { -} - - -// these functions are used by the clients -status_t SharedClient::validate(size_t i) const { - if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX)) - return BAD_INDEX; - return surfaces[i].status; -} - -// ---------------------------------------------------------------------------- - - -SharedBufferStack::SharedBufferStack() -{ -} - -void SharedBufferStack::init(int32_t i) -{ - status = NO_ERROR; - identity = i; -} - -status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return BAD_INDEX; - - buffers[buffer].crop.l = uint16_t(crop.left); - buffers[buffer].crop.t = uint16_t(crop.top); - buffers[buffer].crop.r = uint16_t(crop.right); - buffers[buffer].crop.b = uint16_t(crop.bottom); - return NO_ERROR; -} - -status_t SharedBufferStack::setTransform(int buffer, uint8_t transform) -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return BAD_INDEX; - buffers[buffer].transform = transform; - return NO_ERROR; -} - -status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return BAD_INDEX; - - FlatRegion& reg(buffers[buffer].dirtyRegion); - if (dirty.isEmpty()) { - reg.count = 0; - return NO_ERROR; - } - - size_t count; - Rect const* r = dirty.getArray(&count); - if (count > FlatRegion::NUM_RECT_MAX) { - const Rect bounds(dirty.getBounds()); - reg.count = 1; - reg.rects[0].l = uint16_t(bounds.left); - reg.rects[0].t = uint16_t(bounds.top); - reg.rects[0].r = uint16_t(bounds.right); - reg.rects[0].b = uint16_t(bounds.bottom); - } else { - reg.count = count; - for (size_t i=0 ; i<count ; i++) { - reg.rects[i].l = uint16_t(r[i].left); - reg.rects[i].t = uint16_t(r[i].top); - reg.rects[i].r = uint16_t(r[i].right); - reg.rects[i].b = uint16_t(r[i].bottom); - } - } - return NO_ERROR; -} - -Region SharedBufferStack::getDirtyRegion(int buffer) const -{ - Region res; - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return res; - - const FlatRegion& reg(buffers[buffer].dirtyRegion); - if (reg.count > FlatRegion::NUM_RECT_MAX) - return res; - - if (reg.count == 1) { - const Rect r( - reg.rects[0].l, - reg.rects[0].t, - reg.rects[0].r, - reg.rects[0].b); - res.set(r); - } else { - for (size_t i=0 ; i<reg.count ; i++) { - const Rect r( - reg.rects[i].l, - reg.rects[i].t, - reg.rects[i].r, - reg.rects[i].b); - res.orSelf(r); - } - } - return res; -} - -Rect SharedBufferStack::getCrop(int buffer) const -{ - Rect res(-1, -1); - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return res; - res.left = buffers[buffer].crop.l; - res.top = buffers[buffer].crop.t; - res.right = buffers[buffer].crop.r; - res.bottom = buffers[buffer].crop.b; - return res; -} - -uint32_t SharedBufferStack::getTransform(int buffer) const -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return 0; - return buffers[buffer].transform; -} - - -// ---------------------------------------------------------------------------- - -SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, - int surface, int32_t identity) - : mSharedClient(sharedClient), - mSharedStack(sharedClient->surfaces + surface), - mIdentity(identity) -{ -} - -SharedBufferBase::~SharedBufferBase() -{ -} - -status_t SharedBufferBase::getStatus() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.status; -} - -int32_t SharedBufferBase::getIdentity() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.identity; -} - -String8 SharedBufferBase::dump(char const* prefix) const -{ - const size_t SIZE = 1024; - char buffer[SIZE]; - String8 result; - SharedBufferStack& stack( *mSharedStack ); - snprintf(buffer, SIZE, - "%s[ head=%2d, available=%2d, queued=%2d ] " - "reallocMask=%08x, identity=%d, status=%d", - prefix, stack.head, stack.available, stack.queued, - stack.reallocMask, stack.identity, stack.status); - result.append(buffer); - result.append("\n"); - return result; -} - -status_t SharedBufferBase::waitForCondition(const ConditionBase& condition) -{ - const SharedBufferStack& stack( *mSharedStack ); - SharedClient& client( *mSharedClient ); - const nsecs_t TIMEOUT = s2ns(1); - const int identity = mIdentity; - - Mutex::Autolock _l(client.lock); - while ((condition()==false) && - (stack.identity == identity) && - (stack.status == NO_ERROR)) - { - status_t err = client.cv.waitRelative(client.lock, TIMEOUT); - // handle errors and timeouts - if (CC_UNLIKELY(err != NO_ERROR)) { - if (err == TIMED_OUT) { - if (condition()) { - LOGE("waitForCondition(%s) timed out (identity=%d), " - "but condition is true! We recovered but it " - "shouldn't happen." , condition.name(), stack.identity); - break; - } else { - LOGW("waitForCondition(%s) timed out " - "(identity=%d, status=%d). " - "CPU may be pegged. trying again.", condition.name(), - stack.identity, stack.status); - } - } else { - LOGE("waitForCondition(%s) error (%s) ", - condition.name(), strerror(-err)); - return err; - } - } - } - return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; -} -// ============================================================================ -// conditions and updates -// ============================================================================ - -SharedBufferClient::DequeueCondition::DequeueCondition( - SharedBufferClient* sbc) : ConditionBase(sbc) { -} -bool SharedBufferClient::DequeueCondition::operator()() const { - return stack.available > 0; -} - -SharedBufferClient::LockCondition::LockCondition( - SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) { -} -bool SharedBufferClient::LockCondition::operator()() const { - // NOTE: if stack.head is messed up, we could crash the client - // or cause some drawing artifacts. This is okay, as long as it is - // limited to the client. - return (buf != stack.index[stack.head]); -} - -SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition( - SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs), - mNumBuffers(numBuffers) { -} -bool SharedBufferServer::BuffersAvailableCondition::operator()() const { - return stack.available == mNumBuffers; -} - -// ---------------------------------------------------------------------------- - -SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) - : UpdateBase(sbb) { -} -ssize_t SharedBufferClient::QueueUpdate::operator()() { - android_atomic_inc(&stack.queued); - return NO_ERROR; -} - -SharedBufferClient::DequeueUpdate::DequeueUpdate(SharedBufferBase* sbb) - : UpdateBase(sbb) { -} -ssize_t SharedBufferClient::DequeueUpdate::operator()() { - if (android_atomic_dec(&stack.available) == 0) { - LOGW("dequeue probably called from multiple threads!"); - } - return NO_ERROR; -} - -SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb, - int tail, int buf) - : UpdateBase(sbb), tail(tail), buf(buf) { -} -ssize_t SharedBufferClient::CancelUpdate::operator()() { - stack.index[tail] = buf; - android_atomic_inc(&stack.available); - return NO_ERROR; -} - -SharedBufferServer::RetireUpdate::RetireUpdate( - SharedBufferBase* sbb, int numBuffers) - : UpdateBase(sbb), numBuffers(numBuffers) { -} -ssize_t SharedBufferServer::RetireUpdate::operator()() { - int32_t head = stack.head; - if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - - // Decrement the number of queued buffers - int32_t queued; - do { - queued = stack.queued; - if (queued == 0) { - return NOT_ENOUGH_DATA; - } - } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); - - // lock the buffer before advancing head, which automatically unlocks - // the buffer we preventively locked upon entering this function - - head = (head + 1) % numBuffers; - const int8_t headBuf = stack.index[head]; - stack.headBuf = headBuf; - - // head is only modified here, so we don't need to use cmpxchg - android_atomic_write(head, &stack.head); - - // now that head has moved, we can increment the number of available buffers - android_atomic_inc(&stack.available); - return head; -} - -SharedBufferServer::StatusUpdate::StatusUpdate( - SharedBufferBase* sbb, status_t status) - : UpdateBase(sbb), status(status) { -} - -ssize_t SharedBufferServer::StatusUpdate::operator()() { - android_atomic_write(status, &stack.status); - return NO_ERROR; -} - -// ============================================================================ - -SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, - int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, identity), - mNumBuffers(num), tail(0) -{ - SharedBufferStack& stack( *mSharedStack ); - tail = computeTail(); - queued_head = stack.head; -} - -int32_t SharedBufferClient::computeTail() const -{ - SharedBufferStack& stack( *mSharedStack ); - return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; -} - -ssize_t SharedBufferClient::dequeue() -{ - SharedBufferStack& stack( *mSharedStack ); - - RWLock::AutoRLock _rd(mLock); - - const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); - - //LOGD("[%d] about to dequeue a buffer", - // mSharedStack->identity); - DequeueCondition condition(this); - status_t err = waitForCondition(condition); - if (err != NO_ERROR) - return ssize_t(err); - - DequeueUpdate update(this); - updateCondition( update ); - - int dequeued = stack.index[tail]; - tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); - LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s", - dequeued, tail, dump("").string()); - - mDequeueTime[dequeued] = dequeueTime; - - return dequeued; -} - -status_t SharedBufferClient::undoDequeue(int buf) -{ - return cancel(buf); -} - -status_t SharedBufferClient::cancel(int buf) -{ - RWLock::AutoRLock _rd(mLock); - - // calculate the new position of the tail index (essentially tail--) - int localTail = (tail + mNumBuffers - 1) % mNumBuffers; - CancelUpdate update(this, localTail, buf); - status_t err = updateCondition( update ); - if (err == NO_ERROR) { - tail = localTail; - } - return err; -} - -status_t SharedBufferClient::lock(int buf) -{ - RWLock::AutoRLock _rd(mLock); - - SharedBufferStack& stack( *mSharedStack ); - LockCondition condition(this, buf); - status_t err = waitForCondition(condition); - return err; -} - -status_t SharedBufferClient::queue(int buf) -{ - RWLock::AutoRLock _rd(mLock); - - SharedBufferStack& stack( *mSharedStack ); - - queued_head = (queued_head + 1) % mNumBuffers; - stack.index[queued_head] = buf; - - QueueUpdate update(this); - status_t err = updateCondition( update ); - LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); - - const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); - stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); - - return err; -} - -bool SharedBufferClient::needNewBuffer(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - const uint32_t mask = 1<<(31-buf); - return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0; -} - -status_t SharedBufferClient::setCrop(int buf, const Rect& crop) -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.setCrop(buf, crop); -} - -status_t SharedBufferClient::setTransform(int buf, uint32_t transform) -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.setTransform(buf, uint8_t(transform)); -} - -status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.setDirtyRegion(buf, reg); -} - -status_t SharedBufferClient::setBufferCount( - int bufferCount, const SetBufferCountCallback& ipc) -{ - SharedBufferStack& stack( *mSharedStack ); - if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - - if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN) - return BAD_VALUE; - - RWLock::AutoWLock _wr(mLock); - - status_t err = ipc(bufferCount); - if (err == NO_ERROR) { - mNumBuffers = bufferCount; - queued_head = (stack.head + stack.queued) % mNumBuffers; - tail = computeTail(); - } - return err; -} - -// ---------------------------------------------------------------------------- - -SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, - int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, identity), - mNumBuffers(num) -{ - mSharedStack->init(identity); - mSharedStack->token = surface; - mSharedStack->head = num-1; - mSharedStack->available = num; - mSharedStack->queued = 0; - mSharedStack->reallocMask = 0; - memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers)); - for (int i=0 ; i<num ; i++) { - mBufferList.add(i); - mSharedStack->index[i] = i; - } -} - -SharedBufferServer::~SharedBufferServer() -{ -} - -ssize_t SharedBufferServer::retireAndLock() -{ - RWLock::AutoRLock _l(mLock); - - RetireUpdate update(this, mNumBuffers); - ssize_t buf = updateCondition( update ); - if (buf >= 0) { - if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - SharedBufferStack& stack( *mSharedStack ); - buf = stack.index[buf]; - LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", - int(buf), dump("").string()); - } - return buf; -} - -void SharedBufferServer::setStatus(status_t status) -{ - if (status < NO_ERROR) { - StatusUpdate update(this, status); - updateCondition( update ); - } -} - -status_t SharedBufferServer::reallocateAll() -{ - RWLock::AutoRLock _l(mLock); - - SharedBufferStack& stack( *mSharedStack ); - uint32_t mask = mBufferList.getMask(); - android_atomic_or(mask, &stack.reallocMask); - return NO_ERROR; -} - -status_t SharedBufferServer::reallocateAllExcept(int buffer) -{ - RWLock::AutoRLock _l(mLock); - - SharedBufferStack& stack( *mSharedStack ); - BufferList temp(mBufferList); - temp.remove(buffer); - uint32_t mask = temp.getMask(); - android_atomic_or(mask, &stack.reallocMask); - return NO_ERROR; -} - -int32_t SharedBufferServer::getQueuedCount() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.queued; -} - -Region SharedBufferServer::getDirtyRegion(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.getDirtyRegion(buf); -} - -Rect SharedBufferServer::getCrop(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.getCrop(buf); -} - -uint32_t SharedBufferServer::getTransform(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.getTransform(buf); -} - -/* - * NOTE: this is not thread-safe on the server-side, meaning - * 'head' cannot move during this operation. The client-side - * can safely operate an usual. - * - */ -status_t SharedBufferServer::resize(int newNumBuffers) -{ - if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN || - (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) { - return BAD_VALUE; - } - - RWLock::AutoWLock _l(mLock); - - if (newNumBuffers < mNumBuffers) { - return shrink(newNumBuffers); - } else { - return grow(newNumBuffers); - } -} - -status_t SharedBufferServer::grow(int newNumBuffers) -{ - SharedBufferStack& stack( *mSharedStack ); - const int numBuffers = mNumBuffers; - const int extra = newNumBuffers - numBuffers; - - // read the head, make sure it's valid - int32_t head = stack.head; - if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - - int base = numBuffers; - int32_t avail = stack.available; - int tail = head - avail + 1; - - if (tail >= 0) { - int8_t* const index = const_cast<int8_t*>(stack.index); - const int nb = numBuffers - head; - memmove(&index[head + extra], &index[head], nb); - base = head; - // move head 'extra' ahead, this doesn't impact stack.index[head]; - stack.head = head + extra; - } - stack.available += extra; - - // fill the new free space with unused buffers - BufferList::const_iterator curr(mBufferList.free_begin()); - for (int i=0 ; i<extra ; i++) { - stack.index[base+i] = *curr; - mBufferList.add(*curr); - ++curr; - } - - mNumBuffers = newNumBuffers; - return NO_ERROR; -} - -status_t SharedBufferServer::shrink(int newNumBuffers) -{ - SharedBufferStack& stack( *mSharedStack ); - - // Shrinking is only supported if there are no buffers currently dequeued. - int32_t avail = stack.available; - int32_t queued = stack.queued; - if (avail + queued != mNumBuffers) { - return INVALID_OPERATION; - } - - // Wait for any queued buffers to be displayed. - BuffersAvailableCondition condition(this, mNumBuffers); - status_t err = waitForCondition(condition); - if (err < 0) { - return err; - } - - // Reset head to index 0 and make it refer to buffer 0. The same renaming - // (head -> 0) is done in the BufferManager. - int32_t head = stack.head; - int8_t* index = const_cast<int8_t*>(stack.index); - for (int8_t i = 0; i < newNumBuffers; i++) { - index[i] = i; - } - stack.head = 0; - stack.headBuf = 0; - - // Free the buffers from the end of the list that are no longer needed. - for (int i = newNumBuffers; i < mNumBuffers; i++) { - mBufferList.remove(i); - } - - // Tell the client to reallocate all the buffers. - reallocateAll(); - - mNumBuffers = newNumBuffers; - stack.available = newNumBuffers; - - return NO_ERROR; -} - -SharedBufferStack::Statistics SharedBufferServer::getStats() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.stats; -} - -// --------------------------------------------------------------------------- -status_t SharedBufferServer::BufferList::add(int value) -{ - if (uint32_t(value) >= mCapacity) - return BAD_VALUE; - uint32_t mask = 1<<(31-value); - if (mList & mask) - return ALREADY_EXISTS; - mList |= mask; - return NO_ERROR; -} - -status_t SharedBufferServer::BufferList::remove(int value) -{ - if (uint32_t(value) >= mCapacity) - return BAD_VALUE; - uint32_t mask = 1<<(31-value); - if (!(mList & mask)) - return NAME_NOT_FOUND; - mList &= ~mask; - return NO_ERROR; -} - - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 0c5767b8c2e9..4d1d923a4262 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -21,13 +21,15 @@ #include <sys/types.h> #include <sys/stat.h> -#include <utils/Errors.h> -#include <utils/threads.h> #include <utils/CallStack.h> +#include <utils/Errors.h> #include <utils/Log.h> +#include <utils/threads.h> -#include <binder/IPCThreadState.h> #include <binder/IMemory.h> +#include <binder/IPCThreadState.h> + +#include <gui/SurfaceTextureClient.h> #include <ui/DisplayInfo.h> #include <ui/GraphicBuffer.h> @@ -35,12 +37,11 @@ #include <ui/GraphicLog.h> #include <ui/Rect.h> -#include <surfaceflinger/Surface.h> #include <surfaceflinger/ISurface.h> #include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> #include <surfaceflinger/SurfaceComposerClient.h> -#include <private/surfaceflinger/SharedBufferStack.h> #include <private/surfaceflinger/LayerState.h> namespace android { @@ -273,58 +274,10 @@ sp<Surface> SurfaceControl::getSurface() const // Surface // ============================================================================ -class SurfaceClient : public Singleton<SurfaceClient> -{ - // all these attributes are constants - sp<ISurfaceComposer> mComposerService; - sp<ISurfaceComposerClient> mClient; - status_t mStatus; - SharedClient* mControl; - sp<IMemoryHeap> mControlMemory; - - SurfaceClient() - : Singleton<SurfaceClient>(), mStatus(NO_INIT) - { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - mComposerService = sf; - mClient = sf->createClientConnection(); - if (mClient != NULL) { - mControlMemory = mClient->getControlBlock(); - if (mControlMemory != NULL) { - mControl = static_cast<SharedClient *>( - mControlMemory->getBase()); - if (mControl) { - mStatus = NO_ERROR; - } - } - } - } - friend class Singleton<SurfaceClient>; -public: - status_t initCheck() const { - return mStatus; - } - SharedClient* getSharedClient() const { - return mControl; - } - ssize_t getTokenForSurface(const sp<ISurface>& sur) const { - // TODO: we could cache a few tokens here to avoid an IPC - return mClient->getTokenForSurface(sur); - } - void signalServer() const { - mComposerService->signal(); - } -}; - -ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient); - // --------------------------------------------------------------------------- Surface::Surface(const sp<SurfaceControl>& surface) - : mBufferMapper(GraphicBufferMapper::get()), - mClient(SurfaceClient::getInstance()), - mSharedBufferClient(NULL), - mInitCheck(NO_INIT), + : mInitCheck(NO_INIT), mSurface(surface->mSurface), mIdentity(surface->mIdentity), mFormat(surface->mFormat), mFlags(surface->mFlags), @@ -334,10 +287,7 @@ Surface::Surface(const sp<SurfaceControl>& surface) } Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref) - : mBufferMapper(GraphicBufferMapper::get()), - mClient(SurfaceClient::getInstance()), - mSharedBufferClient(NULL), - mInitCheck(NO_INIT) + : mInitCheck(NO_INIT) { mSurface = interface_cast<ISurface>(ref); mIdentity = parcel.readInt32(); @@ -382,7 +332,6 @@ status_t Surface::writeToParcel( } - Mutex Surface::sCachedSurfacesLock; DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces; @@ -422,32 +371,29 @@ void Surface::init() ANativeWindow::query = query; ANativeWindow::perform = perform; - DisplayInfo dinfo; - SurfaceComposerClient::getDisplayInfo(0, &dinfo); - const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; - const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; - // FIXME: set real values here - const_cast<int&>(ANativeWindow::minSwapInterval) = 1; - const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; - const_cast<uint32_t&>(ANativeWindow::flags) = 0; - - mNextBufferTransform = 0; - mConnected = 0; - mSwapRectangle.makeInvalid(); - mNextBufferCrop = Rect(0,0); - // two buffers by default - mBuffers.setCapacity(2); - mBuffers.insertAt(0, 2); - - if (mSurface != 0 && mClient.initCheck() == NO_ERROR) { - int32_t token = mClient.getTokenForSurface(mSurface); - if (token >= 0) { - mSharedBufferClient = new SharedBufferClient( - mClient.getSharedClient(), token, 2, mIdentity); - mInitCheck = mClient.getSharedClient()->validate(token); - } else { - LOGW("Not initializing the shared buffer client because token = %d", - token); + if (mSurface != NULL) { + sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture()); + LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface"); + if (surfaceTexture != NULL) { + mSurfaceTextureClient = new SurfaceTextureClient(surfaceTexture); + mSurfaceTextureClient->setUsage(GraphicBuffer::USAGE_HW_RENDER); + } + + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; + const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; + + const_cast<int&>(ANativeWindow::minSwapInterval) = + mSurfaceTextureClient->minSwapInterval; + + const_cast<int&>(ANativeWindow::maxSwapInterval) = + mSurfaceTextureClient->maxSwapInterval; + + const_cast<uint32_t&>(ANativeWindow::flags) = 0; + + if (mSurfaceTextureClient != 0) { + mInitCheck = NO_ERROR; } } } @@ -456,9 +402,8 @@ Surface::~Surface() { // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. - mBuffers.clear(); + mSurfaceTextureClient.clear(); mSurface.clear(); - delete mSharedBufferClient; IPCThreadState::self()->flushCommands(); } @@ -473,32 +418,6 @@ status_t Surface::validate(bool inCancelBuffer) const LOGE("invalid token (identity=%u)", mIdentity); return mInitCheck; } - - // verify the identity of this surface - uint32_t identity = mSharedBufferClient->getIdentity(); - if (mIdentity != identity) { - LOGE("[Surface] using an invalid surface, " - "identity=%u should be %d", - mIdentity, identity); - CallStack stack; - stack.update(); - stack.dump("Surface"); - return BAD_INDEX; - } - - // check the surface didn't become invalid - status_t err = mSharedBufferClient->getStatus(); - if (err != NO_ERROR) { - if (!inCancelBuffer) { - LOGE("surface (identity=%u) is invalid, err=%d (%s)", - mIdentity, err, strerror(-err)); - CallStack stack; - stack.update(); - stack.dump("Surface"); - } - return err; - } - return NO_ERROR; } @@ -509,7 +428,8 @@ sp<IBinder> Surface::asBinder() const { // ---------------------------------------------------------------------------- int Surface::setSwapInterval(ANativeWindow* window, int interval) { - return 0; + Surface* self = getSelf(window); + return self->setSwapInterval(interval); } int Surface::dequeueBuffer(ANativeWindow* window, @@ -554,383 +474,52 @@ int Surface::perform(ANativeWindow* window, // ---------------------------------------------------------------------------- -bool Surface::needNewBuffer(int bufIdx, - uint32_t *pWidth, uint32_t *pHeight, - uint32_t *pFormat, uint32_t *pUsage) const -{ - Mutex::Autolock _l(mSurfaceLock); - - // Always call needNewBuffer(), since it clears the needed buffers flags - bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); - bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]); - bool newNeewBuffer = needNewBuffer || !validBuffer; - if (newNeewBuffer) { - mBufferInfo.get(pWidth, pHeight, pFormat, pUsage); - } - return newNeewBuffer; +int Surface::setSwapInterval(int interval) { + return mSurfaceTextureClient->setSwapInterval(interval); } -int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1); - - ssize_t bufIdx = mSharedBufferClient->dequeue(); - - logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx); - - if (bufIdx < 0) { - LOGE("error dequeuing a buffer (%s)", strerror(bufIdx)); - return bufIdx; - } - - // grow the buffer array if needed - const size_t size = mBuffers.size(); - const size_t needed = bufIdx+1; - if (size < needed) { - mBuffers.insertAt(size, needed-size); - } - - uint32_t w, h, format, usage; - if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) { - err = getBufferLocked(bufIdx, w, h, format, usage); - LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)", - bufIdx, w, h, format, usage, strerror(-err)); - if (err == NO_ERROR) { - // reset the width/height with the what we get from the buffer - const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); - mWidth = uint32_t(backBuffer->width); - mHeight = uint32_t(backBuffer->height); - } - } - - // if we still don't have a buffer here, we probably ran out of memory - const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); - if (!err && backBuffer==0) { - err = NO_MEMORY; - } - +int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) { + status_t err = mSurfaceTextureClient->dequeueBuffer(buffer); if (err == NO_ERROR) { - mDirtyRegion.set(backBuffer->width, backBuffer->height); - *buffer = backBuffer.get(); - } else { - mSharedBufferClient->undoDequeue(bufIdx); + mDirtyRegion.set(buffer[0]->width, buffer[0]->height); } - return err; } -int Surface::cancelBuffer(ANativeWindowBuffer* buffer) -{ - status_t err = validate(true); - switch (err) { - case NO_ERROR: - // no error, common case - break; - case BAD_INDEX: - // legitimate errors here - return err; - default: - // other errors happen because the surface is now invalid, - // for instance because it has been destroyed. In this case, - // we just fail silently (canceling a buffer is not technically - // an error at this point) - return NO_ERROR; - } - - int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); - - err = mSharedBufferClient->cancel(bufIdx); - - LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err)); - return err; +int Surface::cancelBuffer(ANativeWindowBuffer* buffer) { + return mSurfaceTextureClient->cancelBuffer(buffer); } - -int Surface::lockBuffer(ANativeWindowBuffer* buffer) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); - - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx); - - err = mSharedBufferClient->lock(bufIdx); - - logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx); - - LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); - return err; +int Surface::lockBuffer(ANativeWindowBuffer* buffer) { + return mSurfaceTextureClient->lockBuffer(buffer); } -int Surface::queueBuffer(ANativeWindowBuffer* buffer) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - if (mSwapRectangle.isValid()) { - mDirtyRegion.set(mSwapRectangle); - } - - int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); - - GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx); - - mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); - mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); - mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); - err = mSharedBufferClient->queue(bufIdx); - LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); - - if (err == NO_ERROR) { - // TODO: can we avoid this IPC if we know there is one pending? - mClient.signalServer(); - } - return err; +int Surface::queueBuffer(ANativeWindowBuffer* buffer) { + return mSurfaceTextureClient->queueBuffer(buffer); } -int Surface::query(int what, int* value) const -{ +int Surface::query(int what, int* value) const { switch (what) { - case NATIVE_WINDOW_WIDTH: - *value = int(mWidth); - return NO_ERROR; - case NATIVE_WINDOW_HEIGHT: - *value = int(mHeight); - return NO_ERROR; - case NATIVE_WINDOW_FORMAT: - *value = int(mFormat); + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: + // TODO: this is not needed anymore + *value = 1; return NO_ERROR; - case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - *value = MIN_UNDEQUEUED_BUFFERS; - return NO_ERROR; - case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - *value = sf->authenticateSurface(mSurface) ? 1 : 0; - return NO_ERROR; - } case NATIVE_WINDOW_CONCRETE_TYPE: + // TODO: this is not needed anymore *value = NATIVE_WINDOW_SURFACE; return NO_ERROR; } - return BAD_VALUE; -} - -int Surface::perform(int operation, va_list args) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - int res = NO_ERROR; - switch (operation) { - case NATIVE_WINDOW_SET_USAGE: - dispatch_setUsage( args ); - break; - case NATIVE_WINDOW_CONNECT: - res = dispatch_connect( args ); - break; - case NATIVE_WINDOW_DISCONNECT: - res = dispatch_disconnect( args ); - break; - case NATIVE_WINDOW_SET_CROP: - res = dispatch_crop( args ); - break; - case NATIVE_WINDOW_SET_BUFFER_COUNT: - res = dispatch_set_buffer_count( args ); - break; - case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: - res = dispatch_set_buffers_geometry( args ); - break; - case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: - res = dispatch_set_buffers_transform( args ); - break; - case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: - res = dispatch_set_buffers_timestamp( args ); - break; - default: - res = NAME_NOT_FOUND; - break; - } - return res; -} - -void Surface::dispatch_setUsage(va_list args) { - int usage = va_arg(args, int); - setUsage( usage ); -} -int Surface::dispatch_connect(va_list args) { - int api = va_arg(args, int); - return connect( api ); -} -int Surface::dispatch_disconnect(va_list args) { - int api = va_arg(args, int); - return disconnect( api ); -} -int Surface::dispatch_crop(va_list args) { - android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); - return crop( reinterpret_cast<Rect const*>(rect) ); -} -int Surface::dispatch_set_buffer_count(va_list args) { - size_t bufferCount = va_arg(args, size_t); - return setBufferCount(bufferCount); -} -int Surface::dispatch_set_buffers_geometry(va_list args) { - int w = va_arg(args, int); - int h = va_arg(args, int); - int f = va_arg(args, int); - return setBuffersGeometry(w, h, f); -} - -int Surface::dispatch_set_buffers_transform(va_list args) { - int transform = va_arg(args, int); - return setBuffersTransform(transform); -} - -int Surface::dispatch_set_buffers_timestamp(va_list args) { - int64_t timestamp = va_arg(args, int64_t); - return setBuffersTimestamp(timestamp); -} - -void Surface::setUsage(uint32_t reqUsage) -{ - Mutex::Autolock _l(mSurfaceLock); - mBufferInfo.set(reqUsage); -} - -int Surface::connect(int api) -{ - Mutex::Autolock _l(mSurfaceLock); - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - if (mConnected) { - err = -EINVAL; - } else { - mConnected = api; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -int Surface::disconnect(int api) -{ - Mutex::Autolock _l(mSurfaceLock); - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - if (mConnected == api) { - mConnected = 0; - } else { - err = -EINVAL; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -int Surface::crop(Rect const* rect) -{ - Mutex::Autolock _l(mSurfaceLock); - // TODO: validate rect size - - if (rect == NULL || rect->isEmpty()) { - mNextBufferCrop = Rect(0,0); - } else { - mNextBufferCrop = *rect; - } - - return NO_ERROR; + return mSurfaceTextureClient->query(what, value); } -int Surface::setBufferCount(int bufferCount) -{ - sp<ISurface> s(mSurface); - if (s == 0) return NO_INIT; - - class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { - sp<ISurface> surface; - virtual status_t operator()(int bufferCount) const { - return surface->setBufferCount(bufferCount); - } - public: - SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { } - } ipc(s); - - status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); - LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", - bufferCount, strerror(-err)); - - if (err == NO_ERROR) { - // Clear out any references to the old buffers. - mBuffers.clear(); - } - - return err; -} - -int Surface::setBuffersGeometry(int w, int h, int format) -{ - if (w<0 || h<0 || format<0) - return BAD_VALUE; - - if ((w && !h) || (!w && h)) - return BAD_VALUE; - - Mutex::Autolock _l(mSurfaceLock); - if (mConnected == NATIVE_WINDOW_API_EGL) { - return INVALID_OPERATION; - } - - mBufferInfo.set(w, h, format); - if (format != 0) { - // we update the format of the surface as reported by query(). - // this is to allow applications to change the format of a surface's - // buffer, and have it reflected in EGL; which is needed for - // EGLConfig validation. - mFormat = format; - } - - mNextBufferCrop = Rect(0,0); - - return NO_ERROR; -} - -int Surface::setBuffersTransform(int transform) -{ - Mutex::Autolock _l(mSurfaceLock); - mNextBufferTransform = transform; - return NO_ERROR; -} - -int Surface::setBuffersTimestamp(int64_t timestamp) -{ - // Surface doesn't really have anything meaningful to do with timestamps - // so they'll just be dropped here. - return NO_ERROR; +int Surface::perform(int operation, va_list args) { + return mSurfaceTextureClient->perform(operation, args); } // ---------------------------------------------------------------------------- -int Surface::getConnectedApi() const -{ - Mutex::Autolock _l(mSurfaceLock); - return mConnected; +int Surface::getConnectedApi() const { + return mSurfaceTextureClient->getConnectedApi(); } // ---------------------------------------------------------------------------- @@ -967,16 +556,17 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) } // we're intending to do software rendering from this point - setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + mSurfaceTextureClient->setUsage( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); ANativeWindowBuffer* out; - status_t err = dequeueBuffer(&out); + status_t err = mSurfaceTextureClient->dequeueBuffer(&out); LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); - err = lockBuffer(backBuffer.get()); - LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)", - getBufferIndex(backBuffer), strerror(-err)); + err = mSurfaceTextureClient->lockBuffer(backBuffer.get()); + LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)", + backBuffer->handle, strerror(-err)); if (err == NO_ERROR) { const Rect bounds(backBuffer->width, backBuffer->height); const Region boundsRegion(bounds); @@ -1043,110 +633,14 @@ status_t Surface::unlockAndPost() status_t err = mLockedBuffer->unlock(); LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); - err = queueBuffer(mLockedBuffer.get()); - LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)", - getBufferIndex(mLockedBuffer), strerror(-err)); + err = mSurfaceTextureClient->queueBuffer(mLockedBuffer.get()); + LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)", + mLockedBuffer->handle, strerror(-err)); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0; return err; } -void Surface::setSwapRectangle(const Rect& r) { - Mutex::Autolock _l(mSurfaceLock); - mSwapRectangle = r; -} - -int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const -{ - int idx = buffer->getIndex(); - if (idx < 0) { - // The buffer doesn't have an index set. See if the handle the same as - // one of the buffers for which we do know the index. This can happen - // e.g. if GraphicBuffer is used to wrap an ANativeWindowBuffer that - // was dequeued from an ANativeWindow. - for (size_t i = 0; i < mBuffers.size(); i++) { - if (mBuffers[i] != 0 && buffer->handle == mBuffers[i]->handle) { - idx = mBuffers[i]->getIndex(); - break; - } - } - } - return idx; -} - -status_t Surface::getBufferLocked(int index, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) -{ - sp<ISurface> s(mSurface); - if (s == 0) return NO_INIT; - - status_t err = NO_MEMORY; - - // free the current buffer - sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); - if (currentBuffer != 0) { - currentBuffer.clear(); - } - - sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage); - LOGE_IF(buffer==0, - "ISurface::getBuffer(%d, %08x) returned NULL", - index, usage); - if (buffer != 0) { // this should always happen by construction - LOGE_IF(buffer->handle == NULL, - "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " - "returned a buffer with a null handle", - mIdentity, index, w, h, format, usage); - err = mSharedBufferClient->getStatus(); - LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); - if (!err && buffer->handle != NULL) { - currentBuffer = buffer; - currentBuffer->setIndex(index); - } else { - err = err<0 ? err : status_t(NO_MEMORY); - } - } - return err; -} - -// ---------------------------------------------------------------------------- -Surface::BufferInfo::BufferInfo() - : mWidth(0), mHeight(0), mFormat(0), - mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0) -{ -} - -void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) { - if ((mWidth != w) || (mHeight != h) || (mFormat != format)) { - mWidth = w; - mHeight = h; - mFormat = format; - mDirty |= GEOMETRY; - } -} - -void Surface::BufferInfo::set(uint32_t usage) { - mUsage = usage; -} - -void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight, - uint32_t *pFormat, uint32_t *pUsage) const { - *pWidth = mWidth; - *pHeight = mHeight; - *pFormat = mFormat; - *pUsage = mUsage; -} - -bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const { - // make sure we AT LEAST have the usage flags we want - if (mDirty || buffer==0 || - ((buffer->usage & mUsage) != mUsage)) { - mDirty = 0; - return false; - } - return true; -} - // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a1ff2c1e2f5d..1678711aaf42 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -20,19 +20,20 @@ #include <sys/types.h> #include <utils/Errors.h> -#include <utils/threads.h> -#include <utils/SortedVector.h> #include <utils/Log.h> #include <utils/Singleton.h> +#include <utils/SortedVector.h> +#include <utils/String8.h> +#include <utils/threads.h> -#include <binder/IServiceManager.h> #include <binder/IMemory.h> +#include <binder/IServiceManager.h> #include <ui/DisplayInfo.h> +#include <surfaceflinger/ISurface.h> #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/ISurfaceComposerClient.h> -#include <surfaceflinger/ISurface.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <private/surfaceflinger/LayerState.h> @@ -217,7 +218,7 @@ void SurfaceComposerClient::dispose() status_t SurfaceComposerClient::getDisplayInfo( DisplayID dpy, DisplayInfo* info) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); @@ -235,7 +236,7 @@ status_t SurfaceComposerClient::getDisplayInfo( ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -244,7 +245,7 @@ ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -253,7 +254,7 @@ ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 2cda4c82dbcf..3cecdb401ab1 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -271,7 +271,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } - if (state == BufferSlot::FREE || i == mCurrentTexture) { + if (state == BufferSlot::FREE /*|| i == mCurrentTexture*/) { foundSync = i; if (i != mCurrentTexture) { found = i; diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index c20fcf27ccae..22b085227068 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -181,6 +181,12 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { int SurfaceTextureClient::query(int what, int* value) const { LOGV("SurfaceTextureClient::query"); switch (what) { + case NATIVE_WINDOW_FORMAT: + if (mReqFormat) { + *value = mReqFormat; + return NO_ERROR; + } + break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: // TODO: this is not needed anymore *value = 0; diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index da04b4a3e6eb..e1a85f324856 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -505,11 +505,11 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + thread->run(); ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - thread->run(); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + //ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + //ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); thread->bufferDequeued(); thread->requestExitAndWait(); } diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 16280d2e01c1..56c1702929bd 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -273,8 +273,12 @@ protected: *outPgm = program; } + static int abs(int value) { + return value > 0 ? value : -value; + } + ::testing::AssertionResult checkPixel(int x, int y, int r, - int g, int b, int a) { + int g, int b, int a, int tolerance=2) { GLubyte pixel[4]; String8 msg; glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); @@ -288,22 +292,22 @@ protected: return ::testing::AssertionFailure( ::testing::Message(msg.string())); } - if (r >= 0 && GLubyte(r) != pixel[0]) { + if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { msg += String8::format("r(%d isn't %d)", pixel[0], r); } - if (g >= 0 && GLubyte(g) != pixel[1]) { + if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("g(%d isn't %d)", pixel[1], g); } - if (b >= 0 && GLubyte(b) != pixel[2]) { + if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("b(%d isn't %d)", pixel[2], b); } - if (a >= 0 && GLubyte(a) != pixel[3]) { + if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } @@ -810,7 +814,8 @@ TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) { +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageAfterFrameFinishedWorks) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index e8d40ba08352..093189c5c05a 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + BlobCache.cpp \ BufferedTextOutput.cpp \ CallStack.cpp \ Debug.cpp \ diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp new file mode 100644 index 000000000000..1298fa733cb0 --- /dev/null +++ b/libs/utils/BlobCache.cpp @@ -0,0 +1,232 @@ +/* + ** Copyright 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. + */ + +#define LOG_TAG "BlobCache" +//#define LOG_NDEBUG 0 + +#include <stdlib.h> +#include <string.h> + +#include <utils/BlobCache.h> +#include <utils/Log.h> + +namespace android { + +BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): + mMaxKeySize(maxKeySize), + mMaxValueSize(maxValueSize), + mMaxTotalSize(maxTotalSize), + mTotalSize(0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mRandState[0] = (now >> 0) & 0xFFFF; + mRandState[1] = (now >> 16) & 0xFFFF; + mRandState[2] = (now >> 32) & 0xFFFF; + LOGV("initializing random seed using %lld", now); +} + +void BlobCache::set(const void* key, size_t keySize, const void* value, + size_t valueSize) { + if (mMaxKeySize < keySize) { + LOGV("set: not caching because the key is too large: %d (limit: %d)", + keySize, mMaxKeySize); + return; + } + if (mMaxValueSize < valueSize) { + LOGV("set: not caching because the value is too large: %d (limit: %d)", + valueSize, mMaxValueSize); + return; + } + if (mMaxTotalSize < keySize + valueSize) { + LOGV("set: not caching because the combined key/value size is too " + "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); + return; + } + if (keySize == 0) { + LOGW("set: not caching because keySize is 0"); + return; + } + if (valueSize <= 0) { + LOGW("set: not caching because valueSize is 0"); + return; + } + + Mutex::Autolock lock(mMutex); + sp<Blob> dummyKey(new Blob(key, keySize, false)); + CacheEntry dummyEntry(dummyKey, NULL); + + while (true) { + + ssize_t index = mCacheEntries.indexOf(dummyEntry); + if (index < 0) { + // Create a new cache entry. + sp<Blob> keyBlob(new Blob(key, keySize, true)); + sp<Blob> valueBlob(new Blob(value, valueSize, true)); + size_t newTotalSize = mTotalSize + keySize + valueSize; + if (mMaxTotalSize < newTotalSize) { + if (isCleanable()) { + // Clean the cache and try again. + clean(); + continue; + } else { + LOGV("set: not caching new key/value pair because the " + "total cache size limit would be exceeded: %d " + "(limit: %d)", + keySize + valueSize, mMaxTotalSize); + break; + } + } + mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); + mTotalSize = newTotalSize; + LOGV("set: created new cache entry with %d byte key and %d byte value", + keySize, valueSize); + } else { + // Update the existing cache entry. + sp<Blob> valueBlob(new Blob(value, valueSize, true)); + sp<Blob> oldValueBlob(mCacheEntries[index].getValue()); + size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize(); + if (mMaxTotalSize < newTotalSize) { + if (isCleanable()) { + // Clean the cache and try again. + clean(); + continue; + } else { + LOGV("set: not caching new value because the total cache " + "size limit would be exceeded: %d (limit: %d)", + keySize + valueSize, mMaxTotalSize); + break; + } + } + mCacheEntries.editItemAt(index).setValue(valueBlob); + mTotalSize = newTotalSize; + LOGV("set: updated existing cache entry with %d byte key and %d byte " + "value", keySize, valueSize); + } + break; + } +} + +size_t BlobCache::get(const void* key, size_t keySize, void* value, + size_t valueSize) { + if (mMaxKeySize < keySize) { + LOGV("get: not searching because the key is too large: %d (limit %d)", + keySize, mMaxKeySize); + return 0; + } + Mutex::Autolock lock(mMutex); + sp<Blob> dummyKey(new Blob(key, keySize, false)); + CacheEntry dummyEntry(dummyKey, NULL); + ssize_t index = mCacheEntries.indexOf(dummyEntry); + if (index < 0) { + LOGV("get: no cache entry found for key of size %d", keySize); + return 0; + } + + // The key was found. Return the value if the caller's buffer is large + // enough. + sp<Blob> valueBlob(mCacheEntries[index].getValue()); + size_t valueBlobSize = valueBlob->getSize(); + if (valueBlobSize <= valueSize) { + LOGV("get: copying %d bytes to caller's buffer", valueBlobSize); + memcpy(value, valueBlob->getData(), valueBlobSize); + } else { + LOGV("get: caller's buffer is too small for value: %d (needs %d)", + valueSize, valueBlobSize); + } + return valueBlobSize; +} + +void BlobCache::clean() { + // Remove a random cache entry until the total cache size gets below half + // the maximum total cache size. + while (mTotalSize > mMaxTotalSize / 2) { + size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size())); + const CacheEntry& entry(mCacheEntries[i]); + mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize(); + mCacheEntries.removeAt(i); + } +} + +bool BlobCache::isCleanable() const { + return mTotalSize > mMaxTotalSize / 2; +} + +BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): + mData(copyData ? malloc(size) : data), + mSize(size), + mOwnsData(copyData) { + if (copyData) { + memcpy(const_cast<void*>(mData), data, size); + } +} + +BlobCache::Blob::~Blob() { + if (mOwnsData) { + free(const_cast<void*>(mData)); + } +} + +bool BlobCache::Blob::operator<(const Blob& rhs) const { + if (mSize == rhs.mSize) { + return memcmp(mData, rhs.mData, mSize) < 0; + } else { + return mSize < rhs.mSize; + } +} + +const void* BlobCache::Blob::getData() const { + return mData; +} + +size_t BlobCache::Blob::getSize() const { + return mSize; +} + +BlobCache::CacheEntry::CacheEntry() { +} + +BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value): + mKey(key), + mValue(value) { +} + +BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce): + mKey(ce.mKey), + mValue(ce.mValue) { +} + +bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const { + return *mKey < *rhs.mKey; +} + +const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) { + mKey = rhs.mKey; + mValue = rhs.mValue; + return *this; +} + +sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const { + return mKey; +} + +sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const { + return mValue; +} + +void BlobCache::CacheEntry::setValue(const sp<Blob>& value) { + mValue = value; +} + +} // namespace android diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 58e0811f21a0..8db2009587f7 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -49,6 +49,11 @@ namespace android { // --------------------------------------------------------------------------- +RefBase::Destroyer::~Destroyer() { +} + +// --------------------------------------------------------------------------- + class RefBase::weakref_impl : public RefBase::weakref_type { public: @@ -56,7 +61,7 @@ public: volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; - + Destroyer* mDestroyer; #if !DEBUG_REFS @@ -65,6 +70,7 @@ public: , mWeak(0) , mBase(base) , mFlags(0) + , mDestroyer(0) { } @@ -345,10 +351,6 @@ void RefBase::incStrong(const void* id) const const_cast<RefBase*>(this)->onFirstRef(); } -void RefBase::destroy() const { - delete this; -} - void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; @@ -361,7 +363,11 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast<RefBase*>(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - destroy(); + if (refs->mDestroyer) { + refs->mDestroyer->destroy(this); + } else { + delete this; + } } } refs->decWeak(id); @@ -394,7 +400,9 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } - +void RefBase::setDestroyer(RefBase::Destroyer* destroyer) { + mRefs->mDestroyer = destroyer; +} RefBase* RefBase::weakref_type::refBase() const { @@ -420,7 +428,11 @@ void RefBase::weakref_type::decWeak(const void* id) if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { if (impl->mStrong == INITIAL_STRONG_VALUE) { if (impl->mBase) { - impl->mBase->destroy(); + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } } } else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); @@ -430,7 +442,11 @@ void RefBase::weakref_type::decWeak(const void* id) impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { if (impl->mBase) { - impl->mBase->destroy(); + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } } } } diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 72d48769a589..87ad98eaad77 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -6,6 +6,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ + BlobCache_test.cpp \ ObbFile_test.cpp \ Looper_test.cpp \ String8_test.cpp \ diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp new file mode 100644 index 000000000000..653ea5e91cde --- /dev/null +++ b/libs/utils/tests/BlobCache_test.cpp @@ -0,0 +1,257 @@ +/* + ** Copyright 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. + */ + +#include <gtest/gtest.h> + +#include <utils/BlobCache.h> + +namespace android { + +class BlobCacheTest : public ::testing::Test { +protected: + enum { + MAX_KEY_SIZE = 6, + MAX_VALUE_SIZE = 8, + MAX_TOTAL_SIZE = 13, + }; + + virtual void SetUp() { + mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); + } + + virtual void TearDown() { + mBC.clear(); + } + + sp<BlobCache> mBC; +}; + +TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { + char buf[2] = { 0xee, 0xee }; + mBC->set("ab", 2, "cd", 2); + mBC->set("ef", 2, "gh", 2); + ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); + ASSERT_EQ('c', buf[0]); + ASSERT_EQ('d', buf[1]); + ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2)); + ASSERT_EQ('g', buf[0]); + ASSERT_EQ('h', buf[1]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { + char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ('e', buf[1]); + ASSERT_EQ('f', buf[2]); + ASSERT_EQ('g', buf[3]); + ASSERT_EQ('h', buf[4]); + ASSERT_EQ(0xee, buf[5]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { + char buf[3] = { 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); +} + +TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); +} + +TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, "ijkl", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('i', buf[0]); + ASSERT_EQ('j', buf[1]); + ASSERT_EQ('k', buf[2]); + ASSERT_EQ('l', buf[3]); +} + +TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { + char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { + char key[MAX_KEY_SIZE+1]; + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE+1; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); + ASSERT_EQ(0xee, buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) { + char buf[MAX_VALUE_SIZE+1]; + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + SCOPED_TRACE(i); + ASSERT_EQ(0xee, buf[i]); + } +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { + // Check a testing assumptions + ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE); + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { + char key[MAX_KEY_SIZE]; + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE, "wxyz", 4); + ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4)); + ASSERT_EQ('w', buf[0]); + ASSERT_EQ('x', buf[1]); + ASSERT_EQ('y', buf[2]); + ASSERT_EQ('z', buf[3]); +} + +TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) { + char buf[MAX_VALUE_SIZE]; + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, + MAX_VALUE_SIZE)); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + SCOPED_TRACE(i); + ASSERT_EQ('b', buf[i]); + } +} + +TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { + // Check a testing assumption + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, bufSize); + ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { + char buf[1] = { 0xee }; + mBC->set("x", 1, "y", 1); + ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); + ASSERT_EQ('y', buf[0]); +} + +TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { + for (int i = 0; i < 256; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + int numCached = 0; + for (int i = 0; i < 256; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached); +} + +TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + // Insert one more entry, causing a cache overflow. + { + uint8_t k = maxEntries; + mBC->set(&k, 1, "x", 1); + } + // Count the number of entries in the cache. + int numCached = 0; + for (int i = 0; i < maxEntries+1; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_EQ(maxEntries/2 + 1, numCached); +} + +} // namespace android diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 29add52c8af2..6e803a441356 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -192,6 +192,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, outAxisInfo->maxValue = info.maximum; outAxisInfo->flat = info.flat; outAxisInfo->fuzz = info.fuzz; + outAxisInfo->resolution = info.resolution; } return OK; } diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 558959b1a968..abe1318206ff 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -34,39 +34,6 @@ #include <linux/input.h> -/* These constants are not defined in linux/input.h in the version of the kernel - * headers currently provided with Bionic. */ - -#define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) - -#define INPUT_PROP_POINTER 0x00 -#define INPUT_PROP_DIRECT 0x01 -#define INPUT_PROP_BUTTONPAD 0x02 -#define INPUT_PROP_SEMI_MT 0x03 -#define INPUT_PROP_MAX 0x1f -#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) - -#define ABS_MT_SLOT 0x2f -#define ABS_MT_TOUCH_MAJOR 0x30 -#define ABS_MT_TOUCH_MINOR 0x31 -#define ABS_MT_WIDTH_MAJOR 0x32 -#define ABS_MT_WIDTH_MINOR 0x33 -#define ABS_MT_ORIENTATION 0x34 -#define ABS_MT_POSITION_X 0x35 -#define ABS_MT_POSITION_Y 0x36 -#define ABS_MT_TOOL_TYPE 0x37 -#define ABS_MT_BLOB_ID 0x38 -#define ABS_MT_TRACKING_ID 0x39 -#define ABS_MT_PRESSURE 0x3a -#define ABS_MT_DISTANCE 0x3b - -#define MT_TOOL_FINGER 0 -#define MT_TOOL_PEN 1 - -#define SYN_MT_REPORT 2 -#define SYN_DROPPED 3 - - /* Convenience constants. */ #define BTN_FIRST 0x100 // first button scancode @@ -97,6 +64,7 @@ struct RawAbsoluteAxisInfo { int32_t maxValue; // maximum value int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise + int32_t resolution; // resolution in units per mm or radians per mm inline void clear() { valid = false; @@ -104,6 +72,7 @@ struct RawAbsoluteAxisInfo { maxValue = 0; flat = 0; fuzz = 0; + resolution = 0; } }; diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index fcc619857c0a..15bb300d3bae 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -1044,8 +1044,8 @@ void InputMapper::fadePointer() { void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, const RawAbsoluteAxisInfo& axis, const char* name) { if (axis.valid) { - dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); } else { dump.appendFormat(INDENT4 "%s: unknown range\n", name); } @@ -5656,9 +5656,10 @@ void JoystickInputMapper::dump(String8& dump) { dump.appendFormat(INDENT4 " scale=%0.5f, offset=%0.5f, " "highScale=%0.5f, highOffset=%0.5f\n", axis.scale, axis.offset, axis.highScale, axis.highOffset); - dump.appendFormat(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, rawFlat=%d, rawFuzz=%d\n", + dump.appendFormat(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " + "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, - axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz); + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution); } } diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index d04c9e7db145..acda86bc74fe 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -481,7 +481,7 @@ public: } void addAbsoluteAxis(int32_t deviceId, int axis, - int32_t minValue, int32_t maxValue, int flat, int fuzz) { + int32_t minValue, int32_t maxValue, int flat, int fuzz, int resolution = 0) { Device* device = getDevice(deviceId); RawAbsoluteAxisInfo info; @@ -490,6 +490,7 @@ public: info.maxValue = maxValue; info.flat = flat; info.fuzz = fuzz; + info.resolution = resolution; device->absoluteAxes.add(axis, info); } diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 86671d6d19bb..ec59da6ed966 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -46,6 +46,7 @@ import android.text.TextUtils.SimpleStringSplitter; import android.util.Slog; import android.util.SparseArray; import android.view.IWindow; +import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnection; @@ -149,9 +150,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { notifyEventListenerLocked(service, eventType); - AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); - service.mPendingEvents.remove(eventType); - tryRecycleLocked(oldEvent); } } }; @@ -319,17 +317,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public boolean sendAccessibilityEvent(AccessibilityEvent event) { synchronized (mLock) { - mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event); - notifyAccessibilityServicesDelayedLocked(event, false); - notifyAccessibilityServicesDelayedLocked(event, true); - } - // event not scheduled for dispatch => recycle - if (mHandledFeedbackTypes == 0) { - event.recycle(); - } else { - mHandledFeedbackTypes = 0; + if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) { + mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event); + notifyAccessibilityServicesDelayedLocked(event, false); + notifyAccessibilityServicesDelayedLocked(event, true); + } } - + event.recycle(); + mHandledFeedbackTypes = 0; return (OWN_PROCESS_ID != Binder.getCallingPid()); } @@ -517,46 +512,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyAccessibilityServiceDelayedLocked(Service service, AccessibilityEvent event) { synchronized (mLock) { - int eventType = event.getEventType(); + final int eventType = event.getEventType(); + // Make a copy since during dispatch it is possible the event to + // be modified to remove its source if the receiving service does + // not have permission to access the window content. + AccessibilityEvent newEvent = AccessibilityEvent.obtain(event); AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); - service.mPendingEvents.put(eventType, event); + service.mPendingEvents.put(eventType, newEvent); - int what = eventType | (service.mId << 16); + final int what = eventType | (service.mId << 16); if (oldEvent != null) { mHandler.removeMessages(what); - tryRecycleLocked(oldEvent); + oldEvent.recycle(); } Message message = mHandler.obtainMessage(what, service); - message.arg1 = event.getEventType(); + message.arg1 = eventType; mHandler.sendMessageDelayed(message, service.mNotificationTimeout); } } /** - * Recycles an event if it can be safely recycled. The condition is that no - * not notified service is interested in the event. - * - * @param event The event. - */ - private void tryRecycleLocked(AccessibilityEvent event) { - if (event == null) { - return; - } - int eventType = event.getEventType(); - List<Service> services = mServices; - - // linear in the number of service which is not large - for (int i = 0, count = services.size(); i < count; i++) { - Service service = services.get(i); - if (service.mPendingEvents.get(eventType) == event) { - return; - } - } - event.recycle(); - } - - /** * Notifies a service for a scheduled event given the event type. * * @param service The service. @@ -565,7 +541,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyEventListenerLocked(Service service, int eventType) { IEventListener listener = service.mServiceInterface; AccessibilityEvent event = service.mPendingEvents.get(eventType); - + service.mPendingEvents.remove(eventType); try { if (mSecurityPolicy.canRetrieveWindowContent(service)) { event.setConnection(service); @@ -574,6 +550,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } event.setSealed(true); listener.onAccessibilityEvent(event); + event.recycle(); if (DEBUG) { Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); } @@ -926,7 +903,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId) { + public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) { IAccessibilityInteractionConnection connection = null; synchronized (mLock) { final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); @@ -961,10 +938,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return null; } - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text) { + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow( + String text) { + return findAccessibilityNodeInfosByViewText(text, + mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID); + } + + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text, + int accessibilityWindowId, int accessibilityViewId) { IAccessibilityInteractionConnection connection = null; synchronized (mLock) { - final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); + final boolean permissionGranted = + mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId); if (permissionGranted) { connection = getConnectionToRetrievalAllowingWindowLocked(); } @@ -978,7 +963,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final long identityToken = Binder.clearCallingIdentity(); try { final int interactionId = mInteractionIdCounter.getAndIncrement(); - connection.findAccessibilityNodeInfosByViewText(text, interactionId, mCallback); + connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId, + interactionId, mCallback); List<AccessibilityNodeInfo> infos = mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId); if (infos != null) { @@ -1112,16 +1098,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED - | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; + | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED + | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; private int mRetrievalAlowingWindowId; + private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) { + // Send window changed event only for the retrieval allowing window. + return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED + || event.getWindowId() == mRetrievalAlowingWindowId); + } + public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) { - final int windowId = event.getSourceAccessibilityWindowId(); + final int windowId = event.getWindowId(); final int eventType = event.getEventType(); if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) != 0) { mRetrievalAlowingWindowId = windowId; - } else { + } else { event.setSource(null); } } diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index b754dbacf7c7..ab85b1464bc8 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -126,13 +126,15 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Unpack the config. // TODO: move constants into VpnBuilder. + int mtu = config.getInt("mtu", -1); String session = config.getString("session"); String addresses = config.getString("addresses"); String routes = config.getString("routes"); String dnsServers = config.getString("dnsServers"); - // Create interface and configure addresses and routes. - ParcelFileDescriptor descriptor = nativeConfigure(addresses, routes); + // Create and configure the interface. + ParcelFileDescriptor descriptor = + ParcelFileDescriptor.adoptFd(nativeEstablish(mtu, addresses, routes)); // Replace the interface and abort if it fails. try { @@ -250,7 +252,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } - private native ParcelFileDescriptor nativeConfigure(String addresses, String routes); + private native int nativeEstablish(int mtu, String addresses, String routes); private native String nativeGetName(int fd); private native void nativeReset(String name); private native int nativeCheck(String name); diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp index 374fd3b6d486..b3d38dc65b24 100644 --- a/services/jni/com_android_server_connectivity_Vpn.cpp +++ b/services/jni/com_android_server_connectivity_Vpn.cpp @@ -38,16 +38,10 @@ #include "jni.h" #include "JNIHelp.h" -#include "android_util_Binder.h" namespace android { -static inline void init_sockaddr(sockaddr *sa) { - ((sockaddr_in *)sa)->sin_family = AF_INET; - ((sockaddr_in *)sa)->sin_port = 0; -} - static inline in_addr_t *as_in_addr(sockaddr *sa) { return &((sockaddr_in *)sa)->sin_addr.s_addr; } @@ -65,7 +59,7 @@ static inline in6_addr *as_in6_addr(sockaddr_storage *ss) { #define SYSTEM_ERROR -1 #define BAD_ARGUMENT -2 -static int create_interface(char *name, int *index) +static int create_interface(int mtu, char *name, int *index) { int tun = open("/dev/tun", O_RDWR); int inet4 = socket(AF_INET, SOCK_DGRAM, 0); @@ -74,7 +68,7 @@ static int create_interface(char *name, int *index) memset(&ifr4, 0, sizeof(ifr4)); // Allocate interface. - ifr4.ifr_flags = IFF_TUN; + ifr4.ifr_flags = IFF_TUN | IFF_NO_PI; if (ioctl(tun, TUNSETIFF, &ifr4)) { LOGE("Cannot allocate TUN: %s", strerror(errno)); goto error; @@ -87,6 +81,13 @@ static int create_interface(char *name, int *index) goto error; } + // Set MTU if it is specified. + ifr4.ifr_mtu = mtu; + if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) { + LOGE("Cannot set MTU on %s: %s", ifr4.ifr_name, strerror(errno)); + goto error; + } + // Get interface index. if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno)); @@ -112,7 +113,7 @@ static int set_addresses(const char *name, int index, const char *addresses) ifreq ifr4; memset(&ifr4, 0, sizeof(ifr4)); strcpy(ifr4.ifr_name, name); - init_sockaddr(&ifr4.ifr_addr); + ifr4.ifr_addr.sa_family = AF_INET; in6_ifreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); @@ -190,9 +191,9 @@ static int set_routes(const char *name, int index, const char *routes) memset(&rt4, 0, sizeof(rt4)); rt4.rt_dev = (char *)name; rt4.rt_flags = RTF_UP; - init_sockaddr(&rt4.rt_dst); - init_sockaddr(&rt4.rt_genmask); - init_sockaddr(&rt4.rt_gateway); + rt4.rt_dst.sa_family = AF_INET; + rt4.rt_genmask.sa_family = AF_INET; + rt4.rt_gateway.sa_family = AF_INET; in6_rtmsg rt6; memset(&rt6, 0, sizeof(rt6)); @@ -223,7 +224,7 @@ static int set_routes(const char *name, int index, const char *routes) if (memcmp(&rt6.rtmsg_gateway, &in6addr_any, sizeof(in6addr_any))) { rt6.rtmsg_flags |= RTF_GATEWAY; } - if (ioctl(inet6, SIOCADDRT, &rt6)) { + if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) { count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; break; } @@ -241,7 +242,7 @@ static int set_routes(const char *name, int index, const char *routes) if (*as_in_addr(&rt4.rt_gateway)) { rt4.rt_flags |= RTF_GATEWAY; } - if (ioctl(inet4, SIOCADDRT, &rt4)) { + if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) { count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; break; } @@ -299,10 +300,10 @@ static int check_interface(const char *name) ifreq ifr4; strncpy(ifr4.ifr_name, name, IFNAMSIZ); + ifr4.ifr_flags = 0; if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) { LOGE("Cannot check %s: %s", name, strerror(errno)); - ifr4.ifr_flags = 0; } close(inet4); return ifr4.ifr_flags; @@ -328,15 +329,15 @@ static void throwException(JNIEnv *env, int error, const char *message) } } -static jobject configure(JNIEnv *env, jobject thiz, - jstring jAddresses, jstring jRoutes) +static jint establish(JNIEnv *env, jobject thiz, + jint mtu, jstring jAddresses, jstring jRoutes) { char name[IFNAMSIZ]; int index; - int tun = create_interface(name, &index); + int tun = create_interface(mtu, name, &index); if (tun < 0) { throwException(env, tun, "Cannot create interface"); - return NULL; + return -1; } LOGD("%s is created", name); @@ -370,12 +371,12 @@ static jobject configure(JNIEnv *env, jobject thiz, LOGD("Configured %d route(s) on %s", count, name); } - return newParcelFileDescriptor(env, jniCreateFileDescriptor(env, tun)); + return tun; error: close(tun); LOGD("%s is destroyed", name); - return NULL; + return -1; } static jstring getName(JNIEnv *env, jobject thiz, jint fd) @@ -434,7 +435,7 @@ static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName) //------------------------------------------------------------------------------ static JNINativeMethod gMethods[] = { - {"nativeConfigure", "(Ljava/lang/String;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", (void *)configure}, + {"nativeEstablish", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)establish}, {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName}, {"nativeReset", "(Ljava/lang/String;)V", (void *)reset}, {"nativeCheck", "(Ljava/lang/String;)I", (void *)check}, diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 9daaad8ea18c..c618263717f4 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -2,18 +2,18 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - clz.cpp.arm \ - DisplayHardware/DisplayHardware.cpp \ + Layer.cpp \ + LayerBase.cpp \ + LayerDim.cpp \ + DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ - DisplayHardware/HWComposer.cpp \ - GLExtensions.cpp \ - Layer.cpp \ - LayerBase.cpp \ - LayerDim.cpp \ - MessageQueue.cpp \ - SurfaceFlinger.cpp \ - TextureManager.cpp \ - Transform.cpp + DisplayHardware/HWComposer.cpp \ + GLExtensions.cpp \ + MessageQueue.cpp \ + SurfaceFlinger.cpp \ + SurfaceTextureLayer.cpp \ + Transform.cpp \ + LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 8df2b9217d0c..2bab6a8fe18b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -18,8 +18,9 @@ #include <stdint.h> #include <sys/types.h> -#include <cutils/properties.h> +#include <cutils/compiler.h> #include <cutils/native_handle.h> +#include <cutils/properties.h> #include <utils/Errors.h> #include <utils/Log.h> @@ -31,12 +32,12 @@ #include <surfaceflinger/Surface.h> #include "clz.h" +#include "DisplayHardware/DisplayHardware.h" +#include "DisplayHardware/HWComposer.h" #include "GLExtensions.h" #include "Layer.h" #include "SurfaceFlinger.h" -#include "DisplayHardware/DisplayHardware.h" -#include "DisplayHardware/HWComposer.h" - +#include "SurfaceTextureLayer.h" #define DEBUG_RESIZE 0 @@ -52,102 +53,83 @@ template <typename T> inline T min(T a, T b) { Layer::Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) : LayerBaseClient(flinger, display, client), + mTextureName(-1U), + mQueuedFrames(0), + mCurrentTransform(0), + mCurrentOpacity(true), mFormat(PIXEL_FORMAT_NONE), mGLExtensions(GLExtensions::getInstance()), - mNeedsBlending(true), + mOpaqueLayer(true), mNeedsDithering(false), mSecure(false), mProtectedByApp(false), - mTextureManager(), - mBufferManager(mTextureManager), - mWidth(0), mHeight(0), - mNeedsScaling(false), mFixedSize(false) -{ -} - -Layer::~Layer() + mFixedSize(false) { - // FIXME: must be called from the main UI thread - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - mBufferManager.destroy(dpy); - - // we can use getUserClientUnsafe here because we know we're - // single-threaded at that point. - sp<UserClient> ourClient(mUserClientRef.getUserClientUnsafe()); - if (ourClient != 0) { - ourClient->detachLayer(this); - } + mCurrentCrop.makeInvalid(); + glGenTextures(1, &mTextureName); } -void Layer::destroy() const { - mFlinger->destroyLayer(this); +void Layer::destroy(RefBase const* base) { + mFlinger->destroyLayer(static_cast<LayerBase const*>(base)); } -status_t Layer::setToken(const sp<UserClient>& userClient, - SharedClient* sharedClient, int32_t token) +void Layer::onFirstRef() { - sp<SharedBufferServer> lcblk = new SharedBufferServer( - sharedClient, token, mBufferManager.getDefaultBufferCount(), - getIdentity()); - - - sp<UserClient> ourClient(mUserClientRef.getClient()); - - /* - * Here it is guaranteed that userClient != ourClient - * (see UserClient::getTokenForSurface()). - * - * We release the token used by this surface in ourClient below. - * This should be safe to do so now, since this layer won't be attached - * to this client, it should be okay to reuse that id. - * - * If this causes problems, an other solution would be to keep a list - * of all the {UserClient, token} ever used and release them when the - * Layer is destroyed. - * - */ - - if (ourClient != 0) { - ourClient->detachLayer(this); - } - - status_t err = mUserClientRef.setToken(userClient, lcblk, token); - LOGE_IF(err != NO_ERROR, - "ClientRef::setToken(%p, %p, %u) failed", - userClient.get(), lcblk.get(), token); - - if (err == NO_ERROR) { - // we need to free the buffers associated with this surface - } + LayerBaseClient::onFirstRef(); + setDestroyer(this); - return err; + struct FrameQueuedListener : public SurfaceTexture::FrameAvailableListener { + FrameQueuedListener(Layer* layer) : mLayer(layer) { } + private: + wp<Layer> mLayer; + virtual void onFrameAvailable() { + sp<Layer> that(mLayer.promote()); + if (that != 0) { + that->onFrameQueued(); + } + } + }; + mSurfaceTexture = new SurfaceTextureLayer(mTextureName, this); + mSurfaceTexture->setFrameAvailableListener(new FrameQueuedListener(this)); + mSurfaceTexture->setSynchronousMode(true); + mSurfaceTexture->setBufferCountServer(2); } -int32_t Layer::getToken() const +Layer::~Layer() { - return mUserClientRef.getToken(); + glDeleteTextures(1, &mTextureName); } -sp<UserClient> Layer::getClient() const -{ - return mUserClientRef.getClient(); +void Layer::onFrameQueued() { + if (android_atomic_or(1, &mQueuedFrames) == 0) { + mFlinger->signalEvent(); + } } // called with SurfaceFlinger::mStateLock as soon as the layer is entered // in the purgatory list void Layer::onRemoved() { - ClientRef::Access sharedClient(mUserClientRef); - SharedBufferServer* lcblk(sharedClient.get()); - if (lcblk) { - // wake up the condition - lcblk->setStatus(NO_INIT); - } } -sp<LayerBaseClient::Surface> Layer::createSurface() const +sp<ISurface> Layer::createSurface() { - sp<Surface> sur(new SurfaceLayer(mFlinger, const_cast<Layer *>(this))); + class BSurface : public BnSurface, public LayerCleaner { + wp<const Layer> mOwner; + virtual sp<ISurfaceTexture> getSurfaceTexture() const { + sp<ISurfaceTexture> res; + sp<const Layer> that( mOwner.promote() ); + if (that != NULL) { + res = that->mSurfaceTexture; + } + return res; + } + public: + BSurface(const sp<SurfaceFlinger>& flinger, + const sp<Layer>& layer) + : LayerCleaner(flinger, layer), mOwner(layer) { } + }; + sp<ISurface> sur(new BSurface(mFlinger, this)); return sur; } @@ -175,17 +157,14 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, const uint32_t hwFlags = hw.getFlags(); mFormat = format; - mWidth = w; - mHeight = h; - - mReqFormat = format; - mReqWidth = w; - mReqHeight = h; mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; mProtectedByApp = (flags & ISurfaceComposer::eProtectedByApp) ? true : false; - mNeedsBlending = (info.h_alpha - info.l_alpha) > 0 && - (flags & ISurfaceComposer::eOpaque) == 0; + mOpaqueLayer = (flags & ISurfaceComposer::eOpaque); + mCurrentOpacity = getOpacityForFormat(format); + + mSurfaceTexture->setDefaultBufferSize(w, h); + mSurfaceTexture->setDefaultBufferFormat(format); // we use the red index int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); @@ -216,10 +195,12 @@ void Layer::setGeometry(hwc_layer_t* hwcl) return; } - Transform tr(Transform(mOrientation) * Transform(mBufferTransform)); + // FIXME: shouldn't we take the state's transform into account here? + + Transform tr(Transform(mOrientation) * Transform(mCurrentTransform)); hwcl->transform = tr.getOrientation(); - if (needsBlending()) { + if (!isOpaque()) { hwcl->blending = mPremultipliedAlpha ? HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE; } @@ -236,7 +217,7 @@ void Layer::setGeometry(hwc_layer_t* hwcl) } void Layer::setPerFrameData(hwc_layer_t* hwcl) { - sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer()); + const sp<GraphicBuffer>& buffer(mActiveBuffer); if (buffer == NULL) { // this can happen if the client never drew into this layer yet, // or if we ran out of memory. In that case, don't let @@ -247,11 +228,11 @@ void Layer::setPerFrameData(hwc_layer_t* hwcl) { } hwcl->handle = buffer->handle; - if (!mBufferCrop.isEmpty()) { - hwcl->sourceCrop.left = mBufferCrop.left; - hwcl->sourceCrop.top = mBufferCrop.top; - hwcl->sourceCrop.right = mBufferCrop.right; - hwcl->sourceCrop.bottom = mBufferCrop.bottom; + if (isCropped()) { + hwcl->sourceCrop.left = mCurrentCrop.left; + hwcl->sourceCrop.top = mCurrentCrop.top; + hwcl->sourceCrop.right = mCurrentCrop.right; + hwcl->sourceCrop.bottom = mCurrentCrop.bottom; } else { hwcl->sourceCrop.left = 0; hwcl->sourceCrop.top = 0; @@ -260,51 +241,12 @@ void Layer::setPerFrameData(hwc_layer_t* hwcl) { } } -void Layer::reloadTexture(const Region& dirty) -{ - sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer()); - if (buffer == NULL) { - // this situation can happen if we ran out of memory for instance. - // not much we can do. continue to use whatever texture was bound - // to this context. - return; - } - - if (mGLExtensions.haveDirectTexture()) { - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) { - // not sure what we can do here... - goto slowpath; - } - } else { -slowpath: - GGLSurface t; - if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) { - status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); - LOGE_IF(res, "error %d (%s) locking buffer %p", - res, strerror(res), buffer.get()); - if (res == NO_ERROR) { - mBufferManager.loadTexture(dirty, t); - buffer->unlock(); - } - } else { - // we can't do anything - } - } -} - -void Layer::drawForSreenShot() const -{ - const bool currentFiltering = mNeedsFiltering; - const_cast<Layer*>(this)->mNeedsFiltering = true; - LayerBase::drawForSreenShot(); - const_cast<Layer*>(this)->mNeedsFiltering = currentFiltering; +static inline uint16_t pack565(int r, int g, int b) { + return (r<<11)|(g<<5)|b; } - void Layer::onDraw(const Region& clip) const { - Texture tex(mBufferManager.getActiveTexture()); - if (tex.name == -1LU) { + if (CC_UNLIKELY(mActiveBuffer == 0)) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with // SurfaceView because the WindowManager can't know when the client @@ -330,7 +272,25 @@ void Layer::onDraw(const Region& clip) const } return; } - drawWithOpenGL(clip, tex); + + GLenum target = mSurfaceTexture->getCurrentTextureTarget(); + glBindTexture(target, mTextureName); + if (getFiltering() || needsFiltering() || isFixedSize() || isCropped()) { + // TODO: we could be more subtle with isFixedSize() + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glEnable(target); + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(mTextureMatrix); + glMatrixMode(GL_MODELVIEW); + + drawWithOpenGL(clip); + + glDisable(target); } // As documented in libhardware header, formats in the range @@ -340,186 +300,37 @@ void Layer::onDraw(const Region& clip) const // hardware.h, instead of using hard-coded values here. #define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) -bool Layer::needsBlending(const sp<GraphicBuffer>& buffer) const +bool Layer::getOpacityForFormat(uint32_t format) { - // If buffers where set with eOpaque flag, all buffers are known to - // be opaque without having to check their actual format - if (mNeedsBlending && buffer != NULL) { - PixelFormat format = buffer->getPixelFormat(); - - if (HARDWARE_IS_DEVICE_FORMAT(format)) { - return false; - } - - PixelFormatInfo info; - status_t err = getPixelFormatInfo(format, &info); - if (!err && info.h_alpha <= info.l_alpha) { - return false; - } + if (HARDWARE_IS_DEVICE_FORMAT(format)) { + return true; } - - // Return opacity as determined from flags and format options - // passed to setBuffers() - return mNeedsBlending; + PixelFormatInfo info; + status_t err = getPixelFormatInfo(PixelFormat(format), &info); + // in case of error (unknown format), we assume no blending + return (err || info.h_alpha <= info.l_alpha); } -bool Layer::needsBlending() const -{ - if (mBufferManager.hasActiveBuffer()) { - return needsBlending(mBufferManager.getActiveBuffer()); - } - return mNeedsBlending; -} - -bool Layer::needsFiltering() const +bool Layer::isOpaque() const { - if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { - // if our buffer is not the same size than ourselves, - // we need filtering. - Mutex::Autolock _l(mLock); - if (mNeedsScaling) - return true; - } - return LayerBase::needsFiltering(); + // if we don't have a buffer yet, we're translucent regardless of the + // layer's opaque flag. + if (mActiveBuffer == 0) + return false; + + // if the layer has the opaque flag, then we're always opaque, + // otherwise we use the current buffer's format. + return mOpaqueLayer || mCurrentOpacity; } bool Layer::isProtected() const { - sp<GraphicBuffer> activeBuffer(mBufferManager.getActiveBuffer()); + const sp<GraphicBuffer>& activeBuffer(mActiveBuffer); return (activeBuffer != 0) && (activeBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); } -status_t Layer::setBufferCount(int bufferCount) -{ - ClientRef::Access sharedClient(mUserClientRef); - SharedBufferServer* lcblk(sharedClient.get()); - if (!lcblk) { - // oops, the client is already gone - return DEAD_OBJECT; - } - - // NOTE: lcblk->resize() is protected by an internal lock - status_t err = lcblk->resize(bufferCount); - if (err == NO_ERROR) { - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - mBufferManager.resize(bufferCount, mFlinger, dpy); - } - - return err; -} - -sp<GraphicBuffer> Layer::requestBuffer(int index, - uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat, - uint32_t usage) -{ - sp<GraphicBuffer> buffer; - - if (int32_t(reqWidth | reqHeight | reqFormat) < 0) - return buffer; - - if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight)) - return buffer; - - // this ensures our client doesn't go away while we're accessing - // the shared area. - ClientRef::Access sharedClient(mUserClientRef); - SharedBufferServer* lcblk(sharedClient.get()); - if (!lcblk) { - // oops, the client is already gone - return buffer; - } - - /* - * This is called from the client's Surface::dequeue(). This can happen - * at any time, especially while we're in the middle of using the - * buffer 'index' as our front buffer. - */ - - status_t err = NO_ERROR; - uint32_t w, h, f; - { // scope for the lock - Mutex::Autolock _l(mLock); - - // zero means default - const bool fixedSize = reqWidth && reqHeight; - if (!reqFormat) reqFormat = mFormat; - if (!reqWidth) reqWidth = mWidth; - if (!reqHeight) reqHeight = mHeight; - - w = reqWidth; - h = reqHeight; - f = reqFormat; - - if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) || - (reqFormat != mReqFormat)) { - mReqWidth = reqWidth; - mReqHeight = reqHeight; - mReqFormat = reqFormat; - mFixedSize = fixedSize; - mNeedsScaling = mWidth != mReqWidth || mHeight != mReqHeight; - - lcblk->reallocateAllExcept(index); - } - } - - // here we have to reallocate a new buffer because the buffer could be - // used as the front buffer, or by a client in our process - // (eg: status bar), and we can't release the handle under its feet. - const uint32_t effectiveUsage = getEffectiveUsage(usage); - buffer = new GraphicBuffer(w, h, f, effectiveUsage); - err = buffer->initCheck(); - - if (err || buffer->handle == 0) { - GraphicBuffer::dumpAllocationsToSystemLog(); - LOGE_IF(err || buffer->handle == 0, - "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d failed (%s)", - this, index, w, h, strerror(-err)); - } else { - LOGD_IF(DEBUG_RESIZE, - "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d, handle=%p", - this, index, w, h, buffer->handle); - } - - if (err == NO_ERROR && buffer->handle != 0) { - Mutex::Autolock _l(mLock); - mBufferManager.attachBuffer(index, buffer); - } - return buffer; -} - -uint32_t Layer::getEffectiveUsage(uint32_t usage) const -{ - /* - * buffers used for software rendering, but h/w composition - * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE - * - * buffers used for h/w rendering and h/w composition - * are allocated with HW_RENDER | HW_TEXTURE - * - * buffers used with h/w rendering and either NPOT or no egl_image_ext - * are allocated with SW_READ_RARELY | HW_RENDER - * - */ - - if (mSecure) { - // secure buffer, don't store it into the GPU - usage = GraphicBuffer::USAGE_SW_READ_OFTEN | - GraphicBuffer::USAGE_SW_WRITE_OFTEN; - } else { - // it's allowed to modify the usage flags here, but generally - // the requested flags should be honored. - // request EGLImage for all buffers - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } - if (mProtectedByApp) { - // need a hardware-protected path to external video sink - usage |= GraphicBuffer::USAGE_PROTECTED; - } - return usage; -} - uint32_t Layer::doTransaction(uint32_t flags) { const Layer::State& front(drawingState()); @@ -531,10 +342,12 @@ uint32_t Layer::doTransaction(uint32_t flags) if (sizeChanged) { // the size changed, we need to ask our client to request a new buffer LOGD_IF(DEBUG_RESIZE, - "resize (layer=%p), requested (%dx%d), drawing (%d,%d)", + "resize (layer=%p), requested (%dx%d), drawing (%d,%d), " + "fixedSize=%d", this, int(temp.requested_w), int(temp.requested_h), - int(front.requested_w), int(front.requested_h)); + int(front.requested_w), int(front.requested_h), + isFixedSize()); if (!isFixedSize()) { // we're being resized and there is a freeze display request, @@ -557,17 +370,7 @@ uint32_t Layer::doTransaction(uint32_t flags) // record the new size, form this point on, when the client request // a buffer, it'll get the new size. - setBufferSize(temp.requested_w, temp.requested_h); - - ClientRef::Access sharedClient(mUserClientRef); - SharedBufferServer* lcblk(sharedClient.get()); - if (lcblk) { - // all buffers need reallocation - lcblk->reallocateAll(); - } - } else { - // record the new size - setBufferSize(temp.requested_w, temp.requested_h); + mSurfaceTexture->setDefaultBufferSize(temp.requested_w, temp.requested_h); } } @@ -582,16 +385,19 @@ uint32_t Layer::doTransaction(uint32_t flags) return LayerBase::doTransaction(flags); } -void Layer::setBufferSize(uint32_t w, uint32_t h) { +bool Layer::isFixedSize() const { Mutex::Autolock _l(mLock); - mWidth = w; - mHeight = h; - mNeedsScaling = mWidth != mReqWidth || mHeight != mReqHeight; + return mFixedSize; } -bool Layer::isFixedSize() const { +void Layer::setFixedSize(bool fixedSize) +{ Mutex::Autolock _l(mLock); - return mFixedSize; + mFixedSize = fixedSize; +} + +bool Layer::isCropped() const { + return !mCurrentCrop.isEmpty(); } // ---------------------------------------------------------------------------- @@ -600,61 +406,48 @@ bool Layer::isFixedSize() const { void Layer::lockPageFlip(bool& recomputeVisibleRegions) { - ClientRef::Access sharedClient(mUserClientRef); - SharedBufferServer* lcblk(sharedClient.get()); - if (!lcblk) { - // client died - recomputeVisibleRegions = true; - return; - } - - ssize_t buf = lcblk->retireAndLock(); - if (buf == NOT_ENOUGH_DATA) { - // NOTE: This is not an error, it simply means there is nothing to - // retire. The buffer is locked because we will use it - // for composition later in the loop - return; - } - - if (buf < NO_ERROR) { - LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); - mPostedDirtyRegion.clear(); - return; - } - - // we retired a buffer, which becomes the new front buffer + if (android_atomic_and(0, &mQueuedFrames)) { + if (mSurfaceTexture->updateTexImage() < NO_ERROR) { + // something happened! + recomputeVisibleRegions = true; + return; + } - const bool noActiveBuffer = !mBufferManager.hasActiveBuffer(); - const bool activeBlending = - noActiveBuffer ? true : needsBlending(mBufferManager.getActiveBuffer()); + // signal another event if we have more frames waiting + if (mSurfaceTexture->getQueuedCount()) { + if (android_atomic_or(1, &mQueuedFrames) == 0) { + mFlinger->signalEvent(); + } + } - if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { - LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); - mPostedDirtyRegion.clear(); - return; - } + mActiveBuffer = mSurfaceTexture->getCurrentBuffer(); + mSurfaceTexture->getTransformMatrix(mTextureMatrix); - if (noActiveBuffer) { - // we didn't have an active buffer, we need to recompute - // our visible region - recomputeVisibleRegions = true; - } + const Rect crop(mSurfaceTexture->getCurrentCrop()); + const uint32_t transform(mSurfaceTexture->getCurrentTransform()); + if ((crop != mCurrentCrop) || (transform != mCurrentTransform)) { + mCurrentCrop = crop; + mCurrentTransform = transform; + mFlinger->invalidateHwcGeometry(); + } - sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); - if (newFrontBuffer != NULL) { - if (!noActiveBuffer && activeBlending != needsBlending(newFrontBuffer)) { - // new buffer has different opacity than previous active buffer, need - // to recompute visible regions accordingly + const bool opacity(getOpacityForFormat(mActiveBuffer->format)); + if (opacity != mCurrentOpacity) { + mCurrentOpacity = opacity; recomputeVisibleRegions = true; } - // get the dirty region - // compute the posted region - const Region dirty(lcblk->getDirtyRegion(buf)); - mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); + const GLenum target(mSurfaceTexture->getCurrentTextureTarget()); + glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // update the layer size and release freeze-lock const Layer::State& front(drawingState()); + + // FIXME: mPostedDirtyRegion = dirty & bounds + mPostedDirtyRegion.set(front.w, front.h); + + sp<GraphicBuffer> newFrontBuffer(mActiveBuffer); if ((newFrontBuffer->getWidth() == front.requested_w && newFrontBuffer->getHeight() == front.requested_h) || isFixedSize()) @@ -685,35 +478,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // we now have the correct size, unfreeze the screen mFreezeLock.clear(); } - - // get the crop region - setBufferCrop( lcblk->getCrop(buf) ); - - // get the transformation - setBufferTransform( lcblk->getTransform(buf) ); - - } else { - // this should not happen unless we ran out of memory while - // allocating the buffer. we're hoping that things will get back - // to normal the next time the app tries to draw into this buffer. - // meanwhile, pretend the screen didn't update. - mPostedDirtyRegion.clear(); - } - - if (lcblk->getQueuedCount()) { - // signal an event if we have more buffers waiting - mFlinger->signalEvent(); } - - /* a buffer was posted, so we need to call reloadTexture(), which - * will update our internal data structures (eg: EGLImageKHR or - * texture names). we need to do this even if mPostedDirtyRegion is - * empty -- it's orthogonal to the fact that a new buffer was posted, - * for instance, a degenerate case could be that the user did an empty - * update but repainted the buffer with appropriate content (after a - * resize for instance). - */ - reloadTexture( mPostedDirtyRegion ); } void Layer::unlockPageFlip( @@ -746,329 +511,36 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const { LayerBaseClient::dump(result, buffer, SIZE); - ClientRef::Access sharedClient(mUserClientRef); - SharedBufferServer* lcblk(sharedClient.get()); - uint32_t totalTime = 0; - if (lcblk) { - SharedBufferStack::Statistics stats = lcblk->getStats(); - totalTime= stats.totalTime; - result.append( lcblk->dump(" ") ); - } - - sp<const GraphicBuffer> buf0(getBuffer(0)); - sp<const GraphicBuffer> buf1(getBuffer(1)); - uint32_t w0=0, h0=0, s0=0; - uint32_t w1=0, h1=0, s1=0; + sp<const GraphicBuffer> buf0(mActiveBuffer); + uint32_t w0=0, h0=0, s0=0, f0=0; if (buf0 != 0) { w0 = buf0->getWidth(); h0 = buf0->getHeight(); s0 = buf0->getStride(); - } - if (buf1 != 0) { - w1 = buf1->getWidth(); - h1 = buf1->getHeight(); - s1 = buf1->getStride(); + f0 = buf0->format; } snprintf(buffer, SIZE, " " - "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," - " freezeLock=%p, dq-q-time=%u us\n", - mFormat, w0, h0, s0, w1, h1, s1, - getFreezeLock().get(), totalTime); + "format=%2d, activeBuffer=[%3ux%3u:%3u,%3u]," + " freezeLock=%p, queued-frames=%d\n", + mFormat, w0, h0, s0,f0, + getFreezeLock().get(), mQueuedFrames); result.append(buffer); -} - -// --------------------------------------------------------------------------- - -Layer::ClientRef::ClientRef() - : mControlBlock(0), mToken(-1) { -} - -Layer::ClientRef::~ClientRef() { -} -int32_t Layer::ClientRef::getToken() const { - Mutex::Autolock _l(mLock); - return mToken; -} - -sp<UserClient> Layer::ClientRef::getClient() const { - Mutex::Autolock _l(mLock); - return mUserClient.promote(); -} - -status_t Layer::ClientRef::setToken(const sp<UserClient>& uc, - const sp<SharedBufferServer>& sharedClient, int32_t token) { - Mutex::Autolock _l(mLock); - - { // scope for strong mUserClient reference - sp<UserClient> userClient(mUserClient.promote()); - if (userClient != 0 && mControlBlock != 0) { - mControlBlock->setStatus(NO_INIT); - } + if (mSurfaceTexture != 0) { + mSurfaceTexture->dump(result, " ", buffer, SIZE); } - - mUserClient = uc; - mToken = token; - mControlBlock = sharedClient; - return NO_ERROR; -} - -sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const { - return mUserClient.promote(); -} - -// this class gives us access to SharedBufferServer safely -// it makes sure the UserClient (and its associated shared memory) -// won't go away while we're accessing it. -Layer::ClientRef::Access::Access(const ClientRef& ref) - : mControlBlock(0) -{ - Mutex::Autolock _l(ref.mLock); - mUserClientStrongRef = ref.mUserClient.promote(); - if (mUserClientStrongRef != 0) - mControlBlock = ref.mControlBlock; } -Layer::ClientRef::Access::~Access() -{ -} - -// --------------------------------------------------------------------------- - -Layer::BufferManager::BufferManager(TextureManager& tm) - : mNumBuffers(NUM_BUFFERS), mTextureManager(tm), - mActiveBufferIndex(-1), mFailover(false) -{ -} - -Layer::BufferManager::~BufferManager() -{ -} - -status_t Layer::BufferManager::resize(size_t size, - const sp<SurfaceFlinger>& flinger, EGLDisplay dpy) -{ - Mutex::Autolock _l(mLock); - - if (size < mNumBuffers) { - // If there is an active texture, move it into slot 0 if needed - if (mActiveBufferIndex > 0) { - BufferData activeBufferData = mBufferData[mActiveBufferIndex]; - mBufferData[mActiveBufferIndex] = mBufferData[0]; - mBufferData[0] = activeBufferData; - mActiveBufferIndex = 0; - } - - // Free the buffers that are no longer needed. - for (size_t i = size; i < mNumBuffers; i++) { - mBufferData[i].buffer = 0; - - // Create a message to destroy the textures on SurfaceFlinger's GL - // thread. - class MessageDestroyTexture : public MessageBase { - Image mTexture; - EGLDisplay mDpy; - public: - MessageDestroyTexture(const Image& texture, EGLDisplay dpy) - : mTexture(texture), mDpy(dpy) { } - virtual bool handler() { - status_t err = Layer::BufferManager::destroyTexture( - &mTexture, mDpy); - LOGE_IF(err<0, "error destroying texture: %d (%s)", - mTexture.name, strerror(-err)); - return true; // XXX: err == 0; ???? - } - }; - - MessageDestroyTexture *msg = new MessageDestroyTexture( - mBufferData[i].texture, dpy); - - // Don't allow this texture to be cleaned up by - // BufferManager::destroy. - mBufferData[i].texture.name = -1U; - mBufferData[i].texture.image = EGL_NO_IMAGE_KHR; - - // Post the message to the SurfaceFlinger object. - flinger->postMessageAsync(msg); - } - } - - mNumBuffers = size; - return NO_ERROR; -} - -// only for debugging -sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const { - return mBufferData[index].buffer; -} - -status_t Layer::BufferManager::setActiveBufferIndex(size_t index) { - BufferData const * const buffers = mBufferData; - Mutex::Autolock _l(mLock); - mActiveBuffer = buffers[index].buffer; - mActiveBufferIndex = index; - return NO_ERROR; -} - -size_t Layer::BufferManager::getActiveBufferIndex() const { - return mActiveBufferIndex; -} - -Texture Layer::BufferManager::getActiveTexture() const { - Texture res; - if (mFailover || mActiveBufferIndex<0) { - res = mFailoverTexture; - } else { - static_cast<Image&>(res) = mBufferData[mActiveBufferIndex].texture; - } - return res; -} - -sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const { - return mActiveBuffer; -} - -bool Layer::BufferManager::hasActiveBuffer() const { - return mActiveBufferIndex >= 0; -} - -sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index) -{ - BufferData* const buffers = mBufferData; - sp<GraphicBuffer> buffer; - Mutex::Autolock _l(mLock); - buffer = buffers[index].buffer; - buffers[index].buffer = 0; - return buffer; -} - -status_t Layer::BufferManager::attachBuffer(size_t index, - const sp<GraphicBuffer>& buffer) -{ - BufferData* const buffers = mBufferData; - Mutex::Autolock _l(mLock); - buffers[index].buffer = buffer; - buffers[index].texture.dirty = true; - return NO_ERROR; -} - -status_t Layer::BufferManager::destroy(EGLDisplay dpy) -{ - BufferData* const buffers = mBufferData; - size_t num; - { // scope for the lock - Mutex::Autolock _l(mLock); - num = mNumBuffers; - for (size_t i=0 ; i<num ; i++) { - buffers[i].buffer = 0; - } - } - for (size_t i=0 ; i<num ; i++) { - destroyTexture(&buffers[i].texture, dpy); - } - destroyTexture(&mFailoverTexture, dpy); - return NO_ERROR; -} - -status_t Layer::BufferManager::initEglImage(EGLDisplay dpy, - const sp<GraphicBuffer>& buffer) -{ - status_t err = NO_INIT; - ssize_t index = mActiveBufferIndex; - if (index >= 0) { - if (!mFailover) { - { - // Without that lock, there is a chance of race condition - // where while composing a specific index, requestBuf - // with the same index can be executed and touch the same data - // that is being used in initEglImage. - // (e.g. dirty flag in texture) - Mutex::Autolock _l(mLock); - Image& texture(mBufferData[index].texture); - err = mTextureManager.initEglImage(&texture, dpy, buffer); - } - // if EGLImage fails, we switch to regular texture mode, and we - // free all resources associated with using EGLImages. - if (err == NO_ERROR) { - mFailover = false; - destroyTexture(&mFailoverTexture, dpy); - } else { - mFailover = true; - const size_t num = mNumBuffers; - for (size_t i=0 ; i<num ; i++) { - destroyTexture(&mBufferData[i].texture, dpy); - } - } - } else { - // we failed once, don't try again - err = BAD_VALUE; - } - } - return err; -} - -status_t Layer::BufferManager::loadTexture( - const Region& dirty, const GGLSurface& t) -{ - return mTextureManager.loadTexture(&mFailoverTexture, dirty, t); -} - -status_t Layer::BufferManager::destroyTexture(Image* tex, EGLDisplay dpy) -{ - if (tex->name != -1U) { - glDeleteTextures(1, &tex->name); - tex->name = -1U; - } - if (tex->image != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(dpy, tex->image); - tex->image = EGL_NO_IMAGE_KHR; - } - return NO_ERROR; -} - -// --------------------------------------------------------------------------- - -Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, - const sp<Layer>& owner) - : Surface(flinger, owner->getIdentity(), owner) -{ -} - -Layer::SurfaceLayer::~SurfaceLayer() -{ -} - -sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) -{ - sp<GraphicBuffer> buffer; - sp<Layer> owner(getOwner()); - if (owner != 0) { - /* - * requestBuffer() cannot be called from the main thread - * as it could cause a dead-lock, since it may have to wait - * on conditions updated my the main thread. - */ - buffer = owner->requestBuffer(index, w, h, format, usage); - } - return buffer; -} - -status_t Layer::SurfaceLayer::setBufferCount(int bufferCount) +uint32_t Layer::getEffectiveUsage(uint32_t usage) const { - status_t err = DEAD_OBJECT; - sp<Layer> owner(getOwner()); - if (owner != 0) { - /* - * setBufferCount() cannot be called from the main thread - * as it could cause a dead-lock, since it may have to wait - * on conditions updated my the main thread. - */ - err = owner->setBufferCount(bufferCount); + // TODO: should we do something special if mSecure is set? + if (mProtectedByApp) { + // need a hardware-protected path to external video sink + usage |= GraphicBuffer::USAGE_PROTECTED; } - return err; + return usage; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 278d64e9738c..e3fc13ddeef6 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -20,9 +20,11 @@ #include <stdint.h> #include <sys/types.h> +#include <gui/SurfaceTexture.h> + +#include <pixelflinger/pixelflinger.h> #include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> -#include <pixelflinger/pixelflinger.h> #include <EGL/egl.h> #include <EGL/eglext.h> @@ -30,8 +32,8 @@ #include <GLES/glext.h> #include "LayerBase.h" +#include "SurfaceTextureLayer.h" #include "Transform.h" -#include "TextureManager.h" namespace android { @@ -40,11 +42,10 @@ namespace android { class FreezeLock; class Client; class GLExtensions; -class UserClient; // --------------------------------------------------------------------------- -class Layer : public LayerBaseClient +class Layer : public LayerBaseClient, private RefBase::Destroyer { public: Layer(SurfaceFlinger* flinger, DisplayID display, @@ -58,164 +59,59 @@ public: status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags=0); - // associate a UserClient to this Layer - status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx); - int32_t getToken() const; - sp<UserClient> getClient() const; - // Set this Layer's buffers size - void setBufferSize(uint32_t w, uint32_t h); bool isFixedSize() const; // LayerBase interface virtual void setGeometry(hwc_layer_t* hwcl); virtual void setPerFrameData(hwc_layer_t* hwcl); - virtual void drawForSreenShot() const; virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t transactionFlags); virtual void lockPageFlip(bool& recomputeVisibleRegions); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); - virtual bool needsBlending(const sp<GraphicBuffer>& buffer) const; - virtual bool needsBlending() const; + virtual bool isOpaque() const; virtual bool needsDithering() const { return mNeedsDithering; } - virtual bool needsFiltering() const; virtual bool isSecure() const { return mSecure; } virtual bool isProtected() const; - virtual sp<Surface> createSurface() const; virtual void onRemoved(); // only for debugging - inline sp<GraphicBuffer> getBuffer(int i) const { - return mBufferManager.getBuffer(i); } - // only for debugging - inline const sp<FreezeLock>& getFreezeLock() const { - return mFreezeLock; } + inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } protected: - virtual void destroy() const; + virtual void destroy(RefBase const* base); + virtual void onFirstRef(); virtual void dump(String8& result, char* scratch, size_t size) const; private: - void reloadTexture(const Region& dirty); + friend class SurfaceTextureLayer; + void onFrameQueued(); + virtual sp<ISurface> createSurface(); uint32_t getEffectiveUsage(uint32_t usage) const; - sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage); - status_t setBufferCount(int bufferCount); - - // ----------------------------------------------------------------------- - - class SurfaceLayer : public LayerBaseClient::Surface { - public: - SurfaceLayer(const sp<SurfaceFlinger>& flinger, const sp<Layer>& owner); - ~SurfaceLayer(); - private: - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage); - virtual status_t setBufferCount(int bufferCount); - sp<Layer> getOwner() const { - return static_cast<Layer*>(Surface::getOwner().get()); - } - }; - friend class SurfaceLayer; + void setFixedSize(bool fixedSize); + bool isCropped() const; + static bool getOpacityForFormat(uint32_t format); // ----------------------------------------------------------------------- - class ClientRef { - ClientRef(const ClientRef& rhs); - ClientRef& operator = (const ClientRef& rhs); - mutable Mutex mLock; - // binder thread, page-flip thread - sp<SharedBufferServer> mControlBlock; - wp<UserClient> mUserClient; - int32_t mToken; - public: - ClientRef(); - ~ClientRef(); - int32_t getToken() const; - sp<UserClient> getClient() const; - status_t setToken(const sp<UserClient>& uc, - const sp<SharedBufferServer>& sharedClient, int32_t token); - sp<UserClient> getUserClientUnsafe() const; - class Access { - Access(const Access& rhs); - Access& operator = (const Access& rhs); - sp<UserClient> mUserClientStrongRef; - sp<SharedBufferServer> mControlBlock; - public: - Access(const ClientRef& ref); - ~Access(); - inline SharedBufferServer* get() const { return mControlBlock.get(); } - }; - friend class Access; - }; - - // ----------------------------------------------------------------------- - - class BufferManager { - static const size_t NUM_BUFFERS = 2; - struct BufferData { - sp<GraphicBuffer> buffer; - Image texture; - }; - // this lock protect mBufferData[].buffer but since there - // is very little contention, we have only one like for - // the whole array, we also use it to protect mNumBuffers. - mutable Mutex mLock; - BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX]; - size_t mNumBuffers; - Texture mFailoverTexture; - TextureManager& mTextureManager; - ssize_t mActiveBufferIndex; - sp<GraphicBuffer> mActiveBuffer; - bool mFailover; - static status_t destroyTexture(Image* tex, EGLDisplay dpy); - - public: - static size_t getDefaultBufferCount() { return NUM_BUFFERS; } - BufferManager(TextureManager& tm); - ~BufferManager(); - - // detach/attach buffer from/to given index - sp<GraphicBuffer> detachBuffer(size_t index); - status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer); - // resize the number of active buffers - status_t resize(size_t size, const sp<SurfaceFlinger>& flinger, - EGLDisplay dpy); - - // ---------------------------------------------- - // must be called from GL thread - - // set/get active buffer index - status_t setActiveBufferIndex(size_t index); - size_t getActiveBufferIndex() const; - // return the active buffer - sp<GraphicBuffer> getActiveBuffer() const; - // return wether we have an active buffer - bool hasActiveBuffer() const; - // return the active texture (or fail-over) - Texture getActiveTexture() const; - // frees resources associated with all buffers - status_t destroy(EGLDisplay dpy); - // load bitmap data into the active buffer - status_t loadTexture(const Region& dirty, const GGLSurface& t); - // make active buffer an EGLImage if needed - status_t initEglImage(EGLDisplay dpy, - const sp<GraphicBuffer>& buffer); - - // ---------------------------------------------- - // only for debugging - sp<GraphicBuffer> getBuffer(size_t index) const; - }; - - // ----------------------------------------------------------------------- + // constants + sp<SurfaceTextureLayer> mSurfaceTexture; + GLuint mTextureName; // thread-safe - ClientRef mUserClientRef; + volatile int32_t mQueuedFrames; + + // main thread + sp<GraphicBuffer> mActiveBuffer; + GLfloat mTextureMatrix[16]; + Rect mCurrentCrop; + uint32_t mCurrentTransform; + bool mCurrentOpacity; // constants PixelFormat mFormat; const GLExtensions& mGLExtensions; - bool mNeedsBlending; + bool mOpaqueLayer; bool mNeedsDithering; // page-flip thread (currently main thread) @@ -226,18 +122,8 @@ private: // page-flip thread and transaction thread (currently main thread) sp<FreezeLock> mFreezeLock; - // see threading usage in declaration - TextureManager mTextureManager; - BufferManager mBufferManager; - // binder thread, transaction thread mutable Mutex mLock; - uint32_t mWidth; - uint32_t mHeight; - uint32_t mReqWidth; - uint32_t mReqHeight; - uint32_t mReqFormat; - bool mNeedsScaling; bool mFixedSize; }; diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 022f25145297..bcd8c8332590 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -32,8 +32,6 @@ #include "LayerBase.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" -#include "TextureManager.h" - namespace android { @@ -44,7 +42,7 @@ int32_t LayerBase::sSequence = 1; LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) : dpy(display), contentDirty(false), sequence(uint32_t(android_atomic_inc(&sSequence))), - mFlinger(flinger), + mFlinger(flinger), mFiltering(false), mNeedsFiltering(false), mOrientation(0), mLeft(0), mTop(0), @@ -54,8 +52,6 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); - mBufferCrop.makeInvalid(); - mBufferTransform = 0; } LayerBase::~LayerBase() @@ -310,6 +306,16 @@ void LayerBase::setPerFrameData(hwc_layer_t* hwcl) { hwcl->handle = NULL; } +void LayerBase::setFiltering(bool filtering) +{ + mFiltering = filtering; +} + +bool LayerBase::getFiltering() const +{ + return mFiltering; +} + void LayerBase::draw(const Region& clip) const { // reset GL state @@ -318,10 +324,12 @@ void LayerBase::draw(const Region& clip) const onDraw(clip); } -void LayerBase::drawForSreenShot() const +void LayerBase::drawForSreenShot() { const DisplayHardware& hw(graphicPlane(0).displayHardware()); + setFiltering(true); onDraw( Region(hw.bounds()) ); + setFiltering(false); } void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, @@ -332,8 +340,12 @@ void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, const uint32_t fbHeight = hw.getHeight(); glColor4f(red,green,blue,alpha); - TextureManager::deactivateTextures(); - +#if defined(GL_OES_EGL_image_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif + glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_DITHER); @@ -354,24 +366,11 @@ void LayerBase::clearWithOpenGL(const Region& clip) const clearWithOpenGL(clip,0,0,0,0); } -template <typename T> -static inline -void swap(T& a, T& b) { - T t(a); - a = b; - b = t; -} - -void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const +void LayerBase::drawWithOpenGL(const Region& clip) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); const State& s(drawingState()); - - // bind our texture - TextureManager::activateTexture(texture, needsFiltering()); - uint32_t width = texture.width; - uint32_t height = texture.height; GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; if (UNLIKELY(s.alpha < 0xFF)) { @@ -387,7 +386,7 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } else { glColor4f(1, 1, 1, 1); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - if (needsBlending()) { + if (!isOpaque()) { glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); } else { @@ -395,86 +394,20 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } } - /* - * compute texture coordinates - * here, we handle NPOT, cropping and buffer transformations - */ - - GLfloat cl, ct, cr, cb; - if (!mBufferCrop.isEmpty()) { - // source is cropped - const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width; - const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height; - cl = mBufferCrop.left * us; - ct = mBufferCrop.top * vs; - cr = mBufferCrop.right * us; - cb = mBufferCrop.bottom * vs; - } else { - cl = 0; - ct = 0; - cr = (texture.NPOTAdjust ? texture.wScale : 1.0f); - cb = (texture.NPOTAdjust ? texture.hScale : 1.0f); - } - - /* - * For the buffer transformation, we apply the rotation last. - * Since we're transforming the texture-coordinates, we need - * to apply the inverse of the buffer transformation: - * inverse( FLIP_V -> FLIP_H -> ROT_90 ) - * <=> inverse( ROT_90 * FLIP_H * FLIP_V ) - * = inverse(FLIP_V) * inverse(FLIP_H) * inverse(ROT_90) - * = FLIP_V * FLIP_H * ROT_270 - * <=> ROT_270 -> FLIP_H -> FLIP_V - * - * The rotation is performed first, in the texture coordinate space. - * - */ - struct TexCoords { GLfloat u; GLfloat v; }; - enum { - // name of the corners in the texture map - LB = 0, // left-bottom - LT = 1, // left-top - RT = 2, // right-top - RB = 3 // right-bottom - }; - - // vertices in screen space - int vLT = LB; - int vLB = LT; - int vRB = RT; - int vRT = RB; - - // the texture's source is rotated - uint32_t transform = mBufferTransform; - if (transform & HAL_TRANSFORM_ROT_90) { - vLT = RB; - vLB = LB; - vRB = LT; - vRT = RT; - } - if (transform & HAL_TRANSFORM_FLIP_V) { - swap(vLT, vLB); - swap(vRT, vRB); - } - if (transform & HAL_TRANSFORM_FLIP_H) { - swap(vLT, vRT); - swap(vLB, vRB); - } - TexCoords texCoords[4]; - texCoords[vLT].u = cl; - texCoords[vLT].v = ct; - texCoords[vLB].u = cl; - texCoords[vLB].v = cb; - texCoords[vRB].u = cr; - texCoords[vRB].v = cb; - texCoords[vRT].u = cr; - texCoords[vRT].v = ct; + texCoords[0].u = 0; + texCoords[0].v = 1; + texCoords[1].u = 0; + texCoords[1].v = 0; + texCoords[2].u = 1; + texCoords[2].v = 0; + texCoords[3].u = 1; + texCoords[3].v = 1; if (needsDithering()) { glEnable(GL_DITHER); @@ -497,20 +430,6 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const glDisableClientState(GL_TEXTURE_COORD_ARRAY); } -void LayerBase::setBufferCrop(const Rect& crop) { - if (mBufferCrop != crop) { - mBufferCrop = crop; - mFlinger->invalidateHwcGeometry(); - } -} - -void LayerBase::setBufferTransform(uint32_t transform) { - if (mBufferTransform != transform) { - mBufferTransform = transform; - mFlinger->invalidateHwcGeometry(); - } -} - void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { const Layer::State& s(drawingState()); @@ -518,10 +437,10 @@ void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const "+ %s %p\n" " " "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " - "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " + "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", getTypeId(), this, s.z, tx(), ty(), s.w, s.h, - needsBlending(), needsDithering(), contentDirty, + isOpaque(), needsDithering(), contentDirty, s.alpha, s.flags, s.transform[0][0], s.transform[0][1], s.transform[1][0], s.transform[1][1]); @@ -555,9 +474,22 @@ LayerBaseClient::~LayerBaseClient() } } -sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() +sp<ISurface> LayerBaseClient::createSurface() +{ + class BSurface : public BnSurface, public LayerCleaner { + virtual sp<ISurfaceTexture> getSurfaceTexture() const { return 0; } + public: + BSurface(const sp<SurfaceFlinger>& flinger, + const sp<LayerBaseClient>& layer) + : LayerCleaner(flinger, layer) { } + }; + sp<ISurface> sur(new BSurface(mFlinger, this)); + return sur; +} + +sp<ISurface> LayerBaseClient::getSurface() { - sp<Surface> s; + sp<ISurface> s; Mutex::Autolock _l(mLock); LOG_ALWAYS_FATAL_IF(mHasSurface, @@ -573,12 +505,6 @@ wp<IBinder> LayerBaseClient::getSurfaceBinder() const { return mClientSurfaceBinder; } -sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const -{ - return new Surface(mFlinger, mIdentity, - const_cast<LayerBaseClient *>(this)); -} - void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const { LayerBase::dump(result, buffer, SIZE); @@ -601,44 +527,14 @@ void LayerBaseClient::shortDump(String8& result, char* scratch, size_t size) con // --------------------------------------------------------------------------- -LayerBaseClient::Surface::Surface( - const sp<SurfaceFlinger>& flinger, - int identity, - const sp<LayerBaseClient>& owner) - : mFlinger(flinger), mIdentity(identity), mOwner(owner) -{ +LayerBaseClient::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger, + const sp<LayerBaseClient>& layer) + : mFlinger(flinger), mLayer(layer) { } -LayerBaseClient::Surface::~Surface() -{ - /* - * This is a good place to clean-up all client resources - */ - +LayerBaseClient::LayerCleaner::~LayerCleaner() { // destroy client resources - mFlinger->destroySurface(mOwner); -} - -sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const { - sp<LayerBaseClient> owner(mOwner.promote()); - return owner; -} - -status_t LayerBaseClient::Surface::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - return BnSurface::onTransact(code, data, reply, flags); -} - -sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) -{ - return NULL; -} - -status_t LayerBaseClient::Surface::setBufferCount(int bufferCount) -{ - return INVALID_OPERATION; + mFlinger->destroySurface(mLayer); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 6c49a19720d8..faf71ddbe0dd 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -29,7 +29,6 @@ #include <ui/Region.h> #include <surfaceflinger/ISurfaceComposerClient.h> -#include <private/surfaceflinger/SharedBufferStack.h> #include <private/surfaceflinger/LayerState.h> #include <pixelflinger/pixelflinger.h> @@ -43,13 +42,12 @@ namespace android { // --------------------------------------------------------------------------- -class DisplayHardware; class Client; +class DisplayHardware; class GraphicBuffer; class GraphicPlane; class LayerBaseClient; class SurfaceFlinger; -class Texture; // --------------------------------------------------------------------------- @@ -121,7 +119,7 @@ public: * to perform the actual drawing. */ virtual void draw(const Region& clip) const; - virtual void drawForSreenShot() const; + virtual void drawForSreenShot(); /** * onDraw - draws the surface. @@ -174,9 +172,9 @@ public: virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); /** - * needsBlending - true if this surface needs blending + * isOpaque - true if this surface is opaque */ - virtual bool needsBlending() const { return false; } + virtual bool isOpaque() const { return true; } /** * needsDithering - true if this surface needs dithering @@ -184,11 +182,9 @@ public: virtual bool needsDithering() const { return false; } /** - * needsLinearFiltering - true if this surface needs filtering + * needsLinearFiltering - true if this surface's state requires filtering */ - virtual bool needsFiltering() const { - return (!(mFlags & DisplayHardware::SLOW_CONFIG)) && mNeedsFiltering; - } + virtual bool needsFiltering() const { return mNeedsFiltering; } /** * isSecure - true if this surface is secure, that is if it prevents @@ -231,21 +227,25 @@ protected: void clearWithOpenGL(const Region& clip, GLclampf r, GLclampf g, GLclampf b, GLclampf alpha) const; void clearWithOpenGL(const Region& clip) const; - void drawWithOpenGL(const Region& clip, const Texture& texture) const; - - // these must be called from the post/drawing thread - void setBufferCrop(const Rect& crop); - void setBufferTransform(uint32_t transform); + void drawWithOpenGL(const Region& clip) const; + + void setFiltering(bool filtering); + bool getFiltering() const; sp<SurfaceFlinger> mFlinger; uint32_t mFlags; - // post/drawing thread - Rect mBufferCrop; - uint32_t mBufferTransform; +private: + // accessed only in the main thread + // Whether filtering is forced on or not + bool mFiltering; // cached during validateVisibility() + // Whether filtering is needed b/c of the drawingstate bool mNeedsFiltering; + +protected: + // cached during validateVisibility() int32_t mOrientation; GLfloat mVertices[4][2]; Rect mTransformedBounds; @@ -281,52 +281,38 @@ private: class LayerBaseClient : public LayerBase { public: - class Surface; - LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client); - virtual ~LayerBaseClient(); - sp<Surface> getSurface(); + virtual ~LayerBaseClient(); + + sp<ISurface> getSurface(); wp<IBinder> getSurfaceBinder() const; - virtual sp<Surface> createSurface() const; + virtual sp<LayerBaseClient> getLayerBaseClient() const { return const_cast<LayerBaseClient*>(this); } + virtual const char* getTypeId() const { return "LayerBaseClient"; } uint32_t getIdentity() const { return mIdentity; } - class Surface : public BnSurface { - public: - int32_t getIdentity() const { return mIdentity; } - - protected: - Surface(const sp<SurfaceFlinger>& flinger, int identity, - const sp<LayerBaseClient>& owner); - virtual ~Surface(); - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags); - sp<LayerBaseClient> getOwner() const; - - private: - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage); - virtual status_t setBufferCount(int bufferCount); - - protected: - friend class LayerBaseClient; - sp<SurfaceFlinger> mFlinger; - int32_t mIdentity; - wp<LayerBaseClient> mOwner; - }; - - friend class Surface; - protected: virtual void dump(String8& result, char* scratch, size_t size) const; virtual void shortDump(String8& result, char* scratch, size_t size) const; + class LayerCleaner { + sp<SurfaceFlinger> mFlinger; + wp<LayerBaseClient> mLayer; + protected: + ~LayerCleaner(); + public: + LayerCleaner(const sp<SurfaceFlinger>& flinger, + const sp<LayerBaseClient>& layer); + }; + private: + virtual sp<ISurface> createSurface(); + mutable Mutex mLock; mutable bool mHasSurface; wp<IBinder> mClientSurfaceBinder; diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp index f79166d8acca..654817d61bbe 100644 --- a/services/surfaceflinger/LayerDim.cpp +++ b/services/surfaceflinger/LayerDim.cpp @@ -65,8 +65,6 @@ void LayerDim::onDraw(const Region& clip) const glDisable(GL_TEXTURE_EXTERNAL_OES); } #endif - glDisable(GL_TEXTURE_2D); - glVertexPointer(2, GL_FLOAT, 0, mVertices); while (it != end) { diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h index 75f9a8928287..8770e6d865c1 100644 --- a/services/surfaceflinger/LayerDim.h +++ b/services/surfaceflinger/LayerDim.h @@ -37,7 +37,7 @@ public: virtual ~LayerDim(); virtual void onDraw(const Region& clip) const; - virtual bool needsBlending() const { return true; } + virtual bool isOpaque() const { return false; } virtual bool isSecure() const { return false; } virtual bool isProtectedByApp() const { return false; } virtual bool isProtectedByDRM() const { return false; } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index b7a51a4e58aa..97edfeef5057 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -53,6 +53,8 @@ #include "DisplayHardware/DisplayHardware.h" #include "DisplayHardware/HWComposer.h" +#include <private/surfaceflinger/SharedBufferStack.h> + /* ideally AID_GRAPHICS would be in a semi-public header * or there would be a way to map a user/group name to its id */ @@ -133,17 +135,6 @@ sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() return bclient; } -sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection() -{ - sp<ISurfaceComposerClient> bclient; - sp<UserClient> client(new UserClient(this)); - status_t err = client->initCheck(); - if (err == NO_ERROR) { - bclient = client; - } - return bclient; -} - sp<IGraphicBufferAlloc> SurfaceFlinger::createGraphicBufferAlloc() { sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc()); @@ -322,11 +313,6 @@ void SurfaceFlinger::signalEvent() { mEventQueue.invalidate(); } -void SurfaceFlinger::signal() const { - // this is the IPC call - const_cast<SurfaceFlinger*>(this)->signalEvent(); -} - bool SurfaceFlinger::authenticateSurface(const sp<ISurface>& surface) const { Mutex::Autolock _l(mStateLock); sp<IBinder> surfBinder(surface->asBinder()); @@ -658,7 +644,7 @@ void SurfaceFlinger::computeVisibleRegions( // handle hidden surfaces by setting the visible region to empty if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) { - const bool translucent = layer->needsBlending(); + const bool translucent = !layer->isOpaque(); const Rect bounds(layer->visibleBounds()); visibleRegion.set(bounds); visibleRegion.andSelf(screenRegion); @@ -921,7 +907,7 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty) for (size_t i=0 ; i<count ; i++) { if (cur[i].hints & HWC_HINT_CLEAR_FB) { const sp<LayerBase>& layer(layers[i]); - if (!(layer->needsBlending())) { + if (layer->isOpaque()) { transparent.orSelf(layer->visibleRegionScreen); } } @@ -979,8 +965,6 @@ void SurfaceFlinger::debugFlashRegions() composeSurfaces(repaint); } - TextureManager::deactivateTextures(); - glDisable(GL_BLEND); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); @@ -1070,6 +1054,7 @@ void SurfaceFlinger::drawWormhole() const glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); } @@ -1269,7 +1254,7 @@ sp<ISurface> SurfaceFlinger::createSurface( uint32_t flags) { sp<LayerBaseClient> layer; - sp<LayerBaseClient::Surface> surfaceHandle; + sp<ISurface> surfaceHandle; if (int32_t(w|h) < 0) { LOGE("createSurface() failed, w or h is negative (w=%d, h=%d)", @@ -1300,13 +1285,13 @@ sp<ISurface> SurfaceFlinger::createSurface( surfaceHandle = layer->getSurface(); if (surfaceHandle != 0) { params->token = token; - params->identity = surfaceHandle->getIdentity(); + params->identity = layer->getIdentity(); params->width = w; params->height = h; params->format = format; if (normalLayer != 0) { Mutex::Autolock _l(mStateLock); - mLayerMap.add(surfaceHandle->asBinder(), normalLayer); + mLayerMap.add(layer->getSurfaceBinder(), normalLayer); } } @@ -1782,7 +1767,6 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() GLfloat vtx[8]; const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} }; - glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tname); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -1900,6 +1884,7 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() glEnable(GL_SCISSOR_TEST); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDeleteTextures(1, &tname); + glDisable(GL_TEXTURE_2D); return NO_ERROR; } @@ -1930,7 +1915,6 @@ status_t SurfaceFlinger::electronBeamOnAnimationImplLocked() GLfloat vtx[8]; const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} }; - glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tname); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -2044,6 +2028,7 @@ status_t SurfaceFlinger::electronBeamOnAnimationImplLocked() glEnable(GL_SCISSOR_TEST); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDeleteTextures(1, &tname); + glDisable(GL_TEXTURE_2D); return NO_ERROR; } @@ -2408,133 +2393,78 @@ sp<LayerBaseClient> Client::getLayerUser(int32_t i) const return lbc; } -sp<IMemoryHeap> Client::getControlBlock() const { - return 0; -} -ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const { - return -1; + +status_t Client::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // these must be checked + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + LOGE("Permission Denial: " + "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + return BnSurfaceComposerClient::onTransact(code, data, reply, flags); } + + sp<ISurface> Client::createSurface( ISurfaceComposerClient::surface_data_t* params, const String8& name, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { - return mFlinger->createSurface(params, name, this, - display, w, h, format, flags); -} -status_t Client::destroySurface(SurfaceID sid) { - return mFlinger->removeSurface(this, sid); -} -status_t Client::setState(int32_t count, const layer_state_t* states) { - return mFlinger->setClientState(this, count, states); -} - -// --------------------------------------------------------------------------- - -UserClient::UserClient(const sp<SurfaceFlinger>& flinger) - : ctrlblk(0), mBitmap(0), mFlinger(flinger) -{ - const int pgsize = getpagesize(); - const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1)); - - mCblkHeap = new MemoryHeapBase(cblksize, 0, - "SurfaceFlinger Client control-block"); - - ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase()); - if (ctrlblk) { // construct the shared structure in-place. - new(ctrlblk) SharedClient; - } -} - -UserClient::~UserClient() -{ - if (ctrlblk) { - ctrlblk->~SharedClient(); // destroy our shared-structure. - } - /* - * When a UserClient dies, it's unclear what to do exactly. - * We could go ahead and destroy all surfaces linked to that client - * however, it wouldn't be fair to the main Client - * (usually the the window-manager), which might want to re-target - * the layer to another UserClient. - * I think the best is to do nothing, or not much; in most cases the - * WM itself will go ahead and clean things up when it detects a client of - * his has died. - * The remaining question is what to display? currently we keep - * just keep the current buffer. + * createSurface must be called from the GL thread so that it can + * have access to the GL context. */ -} -status_t UserClient::initCheck() const { - return ctrlblk == 0 ? NO_INIT : NO_ERROR; -} - -void UserClient::detachLayer(const Layer* layer) -{ - int32_t name = layer->getToken(); - if (name >= 0) { - int32_t mask = 1LU<<name; - if ((android_atomic_and(~mask, &mBitmap) & mask) == 0) { - LOGW("token %d wasn't marked as used %08x", name, int(mBitmap)); + class MessageCreateSurface : public MessageBase { + sp<ISurface> result; + SurfaceFlinger* flinger; + ISurfaceComposerClient::surface_data_t* params; + Client* client; + const String8& name; + DisplayID display; + uint32_t w, h; + PixelFormat format; + uint32_t flags; + public: + MessageCreateSurface(SurfaceFlinger* flinger, + ISurfaceComposerClient::surface_data_t* params, + const String8& name, Client* client, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) + : flinger(flinger), params(params), client(client), name(name), + display(display), w(w), h(h), format(format), flags(flags) + { } - } -} - -sp<IMemoryHeap> UserClient::getControlBlock() const { - return mCblkHeap; -} - -ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const -{ - int32_t name = NAME_NOT_FOUND; - sp<Layer> layer(mFlinger->getLayer(sur)); - if (layer == 0) { - return name; - } - - // if this layer already has a token, just return it - name = layer->getToken(); - if ((name >= 0) && (layer->getClient() == this)) { - return name; - } - - name = 0; - do { - int32_t mask = 1LU<<name; - if ((android_atomic_or(mask, &mBitmap) & mask) == 0) { - // we found and locked that name - status_t err = layer->setToken( - const_cast<UserClient*>(this), ctrlblk, name); - if (err != NO_ERROR) { - // free the name - android_atomic_and(~mask, &mBitmap); - name = err; - } - break; + sp<ISurface> getResult() const { return result; } + virtual bool handler() { + result = flinger->createSurface(params, name, client, + display, w, h, format, flags); + return true; } - if (++name >= int32_t(SharedBufferStack::NUM_LAYERS_MAX)) - name = NO_MEMORY; - } while(name >= 0); - - //LOGD("getTokenForSurface(%p) => %d (client=%p, bitmap=%08lx)", - // sur->asBinder().get(), name, this, mBitmap); - return name; -} + }; -sp<ISurface> UserClient::createSurface( - ISurfaceComposerClient::surface_data_t* params, - const String8& name, - DisplayID display, uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags) { - return 0; + sp<MessageBase> msg = new MessageCreateSurface(mFlinger.get(), + params, name, this, display, w, h, format, flags); + mFlinger->postMessageSync(msg); + return static_cast<MessageCreateSurface*>( msg.get() )->getResult(); } -status_t UserClient::destroySurface(SurfaceID sid) { - return INVALID_OPERATION; +status_t Client::destroySurface(SurfaceID sid) { + return mFlinger->removeSurface(this, sid); } -status_t UserClient::setState(int32_t count, const layer_state_t* states) { - return INVALID_OPERATION; +status_t Client::setState(int32_t count, const layer_state_t* states) { + return mFlinger->setClientState(this, count, states); } // --------------------------------------------------------------------------- @@ -2547,11 +2477,11 @@ sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h PixelFormat format, uint32_t usage) { sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage)); status_t err = graphicBuffer->initCheck(); - if (err != 0) { - LOGE("createGraphicBuffer: init check failed: %d", err); - return 0; - } else if (graphicBuffer->handle == 0) { - LOGE("createGraphicBuffer: unable to create GraphicBuffer"); + if (err != 0 || graphicBuffer->handle == 0) { + GraphicBuffer::dumpAllocationsToSystemLog(); + LOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) " + "failed (%s), handle=%p", + w, h, strerror(-err), graphicBuffer->handle); return 0; } return graphicBuffer; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 992861a0af93..af1ef04dc29e 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -50,6 +50,7 @@ class DisplayHardware; class FreezeLock; class Layer; class LayerDim; +struct surface_flinger_cblk_t; #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) @@ -72,14 +73,14 @@ public: private: // ISurfaceComposerClient interface - virtual sp<IMemoryHeap> getControlBlock() const; - virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const; virtual sp<ISurface> createSurface( surface_data_t* params, const String8& name, DisplayID display, uint32_t w, uint32_t h,PixelFormat format, uint32_t flags); virtual status_t destroySurface(SurfaceID surfaceId); virtual status_t setState(int32_t count, const layer_state_t* states); + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); // constant sp<SurfaceFlinger> mFlinger; @@ -92,40 +93,6 @@ private: mutable Mutex mLock; }; -class UserClient : public BnSurfaceComposerClient -{ -public: - // pointer to this client's control block - SharedClient* ctrlblk; - -public: - UserClient(const sp<SurfaceFlinger>& flinger); - ~UserClient(); - - status_t initCheck() const; - - // protected by SurfaceFlinger::mStateLock - void detachLayer(const Layer* layer); - -private: - - // ISurfaceComposerClient interface - virtual sp<IMemoryHeap> getControlBlock() const; - virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const; - virtual sp<ISurface> createSurface( - surface_data_t* params, const String8& name, - DisplayID display, uint32_t w, uint32_t h,PixelFormat format, - uint32_t flags); - virtual status_t destroySurface(SurfaceID surfaceId); - virtual status_t setState(int32_t count, const layer_state_t* states); - - // atomic-ops - mutable volatile int32_t mBitmap; - - sp<IMemoryHeap> mCblkHeap; - sp<SurfaceFlinger> mFlinger; -}; - class GraphicBufferAlloc : public BnGraphicBufferAlloc { public: @@ -199,7 +166,6 @@ public: // ISurfaceComposer interface virtual sp<ISurfaceComposerClient> createConnection(); - virtual sp<ISurfaceComposerClient> createClientConnection(); virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc(); virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); @@ -208,7 +174,6 @@ public: virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); - virtual void signal() const; virtual bool authenticateSurface(const sp<ISurface>& surface) const; virtual status_t captureScreen(DisplayID dpy, @@ -235,7 +200,6 @@ private: friend class Client; friend class LayerBase; friend class LayerBaseClient; - friend class LayerBaseClient::Surface; friend class Layer; friend class LayerDim; diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp new file mode 100644 index 000000000000..60fa96591061 --- /dev/null +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> + +#include "Layer.h" +#include "SurfaceTextureLayer.h" + +namespace android { +// --------------------------------------------------------------------------- + + +SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer) + : SurfaceTexture(tex), mLayer(layer) { +} + +SurfaceTextureLayer::~SurfaceTextureLayer() { +} + + +status_t SurfaceTextureLayer::setDefaultBufferSize(uint32_t w, uint32_t h) +{ + //LOGD("%s, w=%u, h=%u", __PRETTY_FUNCTION__, w, h); + return SurfaceTexture::setDefaultBufferSize(w, h); +} + +status_t SurfaceTextureLayer::setDefaultBufferFormat(uint32_t format) +{ + mDefaultFormat = format; + return NO_ERROR; +} + +status_t SurfaceTextureLayer::setBufferCount(int bufferCount) { + status_t res = SurfaceTexture::setBufferCount(bufferCount); + return res; +} + +status_t SurfaceTextureLayer::dequeueBuffer(int *buf, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + + status_t res(NO_INIT); + sp<Layer> layer(mLayer.promote()); + if (layer != NULL) { + if (format == 0) + format = mDefaultFormat; + uint32_t effectiveUsage = layer->getEffectiveUsage(usage); + //LOGD("%s, w=%u, h=%u, format=%u, usage=%08x, effectiveUsage=%08x", + // __PRETTY_FUNCTION__, w, h, format, usage, effectiveUsage); + res = SurfaceTexture::dequeueBuffer(buf, w, h, format, effectiveUsage); + if (res == NO_ERROR) { + layer->setFixedSize(w && h); + } + } + return res; +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/services/surfaceflinger/SurfaceTextureLayer.h b/services/surfaceflinger/SurfaceTextureLayer.h new file mode 100644 index 000000000000..7faff548fd34 --- /dev/null +++ b/services/surfaceflinger/SurfaceTextureLayer.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef ANDROID_SURFACE_TEXTURE_LAYER_H +#define ANDROID_SURFACE_TEXTURE_LAYER_H + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <gui/SurfaceTexture.h> + +namespace android { +// --------------------------------------------------------------------------- + +class Layer; + +class SurfaceTextureLayer : public SurfaceTexture +{ + wp<Layer> mLayer; + uint32_t mDefaultFormat; + +public: + SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer); + ~SurfaceTextureLayer(); + + status_t setDefaultBufferSize(uint32_t w, uint32_t h); + status_t setDefaultBufferFormat(uint32_t format); + +public: + virtual status_t setBufferCount(int bufferCount); + +protected: + virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage); +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACE_TEXTURE_LAYER_H diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h index 0ddf986bff9f..ca445553f9ac 100644 --- a/services/surfaceflinger/clz.h +++ b/services/surfaceflinger/clz.h @@ -20,18 +20,10 @@ namespace android { -int clz_impl(int32_t x); - -int inline clz(int32_t x) -{ -#if defined(__arm__) && !defined(__thumb__) +int inline clz(int32_t x) { return __builtin_clz(x); -#else - return clz_impl(x); -#endif } - }; // namespace android #endif /* ANDROID_SURFACE_FLINGER_CLZ_H */ diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java index 7abae09eae70..171b3714d527 100644 --- a/telephony/java/com/android/internal/telephony/CallManager.java +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -1803,33 +1803,33 @@ public final class CallManager { Call call; StringBuilder b = new StringBuilder(); - b.append("########### Dump CallManager ############"); - b.append("\nCallManager state = " + getState()); + b.append("CallManager {"); + b.append("\nstate = " + getState()); call = getActiveFgCall(); - b.append("\n - Foreground: " + getActiveFgCallState()); + b.append("\n- Foreground: " + getActiveFgCallState()); b.append(" from " + call.getPhone()); - b.append("\n Conn: ").append(getFgCallConnections()); + b.append("\n Conn: ").append(getFgCallConnections()); call = getFirstActiveBgCall(); - b.append("\n - Background: " + call.getState()); + b.append("\n- Background: " + call.getState()); b.append(" from " + call.getPhone()); - b.append("\n Conn: ").append(getBgCallConnections()); + b.append("\n Conn: ").append(getBgCallConnections()); call = getFirstActiveRingingCall(); - b.append("\n - Ringing: " +call.getState()); + b.append("\n- Ringing: " +call.getState()); b.append(" from " + call.getPhone()); for (Phone phone : getAllPhones()) { if (phone != null) { - b.append("\n Phone: " + phone + ", name = " + phone.getPhoneName() + b.append("\nPhone: " + phone + ", name = " + phone.getPhoneName() + ", state = " + phone.getState()); call = phone.getForegroundCall(); - b.append("\n - Foreground: ").append(call); + b.append("\n- Foreground: ").append(call); call = phone.getBackgroundCall(); b.append(" Background: ").append(call); call = phone.getRingingCall(); b.append(" Ringing: ").append(call); } } - b.append("\n########## End Dump CallManager ##########"); + b.append("\n}"); return b.toString(); } } diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index 014901deb12f..457fa7aac3c1 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -19,6 +19,7 @@ package com.android.internal.telephony; import android.content.Context; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.location.CountryDetector; import android.net.Uri; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; @@ -29,6 +30,13 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberOfflineGeocoder; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + +import java.util.Locale; + /** * Looks up caller information for the given phone number. @@ -72,7 +80,8 @@ public class CallerInfo { */ public String name; public String phoneNumber; - public String nomalizedNumber; + public String normalizedNumber; + public String geoDescription; public String cnapName; public int numberPresentation; @@ -131,7 +140,7 @@ public class CallerInfo { info.isCachedPhotoCurrent = false; info.contactExists = false; - if (VDBG) Log.v(TAG, "construct callerInfo from cursor"); + if (VDBG) Log.v(TAG, "getCallerInfo() based on cursor..."); if (cursor != null) { if (cursor.moveToFirst()) { @@ -156,7 +165,7 @@ public class CallerInfo { // Look for the normalized number columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER); if (columnIndex != -1) { - info.nomalizedNumber = cursor.getString(columnIndex); + info.normalizedNumber = cursor.getString(columnIndex); } // Look for the label/type combo @@ -236,6 +245,8 @@ public class CallerInfo { * with all relevant fields empty or null. */ public static CallerInfo getCallerInfo(Context context, String number) { + if (VDBG) Log.v(TAG, "getCallerInfo() based on number..."); + if (TextUtils.isEmpty(number)) { return null; } @@ -473,6 +484,66 @@ public class CallerInfo { } /** + * Updates this CallerInfo's geoDescription field, based on the raw + * phone number in the phoneNumber field. + * + * (Note that the various getCallerInfo() methods do *not* set the + * geoDescription automatically; you need to call this method + * explicitly to get it.) + * + * @param context the context used to look up the current locale / country + * @param fallbackNumber if this CallerInfo's phoneNumber field is empty, + * this specifies a fallback number to use instead. + */ + public void updateGeoDescription(Context context, String fallbackNumber) { + String number = TextUtils.isEmpty(phoneNumber) ? fallbackNumber : phoneNumber; + geoDescription = getGeoDescription(context, number); + } + + /** + * @return a geographical description string for the specified number. + * @see com.google.i18n.phonenumbers.PhoneNumberOfflineGeocoder + */ + private static String getGeoDescription(Context context, String number) { + if (VDBG) Log.v(TAG, "getGeoDescription('" + number + "')..."); + + if (TextUtils.isEmpty(number)) { + return null; + } + + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance(); + + String countryIso; + Locale locale = context.getResources().getConfiguration().locale; + CountryDetector detector = (CountryDetector) context.getSystemService( + Context.COUNTRY_DETECTOR); + if (detector != null) { + countryIso = detector.detectCountry().getCountryIso(); + } else { + countryIso = locale.getCountry(); + Log.w(TAG, "No CountryDetector; falling back to countryIso based on locale: " + + countryIso); + } + + PhoneNumber pn = null; + try { + pn = util.parse(number, countryIso); + if (VDBG) Log.v(TAG, "- parsed number: " + pn); + } catch (NumberParseException e) { + Log.w(TAG, "getGeoDescription: NumberParseException for incoming number '" + number + "'"); + } + + if (pn != null) { + String description = geocoder.getDescriptionForNumber(pn, locale); + if (VDBG) Log.v(TAG, "- got description: '" + description + "'"); + return description; + } else { + return null; + } + } + + /** * @return a string debug representation of this instance. */ public String toString() { @@ -482,8 +553,11 @@ public class CallerInfo { if (VERBOSE_DEBUG) { return new StringBuilder(384) + .append(super.toString() + " { ") .append("\nname: " + name) .append("\nphoneNumber: " + phoneNumber) + .append("\nnormalizedNumber: " + normalizedNumber) + .append("\ngeoDescription: " + geoDescription) .append("\ncnapName: " + cnapName) .append("\nnumberPresentation: " + numberPresentation) .append("\nnamePresentation: " + namePresentation) @@ -502,10 +576,11 @@ public class CallerInfo { .append("\nemergency: " + mIsEmergency) .append("\nvoicemail " + mIsVoiceMail) .append("\ncontactExists " + contactExists) + .append(" }") .toString(); } else { return new StringBuilder(128) - .append("CallerInfo { ") + .append(super.toString() + " { ") .append("name " + ((name == null) ? "null" : "non-null")) .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null")) .append(" }") diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index c47e076a6707..bbd4232652c9 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -34,9 +34,11 @@ import android.text.TextUtils; import android.util.Log; /** - * ASYNCHRONOUS QUERY API + * Helper class to make it easier to run asynchronous caller-id lookup queries. + * @see CallerInfo + * + * {@hide} */ - public class CallerInfoAsyncQuery { private static final boolean DBG = false; private static final String LOG_TAG = "CallerInfoAsyncQuery"; @@ -239,12 +241,31 @@ public class CallerInfoAsyncQuery { + mCallerInfo); } + // Final step: look up the geocoded description. + // + // For now, do this only if we *don't* have a valid name (i.e. if + // no contacts matched the phone number of the incoming call), + // since that's the only case where the incoming-call UI cares + // about this field. + // (TODO: But if we ever want the UI to show the geoDescription + // even when we *do* match a contact, we'll need to either call + // updateGeoDescription() unconditionally here, or possibly add a + // new parameter to CallerInfoAsyncQuery.startQuery() to force + // the geoDescription field to be populated.) + if (TextUtils.isEmpty(mCallerInfo.name)) { + // Actually when no contacts match the incoming phone number, + // the CallerInfo object is totally blank here (i.e. no name + // *or* phoneNumber). So we need to pass in cw.number as + // a fallback number. + mCallerInfo.updateGeoDescription(mQueryContext, cw.number); + } + // Use the number entered by the user for display. if (!TextUtils.isEmpty(cw.number)) { CountryDetector detector = (CountryDetector) mQueryContext.getSystemService( Context.COUNTRY_DETECTOR); mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number, - mCallerInfo.nomalizedNumber, + mCallerInfo.normalizedNumber, detector.detectCountry().getCountryIso()); } } @@ -412,7 +433,7 @@ public class CallerInfoAsyncQuery { * Method to create a new CallerInfoAsyncQueryHandler object, ensuring correct * state of context and uri. */ - private void allocate (Context context, Uri contactRef) { + private void allocate(Context context, Uri contactRef) { if ((context == null) || (contactRef == null)){ throw new QueryPoolException("Bad context or query uri."); } @@ -424,7 +445,7 @@ public class CallerInfoAsyncQuery { /** * Releases the relevant data. */ - private void release () { + private void release() { mHandler.mQueryContext = null; mHandler.mQueryUri = null; mHandler.mCallerInfo = null; |