summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/14.txt12
-rw-r--r--api/current.txt28
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java4
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl33
-rw-r--r--core/java/android/content/res/Configuration.java50
-rw-r--r--core/java/android/nfc/INfcTag.aidl3
-rw-r--r--core/java/android/nfc/tech/IsoDep.java6
-rw-r--r--core/java/android/nfc/tech/MifareClassic.java29
-rw-r--r--core/java/android/nfc/tech/MifareUltralight.java30
-rw-r--r--core/java/android/nfc/tech/NfcA.java29
-rw-r--r--core/java/android/nfc/tech/NfcF.java6
-rw-r--r--core/java/android/provider/Calendar.java27
-rw-r--r--core/java/android/view/View.java10
-rw-r--r--core/java/android/view/ViewAncestor.java75
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java159
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java126
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java119
-rw-r--r--core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl4
-rw-r--r--core/java/android/webkit/HTML5VideoFullScreen.java23
-rw-r--r--core/java/android/webkit/WebView.java14
-rw-r--r--core/java/android/widget/AbsListView.java1
-rw-r--r--core/java/android/widget/AdapterView.java2
-rw-r--r--core/java/android/widget/GridLayout.java20
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java136
-rw-r--r--core/tests/coretests/src/android/content/res/ConfigurationTest.java114
-rw-r--r--include/gui/SurfaceTexture.h17
-rw-r--r--include/utils/BlobCache.h181
-rw-r--r--libs/gui/SurfaceTexture.cpp10
-rw-r--r--libs/gui/tests/SurfaceTextureClient_test.cpp108
-rw-r--r--libs/gui/tests/SurfaceTexture_test.cpp268
-rw-r--r--libs/utils/Android.mk1
-rw-r--r--libs/utils/BlobCache.cpp232
-rw-r--r--libs/utils/tests/Android.mk1
-rw-r--r--libs/utils/tests/BlobCache_test.cpp257
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java91
-rw-r--r--tests/GridLayoutTest/res/layout/grid3.xml12
-rw-r--r--tests/GridLayoutTest/src/com/android/test/layout/Activity2.java28
-rw-r--r--wifi/java/android/net/wifi/SupplicantState.java22
-rw-r--r--wifi/java/android/net/wifi/WifiMonitor.java8
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java130
40 files changed, 1945 insertions, 481 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 89654b8e6257..f641c96552cf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22721,13 +22721,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);
@@ -22735,7 +22733,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
@@ -22751,6 +22748,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
}
@@ -22778,9 +22776,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();
@@ -22788,6 +22787,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();
@@ -22801,7 +22801,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);
@@ -22826,7 +22827,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();
@@ -22836,7 +22836,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();
@@ -22857,17 +22859,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/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/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 906a5649880f..9bd45d347e0f 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -277,24 +277,24 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public int compatSmallestScreenWidthDp;
/**
- * @hide
+ * @hide Do not use. Implementation not finished.
*/
- public static final int LAYOUT_DIRECTION_UNDEFINED = -1;
+ public static final int TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE = -1;
/**
- * @hide
+ * @hide Do not use. Implementation not finished.
*/
- public static final int LAYOUT_DIRECTION_LTR = 0;
+ public static final int TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE = 0;
/**
- * @hide
+ * @hide Do not use. Implementation not finished.
*/
- public static final int LAYOUT_DIRECTION_RTL = 1;
+ public static final int TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE = 1;
/**
- * @hide The layout direction associated to the current Locale
+ * @hide The text layout direction associated to the current Locale
*/
- public int layoutDirection;
+ public int textLayoutDirection;
/**
* @hide Internal book-keeping.
@@ -322,7 +322,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
mnc = o.mnc;
if (o.locale != null) {
locale = (Locale) o.locale.clone();
- layoutDirection = o.layoutDirection;
+ textLayoutDirection = o.textLayoutDirection;
}
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
@@ -358,6 +358,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration
} else {
sb.append(" (no locale)");
}
+ switch (textLayoutDirection) {
+ case TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE: sb.append(" ?layoutdir"); break;
+ case TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
+ default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
+ }
if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
} else {
@@ -450,11 +455,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration
case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(navigationHidden); break;
}
- switch (layoutDirection) {
- case LAYOUT_DIRECTION_UNDEFINED: sb.append(" ?layoutdir"); break;
- case LAYOUT_DIRECTION_LTR: sb.append(" ltr"); break;
- case LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
- }
if (seq != 0) {
sb.append(" s.");
sb.append(seq);
@@ -483,8 +483,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+ textLayoutDirection = TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
seq = 0;
- layoutDirection = LAYOUT_DIRECTION_LTR;
}
/** {@hide} */
@@ -519,7 +519,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_LOCALE;
locale = delta.locale != null
? (Locale) delta.locale.clone() : null;
- layoutDirection = getLayoutDirectionFromLocale(locale);
+ textLayoutDirection = getLayoutDirectionFromLocale(locale);
}
if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
{
@@ -611,23 +611,25 @@ public final class Configuration implements Parcelable, Comparable<Configuration
/**
* Return the layout direction for a given Locale
* @param locale the Locale for which we want the layout direction. Can be null.
- * @return the layout direction. This may be one of {@link #LAYOUT_DIRECTION_UNDEFINED},
- * {@link #LAYOUT_DIRECTION_LTR} or {@link #LAYOUT_DIRECTION_RTL}.
+ * @return the layout direction. This may be one of:
+ * {@link #TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE} or
+ * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
+ * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
*
* @hide
*/
public static int getLayoutDirectionFromLocale(Locale locale) {
- if (locale == null || locale.equals(Locale.ROOT)) return LAYOUT_DIRECTION_UNDEFINED;
+ if (locale == null || locale.equals(Locale.ROOT)) return TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE;
// Be careful: this code will need to be changed when vertical scripts will be supported
// OR if ICU4C is updated to have the "likelySubtags" file
switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
- return LAYOUT_DIRECTION_LTR;
+ return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
- return LAYOUT_DIRECTION_RTL;
+ return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
default:
- return LAYOUT_DIRECTION_UNDEFINED;
+ return TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE;
}
}
@@ -810,7 +812,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
- dest.writeInt(layoutDirection);
+ dest.writeInt(textLayoutDirection);
dest.writeInt(seq);
}
@@ -838,7 +840,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
- layoutDirection = source.readInt();
+ textLayoutDirection = source.readInt();
seq = source.readInt();
}
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 441cdc14db8a..ee187222e622 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/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 44229a42be9a..4cae9d80d614 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -253,18 +253,19 @@ public class HTML5VideoFullScreen extends HTML5VideoView
mLayout.setVisibility(View.VISIBLE);
WebChromeClient client = webView.getWebChromeClient();
- client.onShowCustomView(mLayout, mCallback);
- // Plugins like Flash will draw over the video so hide
- // them while we're playing.
- if (webView.getViewManager() != null)
- webView.getViewManager().hideAll();
-
- mProgressView = client.getVideoLoadingProgressView();
- if (mProgressView != null) {
- mLayout.addView(mProgressView, layoutParams);
- mProgressView.setVisibility(View.VISIBLE);
+ if (client != null) {
+ client.onShowCustomView(mLayout, mCallback);
+ // Plugins like Flash will draw over the video so hide
+ // them while we're playing.
+ if (webView.getViewManager() != null)
+ webView.getViewManager().hideAll();
+
+ mProgressView = client.getVideoLoadingProgressView();
+ if (mProgressView != null) {
+ mLayout.addView(mProgressView, layoutParams);
+ mProgressView.setVisibility(View.VISIBLE);
+ }
}
-
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 95e48808665d..10c7390d0da2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5605,8 +5605,8 @@ public class WebView extends AbsoluteLayout
* Adjustable parameters. Angle is the radians on a unit circle, limited
* to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
*/
- private static final float HSLOPE_TO_START_SNAP = .1f;
- private static final float HSLOPE_TO_BREAK_SNAP = .2f;
+ private static final float HSLOPE_TO_START_SNAP = .25f;
+ private static final float HSLOPE_TO_BREAK_SNAP = .4f;
private static final float VSLOPE_TO_START_SNAP = 1.25f;
private static final float VSLOPE_TO_BREAK_SNAP = .95f;
/*
@@ -5617,6 +5617,11 @@ public class WebView extends AbsoluteLayout
*/
private static final float ANGLE_VERT = 2f;
private static final float ANGLE_HORIZ = 0f;
+ /*
+ * The modified moving average weight.
+ * Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
+ */
+ private static final float MMA_WEIGHT_N = 5;
private boolean hitFocusedPlugin(int contentX, int contentY) {
if (DebugFlags.WEB_VIEW) {
@@ -6003,8 +6008,9 @@ public class WebView extends AbsoluteLayout
if (deltaX == 0 && deltaY == 0) {
keepScrollBarsVisible = done = true;
} else {
- mAverageAngle = (mAverageAngle +
- calculateDragAngle(deltaX, deltaY)) / 2;
+ mAverageAngle +=
+ (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
+ / MMA_WEIGHT_N;
if (mSnapScrollMode != SNAP_NONE) {
if (mSnapScrollMode == SNAP_Y) {
// radical change means getting out of snap mode
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/GridLayout.java b/core/java/android/widget/GridLayout.java
index bda82a384254..092c2f75d66a 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -494,6 +494,14 @@ public class GridLayout extends ViewGroup {
requestLayout();
}
+ private static int max2(int[] a, int valueIfEmpty) {
+ int result = valueIfEmpty;
+ for (int i = 0, N = a.length; i < N; i++) {
+ result = Math.max(result, a[i]);
+ }
+ return result;
+ }
+
private static int sum(float[] a) {
int result = 0;
for (int i = 0, length = a.length; i < length; i++) {
@@ -1409,7 +1417,7 @@ public class GridLayout extends ViewGroup {
// External entry points
private int size(int[] locations) {
- return locations[locations.length - 1] - locations[0];
+ return max2(locations, 0) - locations[0];
}
private int getMin() {
@@ -1878,21 +1886,13 @@ public class GridLayout extends ViewGroup {
return result;
}
- private static int max(int[] a, int valueIfEmpty) {
- int result = valueIfEmpty;
- for (int i = 0, length = a.length; i < length; i++) {
- result = Math.max(result, a[i]);
- }
- return result;
- }
-
/*
Create a compact array of keys or values using the supplied index.
*/
private static <K> K[] compact(K[] a, int[] index) {
int size = a.length;
Class<?> componentType = a.getClass().getComponentType();
- K[] result = (K[]) Array.newInstance(componentType, max(index, -1) + 1);
+ K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
// this overwrite duplicates, retaining the last equivalent entry
for (int i = 0; i < size; i++) {
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/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index f1f745ea9be9..54a5e4e48359 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -30,169 +30,169 @@ public class ConfigurationTest extends AndroidTestCase {
args = {Locale.class}
)
public void testGetLayoutDirectionFromLocale() {
- assertEquals(Configuration.LAYOUT_DIRECTION_UNDEFINED,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(null));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ENGLISH));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CANADA));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.FRANCE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.FRENCH));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.GERMAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.GERMANY));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ITALIAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ITALY));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.UK));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.US));
- assertEquals(Configuration.LAYOUT_DIRECTION_UNDEFINED,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ROOT));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CHINA));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CHINESE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.JAPAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.JAPANESE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.KOREA));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.KOREAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.PRC));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.TAIWAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
Locale locale = new Locale("ar");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "AE");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "BH");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "DZ");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "EG");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "IQ");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "JO");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "KW");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "LB");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "LY");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "MA");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "OM");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "QA");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "SA");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "SD");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "SY");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "TN");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "YE");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("fa");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("fa", "AF");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("fa", "IR");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("iw");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("iw", "IL");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("he");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("he", "IL");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
// The following test will not pass until we are able to take care about the scrip subtag
// thru having the "likelySubTags" file into ICU4C
// locale = new Locale("pa_Arab");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("pa_Arab", "PK");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ps");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ps", "AF");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
// The following test will not work as the localized display name would be "Urdu" with ICU 4.4
// We will need ICU 4.6 to get the correct localized display name
// locale = new Locale("ur");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("ur", "IN");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("ur", "PK");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// The following test will not pass until we are able to take care about the scrip subtag
// thru having the "likelySubTags" file into ICU4C
// locale = new Locale("uz_Arab");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("uz_Arab", "AF");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
}
}
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 2b314621abab..9294df674358 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -188,6 +188,11 @@ private:
status_t setBufferCountServerLocked(int bufferCount);
+ // computeCurrentTransformMatrix computes the transform matrix for the
+ // current texture. It uses mCurrentTransform and the current GraphicBuffer
+ // to compute this matrix and stores it in mCurrentTransformMatrix.
+ void computeCurrentTransformMatrix();
+
enum { INVALID_BUFFER_SLOT = -1 };
struct BufferSlot {
@@ -288,9 +293,9 @@ private:
// by calling setBufferCount or setBufferCountServer
int mBufferCount;
- // mRequestedBufferCount is the number of buffer slots requested by the
- // client. The default is zero, which means the client doesn't care how
- // many buffers there is.
+ // mClientBufferCount is the number of buffer slots requested by the client.
+ // The default is zero, which means the client doesn't care how many buffers
+ // there is.
int mClientBufferCount;
// mServerBufferCount buffer count requested by the server-side
@@ -322,6 +327,11 @@ private:
// gets set to mLastQueuedTransform each time updateTexImage is called.
uint32_t mCurrentTransform;
+ // mCurrentTransformMatrix is the transform matrix for the current texture.
+ // It gets computed by computeTransformMatrix each time updateTexImage is
+ // called.
+ float mCurrentTransformMatrix[16];
+
// mCurrentTimestamp is the timestamp for the current texture. It
// gets set to mLastQueuedTimestamp each time updateTexImage is called.
int64_t mCurrentTimestamp;
@@ -362,6 +372,7 @@ private:
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
+
};
// ----------------------------------------------------------------------------
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/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index ee97dcfa9c63..2cda4c82dbcf 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -96,6 +96,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) :
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
+ memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix));
}
SurfaceTexture::~SurfaceTexture() {
@@ -547,6 +548,7 @@ status_t SurfaceTexture::updateTexImage() {
mCurrentCrop = mSlots[buf].mCrop;
mCurrentTransform = mSlots[buf].mTransform;
mCurrentTimestamp = mSlots[buf].mTimestamp;
+ computeCurrentTransformMatrix();
mDequeueCondition.signal();
} else {
// We always bind the texture even if we don't update its contents.
@@ -596,8 +598,12 @@ GLenum SurfaceTexture::getCurrentTextureTarget() const {
}
void SurfaceTexture::getTransformMatrix(float mtx[16]) {
- LOGV("SurfaceTexture::getTransformMatrix");
Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::computeCurrentTransformMatrix() {
+ LOGV("SurfaceTexture::computeCurrentTransformMatrix");
float xform[16];
for (int i = 0; i < 16; i++) {
@@ -684,7 +690,7 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) {
// coordinate of 0, so SurfaceTexture must behave the same way. We don't
// want to expose this to applications, however, so we must add an
// additional vertical flip to the transform after all the other transforms.
- mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
+ mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
nsecs_t SurfaceTexture::getTimestamp() {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 2f704c89b822..da04b4a3e6eb 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -514,4 +514,112 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
thread->requestExitAndWait();
}
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ st->getTransformMatrix(mtx);
+
+ EXPECT_EQ(1.f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-1.f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(1.f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
+}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+ st->getTransformMatrix(mtx);
+
+ EXPECT_EQ(1.f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-1.f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(1.f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
+}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ android_native_rect_t crop;
+ crop.left = 0;
+ crop.top = 0;
+ crop.right = 5;
+ crop.bottom = 5;
+
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+ st->getTransformMatrix(mtx);
+
+ // This accounts for the 1 texel shrink for each edge that's included in the
+ // transform matrix to avoid texturing outside the crop region.
+ EXPECT_EQ(.5f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-.5f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(.5f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
}
+
+} // namespace android
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 8747ba5c7c53..16280d2e01c1 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+
#include <gtest/gtest.h>
#include <gui/SurfaceTexture.h>
#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
+#include <utils/threads.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/Surface.h>
@@ -618,4 +621,269 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
}
}
+/*
+ * This test is for testing GL -> GL texture streaming via SurfaceTexture. It
+ * contains functionality to create a producer thread that will perform GL
+ * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
+ * Additionally it supports interlocking the producer and consumer threads so
+ * that a specific sequence of calls can be deterministically created by the
+ * test.
+ *
+ * The intended usage is as follows:
+ *
+ * TEST_F(...) {
+ * class PT : public ProducerThread {
+ * virtual void render() {
+ * ...
+ * swapBuffers();
+ * }
+ * };
+ *
+ * runProducerThread(new PT());
+ *
+ * // The order of these calls will vary from test to test and may include
+ * // multiple frames and additional operations (e.g. GL rendering from the
+ * // texture).
+ * fc->waitForFrame();
+ * mST->updateTexImage();
+ * fc->finishFrame();
+ * }
+ *
+ */
+class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+protected:
+
+ // ProducerThread is an abstract base class to simplify the creation of
+ // OpenGL ES frame producer threads.
+ class ProducerThread : public Thread {
+ public:
+ virtual ~ProducerThread() {
+ }
+
+ void setEglObjects(EGLDisplay producerEglDisplay,
+ EGLSurface producerEglSurface,
+ EGLContext producerEglContext) {
+ mProducerEglDisplay = producerEglDisplay;
+ mProducerEglSurface = producerEglSurface;
+ mProducerEglContext = producerEglContext;
+ }
+
+ virtual bool threadLoop() {
+ eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext);
+ render();
+ eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ return false;
+ }
+
+ protected:
+ virtual void render() = 0;
+
+ void swapBuffers() {
+ eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface);
+ }
+
+ EGLDisplay mProducerEglDisplay;
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+ };
+
+ // FrameCondition is a utility class for interlocking between the producer
+ // and consumer threads. The FrameCondition object should be created and
+ // destroyed in the consumer thread only. The consumer thread should set
+ // the FrameCondition as the FrameAvailableListener of the SurfaceTexture,
+ // and should call both waitForFrame and finishFrame once for each expected
+ // frame.
+ //
+ // This interlocking relies on the fact that onFrameAvailable gets called
+ // synchronously from SurfaceTexture::queueBuffer.
+ class FrameCondition : public SurfaceTexture::FrameAvailableListener {
+ public:
+ // waitForFrame waits for the next frame to arrive. This should be
+ // called from the consumer thread once for every frame expected by the
+ // test.
+ void waitForFrame() {
+ LOGV("+waitForFrame");
+ Mutex::Autolock lock(mMutex);
+ status_t result = mFrameAvailableCondition.wait(mMutex);
+ LOGV("-waitForFrame");
+ }
+
+ // Allow the producer to return from its swapBuffers call and continue
+ // on to produce the next frame. This should be called by the consumer
+ // thread once for every frame expected by the test.
+ void finishFrame() {
+ LOGV("+finishFrame");
+ Mutex::Autolock lock(mMutex);
+ mFrameFinishCondition.signal();
+ LOGV("-finishFrame");
+ }
+
+ // This should be called by SurfaceTexture on the producer thread.
+ virtual void onFrameAvailable() {
+ LOGV("+onFrameAvailable");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableCondition.signal();
+ mFrameFinishCondition.wait(mMutex);
+ LOGV("-onFrameAvailable");
+ }
+
+ protected:
+ Mutex mMutex;
+ Condition mFrameAvailableCondition;
+ Condition mFrameFinishCondition;
+ };
+
+ SurfaceTextureGLToGLTest():
+ mProducerEglSurface(EGL_NO_SURFACE),
+ mProducerEglContext(EGL_NO_CONTEXT) {
+ }
+
+ virtual void SetUp() {
+ SurfaceTextureGLTest::SetUp();
+
+ EGLConfig myConfig = {0};
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+ mANW.get(), NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
+
+ mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
+ EGL_NO_CONTEXT, getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
+
+ mFC = new FrameCondition();
+ mST->setFrameAvailableListener(mFC);
+ }
+
+ virtual void TearDown() {
+ if (mProducerThread != NULL) {
+ mProducerThread->requestExitAndWait();
+ }
+ if (mProducerEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mProducerEglContext);
+ }
+ if (mProducerEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mProducerEglSurface);
+ }
+ mProducerThread.clear();
+ mFC.clear();
+ }
+
+ void runProducerThread(const sp<ProducerThread> producerThread) {
+ ASSERT_TRUE(mProducerThread == NULL);
+ mProducerThread = producerThread;
+ producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
+ mProducerEglContext);
+ producerThread->run();
+ }
+
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+ sp<ProducerThread> mProducerThread;
+ sp<FrameCondition> mFC;
+};
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks) {
+ class PT : public ProducerThread {
+ virtual void render() {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+ };
+
+ runProducerThread(new PT());
+
+ mFC->waitForFrame();
+ mST->updateTexImage();
+ mFC->finishFrame();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
+
+TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) {
+ class PT : public ProducerThread {
+ virtual void render() {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+ };
+
+ runProducerThread(new PT());
+
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ mST->updateTexImage();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageBeforeFrameFinishedWorks) {
+ enum { NUM_ITERATIONS = 1024 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ runProducerThread(new PT());
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mFC->waitForFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+ mFC->finishFrame();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+ }
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageAfterFrameFinishedWorks) {
+ enum { NUM_ITERATIONS = 1024 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ runProducerThread(new PT());
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+ }
+}
+
+} // namespace android
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/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/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/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml
index 31dc75abfb9c..5cdacf74faa5 100644
--- a/tests/GridLayoutTest/res/layout/grid3.xml
+++ b/tests/GridLayoutTest/res/layout/grid3.xml
@@ -19,14 +19,17 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
+
android:useDefaultMargins="true"
android:marginsIncludedInAlignment="false"
+
android:columnCount="4"
>
<TextView
android:text="Email account"
android:textSize="48dip"
+
android:layout_columnSpan="4"
android:layout_gravity="center_horizontal"
/>
@@ -34,12 +37,14 @@
<TextView
android:text="You can configure email in just a few steps:"
android:textSize="20dip"
+
android:layout_columnSpan="4"
android:layout_gravity="left"
/>
<TextView
android:text="Email address:"
+
android:layout_gravity="right"
/>
@@ -49,6 +54,7 @@
<TextView
android:text="Password:"
+
android:layout_column="0"
android:layout_gravity="right"
/>
@@ -58,14 +64,15 @@
/>
<Space
- android:layout_rowWeight="1"
- android:layout_columnWeight="1"
android:layout_row="4"
android:layout_column="2"
+ android:layout_rowWeight="1"
+ android:layout_columnWeight="1"
/>
<Button
android:text="Manual setup"
+
android:layout_row="5"
android:layout_column="3"
android:layout_gravity="fill_horizontal"
@@ -73,6 +80,7 @@
<Button
android:text="Next"
+
android:layout_column="3"
android:layout_gravity="fill_horizontal"
/>
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
index 5e29cf1ac8da..32365d7f7050 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
@@ -20,11 +20,15 @@ import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
-
-import android.widget.*;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.GridLayout;
+import android.widget.Space;
+import android.widget.TextView;
import static android.text.InputType.TYPE_CLASS_TEXT;
-import static android.view.inputmethod.EditorInfo.*;
+import static android.view.inputmethod.EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+import static android.view.inputmethod.EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
import static android.widget.GridLayout.*;
public class Activity2 extends Activity {
@@ -42,12 +46,12 @@ public class Activity2 extends Activity {
Group row6 = new Group(6, CENTER);
Group row7 = new Group(7, CENTER);
- Group col1a = new Group(1, 5, CENTER);
- Group col1b = new Group(1, 5, LEFT);
+ Group col1a = new Group(1, 4, CENTER);
+ Group col1b = new Group(1, 4, LEFT);
Group col1c = new Group(1, RIGHT);
- Group col2 = new Group(2, LEFT);
- Group col3 = new Group(3, FILL);
- Group col4 = new Group(4, FILL);
+ Group col2 = new Group(2, LEFT);
+ Group col3 = new Group(3, FILL);
+ Group col4 = new Group(4, FILL);
{
TextView v = new TextView(context);
@@ -55,20 +59,17 @@ public class Activity2 extends Activity {
v.setText("Email setup");
vg.addView(v, new LayoutParams(row1, col1a));
}
-
{
TextView v = new TextView(context);
v.setTextSize(20);
v.setText("You can configure email in just a few steps:");
vg.addView(v, new LayoutParams(row2, col1b));
}
-
{
TextView v = new TextView(context);
v.setText("Email address:");
vg.addView(v, new LayoutParams(row3, col1c));
}
-
{
EditText v = new EditText(context);
v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
@@ -78,13 +79,11 @@ public class Activity2 extends Activity {
vg.addView(v, lp);
}
}
-
{
TextView v = new TextView(context);
v.setText("Password:");
vg.addView(v, new LayoutParams(row4, col1c));
}
-
{
TextView v = new EditText(context);
v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
@@ -94,7 +93,6 @@ public class Activity2 extends Activity {
vg.addView(v, lp);
}
}
-
{
Space v = new Space(context);
{
@@ -104,13 +102,11 @@ public class Activity2 extends Activity {
vg.addView(v, lp);
}
}
-
{
Button v = new Button(context);
v.setText("Manual setup");
vg.addView(v, new LayoutParams(row6, col4));
}
-
{
Button v = new Button(context);
v.setText("Next");
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 91e685fde905..509b02ca82d8 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -216,6 +216,28 @@ public enum SupplicantState implements Parcelable {
}
}
+ static boolean isDriverActive(SupplicantState state) {
+ switch(state) {
+ case DISCONNECTED:
+ case DORMANT:
+ case INACTIVE:
+ case AUTHENTICATING:
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case SCANNING:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ return true;
+ case INTERFACE_DISABLED:
+ case UNINITIALIZED:
+ case INVALID:
+ return false;
+ default:
+ throw new IllegalArgumentException("Unknown supplicant state");
+ }
+ }
+
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
return 0;
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 4a45825b1b0b..4ec4cfcd34d9 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -107,7 +107,7 @@ public class WifiMonitor {
* <pre>
* CTRL-EVENT-DRIVER-STATE state
* </pre>
- * <code>state</code> is either STARTED or STOPPED
+ * <code>state</code> can be HANGED
*/
private static final String driverStateEvent = "DRIVER-STATE";
/**
@@ -304,11 +304,7 @@ public class WifiMonitor {
if (state == null) {
return;
}
- if (state.equals("STOPPED")) {
- mWifiStateMachine.notifyDriverStopped();
- } else if (state.equals("STARTED")) {
- mWifiStateMachine.notifyDriverStarted();
- } else if (state.equals("HANGED")) {
+ if (state.equals("HANGED")) {
mWifiStateMachine.notifyDriverHung();
}
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 6df37bbddc3a..3df3736f0497 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -212,22 +212,18 @@ public class WifiStateMachine extends StateMachine {
static final int SUP_CONNECTION_EVENT = BASE + 31;
/* Connection to supplicant lost */
static final int SUP_DISCONNECTION_EVENT = BASE + 32;
- /* Driver start completed */
- static final int DRIVER_START_EVENT = BASE + 33;
- /* Driver stop completed */
- static final int DRIVER_STOP_EVENT = BASE + 34;
- /* Network connection completed */
- static final int NETWORK_CONNECTION_EVENT = BASE + 36;
+ /* Network connection completed */
+ static final int NETWORK_CONNECTION_EVENT = BASE + 33;
/* Network disconnection completed */
- static final int NETWORK_DISCONNECTION_EVENT = BASE + 37;
+ static final int NETWORK_DISCONNECTION_EVENT = BASE + 34;
/* Scan results are available */
- static final int SCAN_RESULTS_EVENT = BASE + 38;
+ static final int SCAN_RESULTS_EVENT = BASE + 35;
/* Supplicate state changed */
- static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 39;
+ static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 36;
/* Password failure and EAP authentication failure */
- static final int AUTHENTICATION_FAILURE_EVENT = BASE + 40;
+ static final int AUTHENTICATION_FAILURE_EVENT = BASE + 37;
/* WPS overlap detected */
- static final int WPS_OVERLAP_EVENT = BASE + 41;
+ static final int WPS_OVERLAP_EVENT = BASE + 38;
/* Supplicant commands */
@@ -1421,6 +1417,35 @@ public class WifiStateMachine extends StateMachine {
return mNetworkInfo.getDetailedState();
}
+
+ private SupplicantState handleSupplicantStateChange(Message message) {
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = stateChangeResult.state;
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
+ mWifiInfo.setSupplicantState(state);
+ // Network id is only valid when we start connecting
+ if (SupplicantState.isConnecting(state)) {
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ } else {
+ mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+
+ if (state == SupplicantState.ASSOCIATING) {
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ }
+ setNetworkDetailedState(WifiInfo.getDetailedStateOf(state));
+
+ mSupplicantStateTracker.sendMessage(Message.obtain(message));
+ mWpsStateMachine.sendMessage(Message.obtain(message));
+
+ return state;
+ }
+
/**
* Resets the Wi-Fi Connections by clearing any state, resetting any sockets
* using the interface, stopping DHCP & disabling interface
@@ -1674,14 +1699,6 @@ public class WifiStateMachine extends StateMachine {
sendMessage(SCAN_RESULTS_EVENT);
}
- void notifyDriverStarted() {
- sendMessage(DRIVER_START_EVENT);
- }
-
- void notifyDriverStopped() {
- sendMessage(DRIVER_STOP_EVENT);
- }
-
void notifyDriverHung() {
setWifiEnabled(false);
setWifiEnabled(true);
@@ -1737,8 +1754,6 @@ public class WifiStateMachine extends StateMachine {
case CMD_REASSOCIATE:
case SUP_CONNECTION_EVENT:
case SUP_DISCONNECTION_EVENT:
- case DRIVER_START_EVENT:
- case DRIVER_STOP_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
case SCAN_RESULTS_EVENT:
@@ -2284,13 +2299,19 @@ public class WifiStateMachine extends StateMachine {
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case DRIVER_START_EVENT:
- transitionTo(mDriverStartedState);
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ SupplicantState state = handleSupplicantStateChange(message);
+ /* If suplicant is exiting out of INTERFACE_DISABLED state into
+ * a state that indicates driver has started, it is ready to
+ * receive driver commands
+ */
+ if (SupplicantState.isDriverActive(state)) {
+ transitionTo(mDriverStartedState);
+ }
break;
/* Queue driver commands & connection events */
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
- case SUPPLICANT_STATE_CHANGE_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
case AUTHENTICATION_FAILURE_EVENT:
@@ -2429,8 +2450,11 @@ public class WifiStateMachine extends StateMachine {
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case DRIVER_STOP_EVENT:
- transitionTo(mDriverStoppedState);
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ SupplicantState state = handleSupplicantStateChange(message);
+ if (state == SupplicantState.INTERFACE_DISABLED) {
+ transitionTo(mDriverStoppedState);
+ }
break;
/* Queue driver commands */
case CMD_START_DRIVER:
@@ -2465,11 +2489,23 @@ public class WifiStateMachine extends StateMachine {
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
- case CMD_START_DRIVER:
- mWakeLock.acquire();
- WifiNative.startDriverCommand();
- transitionTo(mDriverStartingState);
- mWakeLock.release();
+ case CMD_START_DRIVER:
+ mWakeLock.acquire();
+ WifiNative.startDriverCommand();
+ mWakeLock.release();
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ SupplicantState state = handleSupplicantStateChange(message);
+ /* A driver start causes supplicant to first report an INTERFACE_DISABLED
+ * state before transitioning out of it for connection. Stay in
+ * DriverStoppedState until we get an INTERFACE_DISABLED state and transition
+ * to DriverStarting upon getting that
+ * TODO: Fix this when the supplicant can be made to just transition out of
+ * INTERFACE_DISABLED state when driver gets started
+ */
+ if (state == SupplicantState.INTERFACE_DISABLED) {
+ transitionTo(mDriverStartingState);
+ }
break;
default:
return NOT_HANDLED;
@@ -2535,29 +2571,8 @@ public class WifiStateMachine extends StateMachine {
sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
break;
case SUPPLICANT_STATE_CHANGE_EVENT:
- stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = stateChangeResult.state;
- // Supplicant state change
- // [31-13] Reserved for future use
- // [8 - 0] Supplicant state (as defined in SupplicantState.java)
- // 50023 supplicant_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
- mWifiInfo.setSupplicantState(state);
- // Network id is only valid when we start connecting
- if (SupplicantState.isConnecting(state)) {
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- } else {
- mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
- }
-
- if (state == SupplicantState.ASSOCIATING) {
- /* BSSID is valid only in ASSOCIATING state */
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- }
-
- mSupplicantStateTracker.sendMessage(Message.obtain(message));
- mWpsStateMachine.sendMessage(Message.obtain(message));
- break;
+ handleSupplicantStateChange(message);
+ break;
/* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
WifiNative.disconnectCommand();
@@ -2964,12 +2979,7 @@ public class WifiStateMachine extends StateMachine {
/* Ignore network disconnect */
case NETWORK_DISCONNECTION_EVENT:
break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
- /* DriverStartedState does the rest of the handling */
- return NOT_HANDLED;
- case CMD_START_SCAN:
+ case CMD_START_SCAN:
/* Disable background scan temporarily during a regular scan */
if (mEnableBackgroundScan) {
WifiNative.enableBackgroundScanCommand(false);