summaryrefslogtreecommitdiffstats
path: root/core/tests
diff options
context:
space:
mode:
authorSvetoslav Ganov <svetoslavganov@google.com>2011-11-21 18:41:59 -0800
committerSvetoslav Ganov <svetoslavganov@google.com>2011-11-29 18:51:30 -0800
commitd116d7c78a9c53f30a73bf273bd7618312cf3847 (patch)
treec46c6bdb1abeca54d711b304ab7596569e9aa43c /core/tests
parent50b2042502bc459b40430fe3b3b83d7d61e5daf9 (diff)
downloadframeworks_base-d116d7c78a9c53f30a73bf273bd7618312cf3847.zip
frameworks_base-d116d7c78a9c53f30a73bf273bd7618312cf3847.tar.gz
frameworks_base-d116d7c78a9c53f30a73bf273bd7618312cf3847.tar.bz2
Fixing memory leaks in the accessiiblity layer.
1. AccessibilityInteractionConnections were removed from the AccessiiblityManagerService but their DeathRecipents were not unregistered, thus every removed interaction connection was essentially leaking. Such connection is registered in the system for every ViewRootImpl when accessiiblity is enabled and inregistered when disabled. 2. Every AccessibilityEvent and AccessiilbityEventInfo obtained from a widnow content querying accessibility service had a handle to a binder proxy over which to make queries. Hoewever, holding a proxy to a remote binder prevents the latter from being garbage collected. Therefore, now the events and infos have a connection id insteand and the hindden singleton AccessiiblityInteaction client via which queries are made has a registry with the connections. This class looks up the connection given its id before making an IPC. Now the connection is stored in one place and when an accessibility service is disconnected the system sets the connection to null so the binder object in the system process can be GCed. Note that before this change a bad implemented accessibility service could cache events or infos causing a leak in the system process. This should never happen. 3. SparseArray was not clearing the reference to the last moved element while garbage collecting thus causing a leak. bug:5664337 Change-Id: Id397f614b026d43bd7b57bb7f8186bca5cdfcff9
Diffstat (limited to 'core/tests')
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java198
1 files changed, 83 insertions, 115 deletions
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
index 3521296..ec12124 100644
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -26,6 +26,7 @@ import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
@@ -54,28 +55,31 @@ public class InterrogationActivityTest
// Timeout before give up wait for the system to process an accessibility setting change.
private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000;
+ // Timeout for the accessibility state of an Activity to be fully initialized.
+ private static final int TIMEOUT_ACCESSIBLITY_STATE_INITIALIZED_MILLIS = 100;
+
// Handle to a connection to the AccessibilityManagerService
- private static IAccessibilityServiceConnection sConnection;
+ private static int sConnectionId = View.NO_ID;
// The last received accessibility event
- private static volatile AccessibilityEvent sLastFocusAccessibilityEvent;
+ private volatile AccessibilityEvent mLastAccessibilityEvent;
public InterrogationActivityTest() {
super(InterrogationActivity.class);
}
+ @Override
+ public void setUp() throws Exception {
+ ensureConnection();
+ bringUpActivityWithInitalizedAccessbility();
+ }
+
@LargeTest
public void testFindAccessibilityNodeInfoByViewId() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertNotNull(button);
assertEquals(0, button.getChildCount());
@@ -120,15 +124,9 @@ public class InterrogationActivityTest
public void testFindAccessibilityNodeInfoByViewText() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view by text
- List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByViewTextInActiveWindow(connection, "butto");
+ List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfosByViewTextInActiveWindow(sConnectionId, "butto");
assertEquals(9, buttons.size());
} finally {
if (DEBUG) {
@@ -143,15 +141,11 @@ public class InterrogationActivityTest
public void testFindAccessibilityNodeInfoByViewTextContentDescription() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
+ bringUpActivityWithInitalizedAccessbility();
// find a view by text
- List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByViewTextInActiveWindow(connection,
+ List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfosByViewTextInActiveWindow(sConnectionId,
"contentDescription");
assertEquals(1, buttons.size());
} finally {
@@ -167,12 +161,6 @@ public class InterrogationActivityTest
public void testTraverseAllViews() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// make list of expected nodes
List<String> classNameAndTextList = new ArrayList<String>();
classNameAndTextList.add("android.widget.LinearLayout");
@@ -190,7 +178,7 @@ public class InterrogationActivityTest
classNameAndTextList.add("android.widget.ButtonButton9");
AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.root);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.root);
assertNotNull("We must find the existing root.", root);
Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
@@ -227,23 +215,17 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionFocus() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, 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 = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ button = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isFocused());
} finally {
if (DEBUG) {
@@ -257,15 +239,9 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionClearFocus() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isFocused());
// focus the view
@@ -273,7 +249,7 @@ public class InterrogationActivityTest
// find the view again and make sure it is focused
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isFocused());
// unfocus the view
@@ -281,7 +257,7 @@ public class InterrogationActivityTest
// find the view again and make sure it is not focused
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isFocused());
} finally {
if (DEBUG) {
@@ -296,15 +272,9 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionSelect() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not selected
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
// select the view
@@ -312,7 +282,7 @@ public class InterrogationActivityTest
// find the view again and make sure it is selected
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isSelected());
} finally {
if (DEBUG) {
@@ -326,15 +296,9 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionClearSelection() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not selected
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
// select the view
@@ -342,15 +306,15 @@ public class InterrogationActivityTest
// find the view again and make sure it is selected
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, 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 = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ button = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
} finally {
if (DEBUG) {
@@ -365,30 +329,24 @@ public class InterrogationActivityTest
public void testAccessibilityEventGetSource() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
// focus the view
assertTrue(button.performAction(ACTION_FOCUS));
- synchronized (sConnection) {
+ synchronized (this) {
try {
- sConnection.wait(500);
+ wait(TIMEOUT_ACCESSIBLITY_STATE_INITIALIZED_MILLIS);
} catch (InterruptedException ie) {
/* ignore */
}
}
// check that last event source
- AccessibilityNodeInfo source = sLastFocusAccessibilityEvent.getSource();
+ AccessibilityNodeInfo source = mLastAccessibilityEvent.getSource();
assertNotNull(source);
// bounds
@@ -430,15 +388,9 @@ public class InterrogationActivityTest
public void testObjectContract() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
AccessibilityNodeInfo parent = button.getParent();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -459,24 +411,57 @@ public class InterrogationActivityTest
}
}
- @Override
- protected void scrubClass(Class<?> testCaseClass) {
- /* intentionally do not scrub */
+ private void bringUpActivityWithInitalizedAccessbility() {
+ mLastAccessibilityEvent = null;
+ // bring up the activity
+ getActivity();
+
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ while (true) {
+ if (mLastAccessibilityEvent != null) {
+ final int eventType = mLastAccessibilityEvent.getEventType();
+ if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ return;
+ }
+ }
+ final long remainingTimeMillis = TIMEOUT_ACCESSIBLITY_STATE_INITIALIZED_MILLIS
+ - (SystemClock.uptimeMillis() - startTimeMillis);
+ if (remainingTimeMillis <= 0) {
+ return;
+ }
+ synchronized (this) {
+ try {
+ wait(remainingTimeMillis);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+ }
+ }
}
- private IAccessibilityServiceConnection getConnection() throws Exception {
- if (sConnection == null) {
+ private void ensureConnection() throws Exception {
+ if (sConnectionId == View.NO_ID) {
IEventListener listener = new IEventListener.Stub() {
- public void setConnection(IAccessibilityServiceConnection connection) {}
+ public void setConnection(IAccessibilityServiceConnection connection,
+ int connectionId) {
+ sConnectionId = connectionId;
+ if (connection != null) {
+ AccessibilityInteractionClient.getInstance().addConnection(connectionId,
+ connection);
+ } else {
+ AccessibilityInteractionClient.getInstance().removeConnection(connectionId);
+ }
+ synchronized (this) {
+ notifyAll();
+ }
+ }
public void onInterrupt() {}
public void onAccessibilityEvent(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
- sLastFocusAccessibilityEvent = AccessibilityEvent.obtain(event);
- }
- synchronized (sConnection) {
- sConnection.notifyAll();
+ mLastAccessibilityEvent = AccessibilityEvent.obtain(event);
+ synchronized (this) {
+ notifyAll();
}
}
};
@@ -485,28 +470,11 @@ public class InterrogationActivityTest
AccessibilityManager.getInstance(getInstrumentation().getContext());
synchronized (this) {
- if (!accessibilityManager.isEnabled()) {
- // Make sure we wake ourselves as the desired state is propagated.
- accessibilityManager.addAccessibilityStateChangeListener(
- new AccessibilityManager.AccessibilityStateChangeListener() {
- public void onAccessibilityStateChanged(boolean enabled) {
- synchronized (this) {
- notifyAll();
- }
- }
- });
- IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+ IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
- sConnection = manager.registerEventListener(listener);
-
- wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
- } else {
- IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
- ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
- sConnection = manager.registerEventListener(listener);
- }
+ manager.registerEventListener(listener);
+ wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
}
}
- return sConnection;
}
}