summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--CleanSpec.mk2
-rw-r--r--api/current.txt28
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java160
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java35
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl5
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl5
-rw-r--r--core/java/android/app/UiAutomation.java39
-rw-r--r--core/java/android/util/LongArray.java4
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java153
-rw-r--r--core/java/android/view/IMagnificationCallbacks.aidl29
-rw-r--r--core/java/android/view/IWindowManager.aidl51
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewGroup.java4
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/WindowInfo.aidl19
-rw-r--r--core/java/android/view/WindowInfo.java164
-rw-r--r--core/java/android/view/WindowManagerInternal.java133
-rw-r--r--core/java/android/view/WindowManagerPolicy.java7
-rw-r--r--core/java/android/view/accessibility/AccessibilityCache.java467
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java30
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java142
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java289
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java96
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfoCache.java336
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeProvider.java13
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java4
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.aidl19
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java574
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/strings.xml14
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java17
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java670
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java42
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java1173
-rw-r--r--services/core/java/com/android/server/wm/DisplayMagnifier.java756
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java249
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java12
40 files changed, 4017 insertions, 1757 deletions
diff --git a/Android.mk b/Android.mk
index a691987..d279704 100644
--- a/Android.mk
+++ b/Android.mk
@@ -196,7 +196,6 @@ LOCAL_SRC_FILES += \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
core/java/android/view/IAssetAtlas.aidl \
- core/java/android/view/IMagnificationCallbacks.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
core/java/android/view/IOnKeyguardExitResult.aidl \
@@ -399,6 +398,7 @@ aidl_files := \
frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.aidl \
frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl \
frameworks/base/core/java/android/view/accessibility/AccessibilityRecord.aidl \
+ frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl \
frameworks/base/core/java/android/view/KeyEvent.aidl \
frameworks/base/core/java/android/view/MotionEvent.aidl \
frameworks/base/core/java/android/view/Surface.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 448b03d..4f0e603 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -187,6 +187,8 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_in
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrintClient.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/IMedia*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/view/IMagnificationCallbacks*)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index 139e927..e41b3fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2328,6 +2328,7 @@ package android.accessibilityservice {
ctor public AccessibilityService();
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+ method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method protected boolean onGesture(int);
@@ -2393,6 +2394,7 @@ package android.accessibilityservice {
field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+ field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
field public int eventTypes;
field public int feedbackType;
field public int flags;
@@ -4600,6 +4602,7 @@ package android.app {
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+ method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public boolean injectInputEvent(android.view.InputEvent, boolean);
method public final boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
@@ -30400,6 +30403,7 @@ package android.view.accessibility {
field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+ field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
}
@@ -30463,6 +30467,7 @@ package android.view.accessibility {
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
method public java.lang.String getViewIdResourceName();
+ method public android.view.accessibility.AccessibilityWindowInfo getWindow();
method public int getWindowId();
method public boolean isAccessibilityFocused();
method public boolean isCheckable();
@@ -30609,6 +30614,7 @@ package android.view.accessibility {
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public boolean performAction(int, int, android.os.Bundle);
+ field public static final int HOST_VIEW_ID = -1; // 0xffffffff
}
public class AccessibilityRecord {
@@ -30660,6 +30666,28 @@ package android.view.accessibility {
method public void setToIndex(int);
}
+ public final class AccessibilityWindowInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void getBoundsInScreen(android.graphics.Rect);
+ method public android.view.accessibility.AccessibilityWindowInfo getChild(int);
+ method public int getChildCount();
+ method public int getId();
+ method public int getLayer();
+ method public android.view.accessibility.AccessibilityWindowInfo getParent();
+ method public android.view.accessibility.AccessibilityNodeInfo getRoot();
+ method public int getType();
+ method public boolean isActive();
+ method public boolean isFocused();
+ method public static android.view.accessibility.AccessibilityWindowInfo obtain();
+ method public static android.view.accessibility.AccessibilityWindowInfo obtain(android.view.accessibility.AccessibilityWindowInfo);
+ method public void recycle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int TYPE_APPLICATION = 1; // 0x1
+ field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+ field public static final int TYPE_SYSTEM = 3; // 0x3
+ }
+
public class CaptioningManager {
method public void addCaptioningChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
method public final float getFontScale();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1e3d5be..b01d92c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -23,14 +23,18 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.os.HandlerCaller;
+import java.util.List;
+
/**
* An accessibility service runs in the background and receives callbacks by the system
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -180,28 +184,37 @@ import com.android.internal.os.HandlerCaller;
* event generation has settled down.</p>
* <h3>Event types</h3>
* <ul>
- * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
- * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
+ * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
* </ul>
* <h3>Feedback types</h3>
* <ul>
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
* </ul>
* @see AccessibilityEvent
* @see AccessibilityServiceInfo
@@ -443,8 +456,41 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Gets the windows on the screen. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are higher in the Z-order are reported first.
+ * <p>
+ * <strong>Note:</strong> In order to access the windows your service has
+ * to declare the capability to retrieve window content by setting the
+ * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * Also the service has to opt-in to retrieve the interactive windows by
+ * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
+ * flag.
+ * </p>
+ *
+ * @return The windows if there are windows and the service is can retrieve
+ * them, otherwise an empty list.
+ */
+ public List<AccessibilityWindowInfo> getWindows() {
+ return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
+ }
+
+ /**
* Gets the root node in the currently active window if this service
- * can retrieve window content.
+ * can retrieve window content. The active window is the one that the user
+ * is currently touching or the window with input focus, if the user is not
+ * touching any window.
+ * <p>
+ * <strong>Note:</strong> In order to access the root node your service has
+ * to declare the capability to retrieve window content by setting the
+ * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * </p>
*
* @return The root node if this service can retrieve window content.
*/
@@ -584,12 +630,13 @@ public abstract class AccessibilityService extends Service {
static final int NO_ID = -1;
- private static final int DO_SET_SET_CONNECTION = 10;
- private static final int DO_ON_INTERRUPT = 20;
- private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
- private static final int DO_ON_GESTURE = 40;
- private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50;
- private static final int DO_ON_KEY_EVENT = 60;
+ private static final int DO_SET_SET_CONNECTION = 1;
+ private static final int DO_ON_INTERRUPT = 2;
+ private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
+ private static final int DO_ON_GESTURE = 4;
+ private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
+ private static final int DO_ON_KEY_EVENT = 6;
+ private static final int DO_ON_WINDOWS_CHANGED = 7;
private final HandlerCaller mCaller;
@@ -624,8 +671,8 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
- public void clearAccessibilityNodeInfoCache() {
- Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+ public void clearAccessibilityCache() {
+ Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
mCaller.sendMessage(message);
}
@@ -635,6 +682,13 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
+ @Override
+ public void onWindowsChanged(int[] windowIds) {
+ Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds);
+ mCaller.sendMessage(message);
+ }
+
+ @Override
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT: {
@@ -642,12 +696,19 @@ public abstract class AccessibilityService extends Service {
if (event != null) {
AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
mCallback.onAccessibilityEvent(event);
- event.recycle();
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
}
} return;
+
case DO_ON_INTERRUPT: {
mCallback.onInterrupt();
} return;
+
case DO_SET_SET_CONNECTION: {
mConnectionId = message.arg1;
IAccessibilityServiceConnection connection =
@@ -658,18 +719,22 @@ public abstract class AccessibilityService extends Service {
mCallback.onSetConnectionId(mConnectionId);
mCallback.onServiceConnected();
} else {
- AccessibilityInteractionClient.getInstance().removeConnection(mConnectionId);
+ AccessibilityInteractionClient.getInstance().removeConnection(
+ mConnectionId);
AccessibilityInteractionClient.getInstance().clearCache();
mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
}
} return;
+
case DO_ON_GESTURE: {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
} return;
- case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
+
+ case DO_CLEAR_ACCESSIBILITY_CACHE: {
AccessibilityInteractionClient.getInstance().clearCache();
} return;
+
case DO_ON_KEY_EVENT: {
KeyEvent event = (KeyEvent) message.obj;
try {
@@ -685,9 +750,40 @@ public abstract class AccessibilityService extends Service {
}
}
} finally {
- event.recycle();
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
}
} return;
+
+ case DO_ON_WINDOWS_CHANGED: {
+ final int[] windowIds = (int[]) message.obj;
+
+ // Update the cached windows first.
+ // NOTE: The cache will hold on to the windows so do not recycle.
+ if (windowIds != null) {
+ AccessibilityInteractionClient.getInstance().removeWindows(windowIds);
+ }
+
+ // Let the client know the windows changed.
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_WINDOWS_CHANGED);
+ event.setEventTime(SystemClock.uptimeMillis());
+ event.setSealed(true);
+
+ mCallback.onAccessibilityEvent(event);
+
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
+ } break;
+
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index bdc4fdd..4f9ba59 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -284,6 +284,27 @@ public class AccessibilityServiceInfo implements Parcelable {
public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
/**
+ * This flag indicates to the system that the accessibility service wants
+ * to access content of all interactive windows. An interactive window is a
+ * window that can be touched by a sighted user when explore by touch is not
+ * enabled. If this flag is not set your service will not receive
+ * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED}
+ * events, calling AccessibilityService{@link AccessibilityService#getWindows()
+ * AccessibilityService.getWindows()} will return an empty list, and {@link
+ * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will
+ * return null.
+ * <p>
+ * Services that want to set this flag have to declare the capability
+ * to retrieve window content in their meta-data by setting the attribute
+ * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to
+ * true, otherwise this flag will be ignored. For how to declare the meta-data
+ * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
+ * </p>
+ * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
+ */
+ public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;
+
+ /**
* The event types an {@link AccessibilityService} is interested in.
* <p>
* <strong>Can be dynamically set at runtime.</strong>
@@ -302,6 +323,15 @@ public class AccessibilityServiceInfo implements Parcelable {
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
* @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
* @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED
*/
public int eventTypes;
@@ -354,6 +384,7 @@ public class AccessibilityServiceInfo implements Parcelable {
* @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
* @see #FLAG_REQUEST_FILTER_KEY_EVENTS
* @see #FLAG_REPORT_VIEW_IDS
+ * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
*/
public int flags;
@@ -449,7 +480,7 @@ public class AccessibilityServiceInfo implements Parcelable {
com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
0);
notificationTimeout = asAttributes.getInt(
- com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
+ com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
0);
flags = asAttributes.getInt(
com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
@@ -861,6 +892,8 @@ public class AccessibilityServiceInfo implements Parcelable {
return "FLAG_REPORT_VIEW_IDS";
case FLAG_REQUEST_FILTER_KEY_EVENTS:
return "FLAG_REQUEST_FILTER_KEY_EVENTS";
+ case FLAG_RETRIEVE_INTERACTIVE_WINDOWS:
+ return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
default:
return null;
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index c5e3d43a..edd8727 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -18,6 +18,7 @@ package android.accessibilityservice;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.KeyEvent;
/**
@@ -35,7 +36,9 @@ import android.view.KeyEvent;
void onGesture(int gesture);
- void clearAccessibilityNodeInfoCache();
+ void clearAccessibilityCache();
void onKeyEvent(in KeyEvent event, int sequence);
+
+ void onWindowsChanged(in int[] changedWindowIds);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 3df06b5..5f7a17d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -21,6 +21,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.accessibility.AccessibilityWindowInfo;
/**
* Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
@@ -53,6 +54,10 @@ interface IAccessibilityServiceConnection {
int action, in Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
+ AccessibilityWindowInfo getWindow(int windowId);
+
+ List<AccessibilityWindowInfo> getWindows();
+
AccessibilityServiceInfo getServiceInfo();
boolean performGlobalAction(int action);
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 498fa42..354a19f 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -36,9 +36,11 @@ import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeoutException;
/**
@@ -269,10 +271,10 @@ public final class UiAutomation {
* @param action The action to perform.
* @return Whether the action was successfully performed.
*
- * @see AccessibilityService#GLOBAL_ACTION_BACK
- * @see AccessibilityService#GLOBAL_ACTION_HOME
- * @see AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
- * @see AccessibilityService#GLOBAL_ACTION_RECENTS
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
+ * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_RECENTS
*/
public final boolean performGlobalAction(int action) {
final IAccessibilityServiceConnection connection;
@@ -346,6 +348,33 @@ public final class UiAutomation {
}
/**
+ * Gets the windows on the screen. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are higher in the Z-order are reported first.
+ * <p>
+ * <strong>Note:</strong> In order to access the windows you have to opt-in
+ * to retrieve the interactive windows by setting the
+ * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+ * </p>
+ *
+ * @return The windows if there are windows such, otherwise an empty list.
+ */
+ public List<AccessibilityWindowInfo> getWindows() {
+ final int connectionId;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connectionId = mConnectionId;
+ }
+ // Calling out without a lock held.
+ return AccessibilityInteractionClient.getInstance()
+ .getWindows(connectionId);
+ }
+
+ /**
* Gets the root {@link AccessibilityNodeInfo} in the active window.
*
* @return The root info.
@@ -632,7 +661,7 @@ public final class UiAutomation {
* potentially undesirable actions such as calling 911 or posting on public forums etc.
*
* @param enable whether to run in a "monkey" mode or not. Default is not.
- * @see {@link ActivityManager#isUserAMonkey()}
+ * @see {@link android.app.ActivityManager#isUserAMonkey()}
*/
public void setRunAsMonkey(boolean enable) {
synchronized (mLock) {
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 290a89b..d5f15f0 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -111,7 +111,6 @@ public class LongArray implements Cloneable {
}
@Override
- @SuppressWarnings("unchecked")
public LongArray clone() {
LongArray clone = null;
try {
@@ -154,7 +153,8 @@ public class LongArray implements Cloneable {
if (index >= mSize) {
throw new ArrayIndexOutOfBoundsException(mSize, index);
}
- System.arraycopy(mValues, index, mValues, index + 1, mSize - index);
+ System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
+ mSize--;
}
/**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 3859ad4..abae068 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -18,12 +18,14 @@ package android.view;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.util.LongSparseArray;
import android.view.View.AttachInfo;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -35,8 +37,11 @@ import com.android.internal.util.Predicate;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
/**
* Class for managing accessibility interactions initiated from the system
@@ -47,6 +52,8 @@ import java.util.Map;
*/
final class AccessibilityInteractionController {
+ private static final boolean ENFORCE_NODE_TREE_CONSISTENT = Build.IS_DEBUGGABLE;
+
private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
@@ -137,7 +144,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = mViewRootImpl.mView;
} else {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -209,7 +216,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -289,7 +296,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -297,9 +304,14 @@ final class AccessibilityInteractionController {
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
if (provider != null) {
- infos = provider.findAccessibilityNodeInfosByText(text,
- virtualDescendantId);
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ virtualDescendantId);
+ } else {
+ infos = provider.findAccessibilityNodeInfosByText(text,
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ }
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
ArrayList<View> foundViews = mTempArrayList;
foundViews.clear();
root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
@@ -316,7 +328,7 @@ final class AccessibilityInteractionController {
if (provider != null) {
List<AccessibilityNodeInfo> infosFromProvider =
provider.findAccessibilityNodeInfosByText(text,
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
if (infosFromProvider != null) {
infos.addAll(infosFromProvider);
}
@@ -391,7 +403,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -417,7 +429,7 @@ final class AccessibilityInteractionController {
focused = AccessibilityNodeInfo.obtain(
mViewRootImpl.mAccessibilityFocusedVirtualView);
}
- } else if (virtualDescendantId == View.NO_ID) {
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
focused = host.createAccessibilityNodeInfo();
}
} break;
@@ -500,7 +512,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
@@ -576,7 +588,7 @@ final class AccessibilityInteractionController {
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View target = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
target = findViewByAccessibilityId(accessibilityViewId);
} else {
target = mViewRootImpl.mView;
@@ -584,9 +596,14 @@ final class AccessibilityInteractionController {
if (target != null && isShown(target)) {
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
- succeeded = provider.performAction(virtualDescendantId, action,
- arguments);
- } else if (virtualDescendantId == View.NO_ID) {
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ succeeded = provider.performAction(virtualDescendantId, action,
+ arguments);
+ } else {
+ succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
+ action, arguments);
+ }
+ } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
succeeded = target.performAccessibilityAction(action, arguments);
}
}
@@ -734,6 +751,85 @@ final class AccessibilityInteractionController {
}
}
}
+ if (ENFORCE_NODE_TREE_CONSISTENT) {
+ enforceNodeTreeConsistent(outInfos);
+ }
+ }
+
+ private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
+ LongSparseArray<AccessibilityNodeInfo> nodeMap =
+ new LongSparseArray<AccessibilityNodeInfo>();
+ final int nodeCount = nodes.size();
+ for (int i = 0; i < nodeCount; i++) {
+ AccessibilityNodeInfo node = nodes.get(i);
+ nodeMap.put(node.getSourceNodeId(), node);
+ }
+
+ // If the nodes are a tree it does not matter from
+ // which node we start to search for the root.
+ AccessibilityNodeInfo root = nodeMap.valueAt(0);
+ AccessibilityNodeInfo parent = root;
+ while (parent != null) {
+ root = parent;
+ parent = nodeMap.get(parent.getParentNodeId());
+ }
+
+ // Traverse the tree and do some checks.
+ AccessibilityNodeInfo accessFocus = null;
+ AccessibilityNodeInfo inputFocus = null;
+ HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
+ Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+ fringe.add(root);
+
+ while (!fringe.isEmpty()) {
+ AccessibilityNodeInfo current = fringe.poll();
+
+ // Check for duplicates
+ if (!seen.add(current)) {
+ throw new IllegalStateException("Duplicate node: "
+ + current + " in window:"
+ + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+ }
+
+ // Check for one accessibility focus.
+ if (current.isAccessibilityFocused()) {
+ if (accessFocus != null) {
+ throw new IllegalStateException("Duplicate accessibility focus:"
+ + current
+ + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+ } else {
+ accessFocus = current;
+ }
+ }
+
+ // Check for one input focus.
+ if (current.isFocused()) {
+ if (inputFocus != null) {
+ throw new IllegalStateException("Duplicate input focus: "
+ + current + " in window:"
+ + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+ } else {
+ inputFocus = current;
+ }
+ }
+
+ final int childCount = current.getChildCount();
+ for (int j = 0; j < childCount; j++) {
+ final long childId = current.getChildId(j);
+ final AccessibilityNodeInfo child = nodeMap.get(childId);
+ if (child != null) {
+ fringe.add(child);
+ }
+ }
+ }
+
+ // Check for disconnected nodes.
+ for (int j = nodeMap.size() - 1; j >= 0; j--) {
+ AccessibilityNodeInfo info = nodeMap.valueAt(j);
+ if (!seen.contains(info)) {
+ throw new IllegalStateException("Disconnected node: " + info);
+ }
+ }
}
private void prefetchPredecessorsOfRealNode(View view,
@@ -774,7 +870,7 @@ final class AccessibilityInteractionController {
info = child.createAccessibilityNodeInfo();
} else {
info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
}
if (info != null) {
outInfos.add(info);
@@ -814,7 +910,7 @@ final class AccessibilityInteractionController {
}
} else {
AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
if (info != null) {
outInfos.add(info);
addedChildren.put(child, info);
@@ -845,16 +941,22 @@ final class AccessibilityInteractionController {
List<AccessibilityNodeInfo> outInfos) {
long parentNodeId = root.getParentNodeId();
int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
- while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+ while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
return;
}
final int virtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|| accessibilityViewId == providerHost.getAccessibilityViewId()) {
- AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
- virtualDescendantId);
+ final AccessibilityNodeInfo parent;
+ if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ parent = provider.createAccessibilityNodeInfo(
+ virtualDescendantId);
+ } else {
+ parent= provider.createAccessibilityNodeInfo(
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ }
if (parent != null) {
outInfos.add(parent);
}
@@ -875,10 +977,15 @@ final class AccessibilityInteractionController {
AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
final int parentVirtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
- if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+ if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
|| parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
- AccessibilityNodeInfo parent =
- provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+ final AccessibilityNodeInfo parent;
+ if (parentAccessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+ parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+ } else {
+ parent = provider.createAccessibilityNodeInfo(
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ }
if (parent != null) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
diff --git a/core/java/android/view/IMagnificationCallbacks.aidl b/core/java/android/view/IMagnificationCallbacks.aidl
deleted file mode 100644
index 032d073..0000000
--- a/core/java/android/view/IMagnificationCallbacks.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.view;
-
-import android.graphics.Region;
-
-/**
- * {@hide}
- */
-oneway interface IMagnificationCallbacks {
- void onMagnifedBoundsChanged(in Region bounds);
- void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
- void onRotationChanged(int rotation);
- void onUserContextChanged();
-}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c92a104..8f542bb 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -27,7 +27,6 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
-import android.view.IMagnificationCallbacks;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
@@ -197,7 +196,7 @@ interface IWindowManager
void thawRotation();
/**
- * Gets whether the rotation is frozen.
+ * Gets whether the rotation is frozen.
*
* @return Whether the rotation is frozen.
*/
@@ -231,55 +230,7 @@ interface IWindowManager
void lockNow(in Bundle options);
/**
- * Gets the token for the focused window.
- */
- IBinder getFocusedWindowToken();
-
- /**
- * Sets an input filter for manipulating the input event stream.
- */
- void setInputFilter(in IInputFilter filter);
-
- /**
- * Gets the frame of a window given its token.
- */
- void getWindowFrame(IBinder token, out Rect outFrame);
-
- /**
* Device is in safe mode.
*/
boolean isSafeModeEnabled();
-
- /**
- * Sets the display magnification callbacks. These callbacks notify
- * the client for contextual changes related to display magnification.
- *
- * @param callbacks The magnification callbacks.
- */
- void setMagnificationCallbacks(IMagnificationCallbacks callbacks);
-
- /**
- * Sets the magnification spec to be applied to all windows that can be
- * magnified.
- *
- * @param spec The current magnification spec.
- */
- void setMagnificationSpec(in MagnificationSpec spec);
-
- /**
- * Gets the magnification spec for a window given its token. If the
- * window has a compatibility scale it is also folded in the returned
- * magnification spec.
- *
- * @param windowToken The unique window token.
- * @return The magnification spec if such or null.
- */
- MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken);
-
- /**
- * Sets the current touch exploration state.
- *
- * @param enabled Whether touch exploration is enabled.
- */
- void setTouchExplorationEnabled(boolean enabled);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 179c805..a8ccd49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5101,10 +5101,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see AccessibilityDelegate
*/
public void sendAccessibilityEvent(int eventType) {
- // Excluded views do not send accessibility events.
- if (!includeForAccessibility()) {
- return;
- }
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
@@ -9386,6 +9382,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mParent.invalidateChild(this, null);
}
dispatchVisibilityChanged(this, newVisibility);
+
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f9b9401..22fbbd6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3711,7 +3711,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
childHasTransientStateChanged(child, true);
}
- if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
+ if (child.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -3954,7 +3954,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
onViewRemoved(view);
- if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
+ if (view.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 185cb65..1d6e998 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6355,7 +6355,7 @@ public final class ViewRootImpl implements ViewParent,
public void ensureConnection() {
if (mAttachInfo != null) {
final boolean registered =
- mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+ mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
@@ -6366,9 +6366,9 @@ public final class ViewRootImpl implements ViewParent,
public void ensureNoConnection() {
final boolean registered =
- mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+ mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
if (registered) {
- mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED;
+ mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
}
}
diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/WindowInfo.aidl
new file mode 100644
index 0000000..75b8fd2
--- /dev/null
+++ b/core/java/android/view/WindowInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable WindowInfo;
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
new file mode 100644
index 0000000..7f89044
--- /dev/null
+++ b/core/java/android/view/WindowInfo.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pools;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents information about a window from the
+ * window manager to another part of the system.
+ *
+ * @hide
+ */
+public class WindowInfo implements Parcelable {
+ private static final int MAX_POOL_SIZE = 10;
+
+ private static final Pools.SynchronizedPool<WindowInfo> sPool =
+ new Pools.SynchronizedPool<WindowInfo>(MAX_POOL_SIZE);
+
+ public int type;
+ public int layer;
+ public IBinder token;
+ public IBinder parentToken;
+ public boolean focused;
+ public final Rect boundsInScreen = new Rect();
+ public List<IBinder> childTokens;
+
+ private WindowInfo() {
+ /* do nothing - hide constructor */
+ }
+
+ public static WindowInfo obtain() {
+ WindowInfo window = sPool.acquire();
+ if (window == null) {
+ window = new WindowInfo();
+ }
+ return window;
+ }
+
+ public static WindowInfo obtain(WindowInfo other) {
+ WindowInfo window = obtain();
+ window.type = other.type;
+ window.layer = other.layer;
+ window.token = other.token;
+ window.parentToken = other.parentToken;
+ window.focused = other.focused;
+ window.boundsInScreen.set(other.boundsInScreen);
+
+ if (other.childTokens != null && !other.childTokens.isEmpty()) {
+ if (window.childTokens == null) {
+ window.childTokens = new ArrayList<IBinder>(other.childTokens);
+ } else {
+ window.childTokens.addAll(other.childTokens);
+ }
+ }
+
+ return window;
+ }
+
+ public void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(type);
+ parcel.writeInt(layer);
+ parcel.writeStrongBinder(token);
+ parcel.writeStrongBinder(parentToken);
+ parcel.writeInt(focused ? 1 : 0);
+ boundsInScreen.writeToParcel(parcel, flags);
+
+ if (childTokens != null && !childTokens.isEmpty()) {
+ parcel.writeInt(1);
+ parcel.writeBinderList(childTokens);
+ } else {
+ parcel.writeInt(0);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("WindowInfo[");
+ builder.append("type=").append(type);
+ builder.append(", layer=").append(layer);
+ builder.append(", token=").append(token);
+ builder.append(", parent=").append(parentToken);
+ builder.append(", focused=").append(focused);
+ builder.append(", children=").append(childTokens);
+ builder.append(']');
+ return builder.toString();
+ }
+
+ private void initFromParcel(Parcel parcel) {
+ type = parcel.readInt();
+ layer = parcel.readInt();
+ token = parcel.readStrongBinder();
+ parentToken = parcel.readStrongBinder();
+ focused = (parcel.readInt() == 1);
+ boundsInScreen.readFromParcel(parcel);
+
+ final boolean hasChildren = (parcel.readInt() == 1);
+ if (hasChildren) {
+ if (childTokens == null) {
+ childTokens = new ArrayList<IBinder>();
+ }
+ parcel.readBinderList(childTokens);
+ }
+ }
+
+ private void clear() {
+ type = 0;
+ layer = 0;
+ token = null;
+ parentToken = null;
+ focused = false;
+ boundsInScreen.setEmpty();
+ if (childTokens != null) {
+ childTokens.clear();
+ }
+ }
+
+ public static final Parcelable.Creator<WindowInfo> CREATOR =
+ new Creator<WindowInfo>() {
+ @Override
+ public WindowInfo createFromParcel(Parcel parcel) {
+ WindowInfo window = obtain();
+ window.initFromParcel(parcel);
+ return window;
+ }
+
+ @Override
+ public WindowInfo[] newArray(int size) {
+ return new WindowInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a1bd4bd..14dc356 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -16,7 +16,12 @@
package android.view;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
+import android.os.IBinder;
+
+import java.util.List;
/**
* Window manager local system service interface.
@@ -24,10 +29,136 @@ import android.hardware.display.DisplayManagerInternal;
* @hide Only for use within the system server.
*/
public abstract class WindowManagerInternal {
+
+ /**
+ * Interface to receive a callback when the windows reported for
+ * accessibility changed.
+ */
+ public interface WindowsForAccessibilityCallback {
+
+ /**
+ * Called when the windows for accessibility changed.
+ *
+ * @param windows The windows for accessibility.
+ */
+ public void onWindowsForAccessibilityChanged(List<WindowInfo> windows);
+ }
+
+ /**
+ * Callbacks for contextual changes that affect the screen magnification
+ * feature.
+ */
+ public interface MagnificationCallbacks {
+
+ /**
+ * Called when the bounds of the screen content that is magnified changed.
+ * Note that not the entire screen is magnified.
+ *
+ * @param bounds The bounds.
+ */
+ public void onMagnifedBoundsChanged(Region bounds);
+
+ /**
+ * Called when an application requests a rectangle on the screen to allow
+ * the client to apply the appropriate pan and scale.
+ *
+ * @param left The rectangle left.
+ * @param top The rectangle top.
+ * @param right The rectangle right.
+ * @param bottom The rectangle bottom.
+ */
+ public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
+
+ /**
+ * Notifies that the rotation changed.
+ *
+ * @param rotation The current rotation.
+ */
+ public void onRotationChanged(int rotation);
+
+ /**
+ * Notifies that the context of the user changed. For example, an application
+ * was started.
+ */
+ public void onUserContextChanged();
+ }
+
/**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
*/
public abstract void requestTraversalFromDisplayManager();
-} \ No newline at end of file
+
+ /**
+ * Set by the accessibility layer to observe changes in the magnified region,
+ * rotation, and other window transformations related to display magnification
+ * as the window manager is responsible for doing the actual magnification
+ * and has access to the raw window data while the accessibility layer serves
+ * as a controller.
+ *
+ * @param callbacks The callbacks to invoke.
+ */
+ public abstract void setMagnificationCallbacks(MagnificationCallbacks callbacks);
+
+ /**
+ * Set by the accessibility layer to specify the magnification and panning to
+ * be applied to all windows that should be magnified.
+ *
+ * @param callbacks The callbacks to invoke.
+ *
+ * @see #setMagnificationCallbacks(MagnificationCallbacks)
+ */
+ public abstract void setMagnificationSpec(MagnificationSpec spec);
+
+ /**
+ * Gets the magnification and translation applied to a window given its token.
+ * Not all windows are magnified and the window manager policy determines which
+ * windows are magnified. The returned result also takes into account the compat
+ * scale if necessary.
+ *
+ * @param windowToken The window's token.
+ *
+ * @return The magnification spec for the window.
+ *
+ * @see #setMagnificationCallbacks(MagnificationCallbacks)
+ */
+ public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow(
+ IBinder windowToken);
+
+ /**
+ * Sets a callback for observing which windows are touchable for the purposes
+ * of accessibility.
+ *
+ * @param callback The callback.
+ */
+ public abstract void setWindowsForAccessibilityCallback(
+ WindowsForAccessibilityCallback callback);
+
+ /**
+ * Sets a filter for manipulating the input event stream.
+ *
+ * @param filter The filter implementation.
+ */
+ public abstract void setInputFilter(IInputFilter filter);
+
+ /**
+ * Gets the token of the window that has input focus.
+ *
+ * @return The token.
+ */
+ public abstract IBinder getFocusedWindowToken();
+
+ /**
+ * @return Whether the keyguard is engaged.
+ */
+ public abstract boolean isKeyguardLocked();
+
+ /**
+ * Gets the frame of a window given its token.
+ *
+ * @param token The token.
+ * @param outBounds The frame to populate.
+ */
+ public abstract void getWindowFrame(IBinder token, Rect outBounds);
+}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 75656545..bd203c8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1187,11 +1187,4 @@ public interface WindowManagerPolicy {
* @return True if the window is a top level one.
*/
public boolean isTopLevelWindow(int windowType);
-
- /**
- * Sets the current touch exploration state.
- *
- * @param enabled Whether touch exploration is enabled.
- */
- public void setTouchExplorationEnabled(boolean enabled);
}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
new file mode 100644
index 0000000..77d48e2
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.LongArray;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Cache for AccessibilityWindowInfos and AccessibilityNodeInfos.
+ * It is updated when windows change or nodes change.
+ */
+final class AccessibilityCache {
+
+ private static final String LOG_TAG = "AccessibilityCache";
+
+ private static final boolean DEBUG = false;
+
+ private static final boolean CHECK_INTEGRITY = Build.IS_DEBUGGABLE;
+
+ private final Object mLock = new Object();
+
+ private final LongArray mTempLongArray = new LongArray();
+
+ private final SparseArray<AccessibilityWindowInfo> mWindowCache =
+ new SparseArray<AccessibilityWindowInfo>();
+
+ private final SparseArray<LongSparseArray<AccessibilityNodeInfo>> mNodeCache =
+ new SparseArray<LongSparseArray<AccessibilityNodeInfo>>();
+
+ private final SparseArray<AccessibilityWindowInfo> mTempWindowArray =
+ new SparseArray<AccessibilityWindowInfo>();
+
+ public void addWindow(AccessibilityWindowInfo window) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Caching window: " + window.getId());
+ }
+ mWindowCache.put(window.getId(), window);
+ }
+ }
+
+ public void removeWindows(int[] windowIds) {
+ synchronized (mLock) {
+ final int windowCount = windowIds.length;
+ for (int i = 0; i < windowCount; i++) {
+ final int windowId = windowIds[i];
+ AccessibilityWindowInfo window = mWindowCache.get(windowId);
+ if (window != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Removing window: " + windowId);
+ }
+ window.recycle();
+ mWindowCache.remove(windowId);
+ }
+ clearNodesForWindowLocked(windowIds[i]);
+ }
+ }
+ }
+
+ /**
+ * Notifies the cache that the something in the UI changed. As a result
+ * the cache will either refresh some nodes or evict some nodes.
+ *
+ * @param event An event.
+ */
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ synchronized (mLock) {
+ final int eventType = event.getEventType();
+ switch (eventType) {
+ case AccessibilityEvent.TYPE_VIEW_FOCUSED:
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
+ case AccessibilityEvent.TYPE_VIEW_SELECTED:
+ case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
+ case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+ refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId());
+ } break;
+
+ case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+ synchronized (mLock) {
+ final int windowId = event.getWindowId();
+ final long sourceId = event.getSourceNodeId();
+ if ((event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
+ clearSubTreeLocked(windowId, sourceId);
+ } else {
+ refreshCachedNodeLocked(windowId, sourceId);
+ }
+ }
+ } break;
+
+ case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+ clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
+ } break;
+ }
+ }
+
+ if (CHECK_INTEGRITY) {
+ checkIntegrity();
+ }
+ }
+
+ private void refreshCachedNodeLocked(int windowId, long sourceId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Refreshing cached node.");
+ }
+
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return;
+ }
+ AccessibilityNodeInfo cachedInfo = nodes.get(sourceId);
+ // If the source is not in the cache - nothing to do.
+ if (cachedInfo == null) {
+ return;
+ }
+ // The node changed so we will just refresh it right now.
+ if (cachedInfo.refresh(true)) {
+ return;
+ }
+ // Weird, we could not refresh. Just evict the entire sub-tree.
+ clearSubTreeLocked(windowId, sourceId);
+ }
+
+ /**
+ * Gets a cached {@link AccessibilityNodeInfo} given the id of the hosting
+ * window and the accessibility id of the node.
+ *
+ * @param windowId The id of the window hosting the node.
+ * @param accessibilityNodeId The info accessibility node id.
+ * @return The cached {@link AccessibilityNodeInfo} or null if such not found.
+ */
+ public AccessibilityNodeInfo getNode(int windowId, long accessibilityNodeId) {
+ synchronized(mLock) {
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return null;
+ }
+ AccessibilityNodeInfo info = nodes.get(accessibilityNodeId);
+ if (info != null) {
+ // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
+ // will wipe the data of the cached info.
+ info = AccessibilityNodeInfo.obtain(info);
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
+ }
+ return info;
+ }
+ }
+
+ public List<AccessibilityWindowInfo> getWindows() {
+ synchronized (mLock) {
+ final int windowCount = mWindowCache.size();
+ if (windowCount > 0) {
+ // Careful to return the windows in a decreasing layer order.
+ SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
+ sortedWindows.clear();
+
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ sortedWindows.put(window.getLayer(), window);
+ }
+
+ List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = sortedWindows.valueAt(i);
+ windows.add(AccessibilityWindowInfo.obtain(window));
+ }
+
+ sortedWindows.clear();
+
+ return windows;
+ }
+ return null;
+ }
+ }
+
+ public AccessibilityWindowInfo getWindow(int windowId) {
+ synchronized (mLock) {
+ AccessibilityWindowInfo window = mWindowCache.get(windowId);
+ if (window != null) {
+ return AccessibilityWindowInfo.obtain(window);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Caches an {@link AccessibilityNodeInfo}.
+ *
+ * @param info The node to cache.
+ */
+ public void add(AccessibilityNodeInfo info) {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "add(" + info + ")");
+ }
+
+ final int windowId = info.getWindowId();
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ nodes = new LongSparseArray<AccessibilityNodeInfo>();
+ mNodeCache.put(windowId, nodes);
+ }
+
+ final long sourceId = info.getSourceNodeId();
+ AccessibilityNodeInfo oldInfo = nodes.get(sourceId);
+ if (oldInfo != null) {
+ // If the added node is in the cache we have to be careful if
+ // the new one represents a source state where some of the
+ // children have been removed to remove the descendants that
+ // are no longer present.
+ final LongArray newChildrenIds = info.getChildNodeIds();
+ if (newChildrenIds != null) {
+ // Cache the new ids as we will do some lookups.
+ LongArray newChildNodeIds = mTempLongArray;
+ final int newChildCount = newChildNodeIds.size();
+ for (int i = 0; i < newChildCount; i++) {
+ newChildNodeIds.add(newChildrenIds.get(i));
+ }
+
+ final int oldChildCount = oldInfo.getChildCount();
+ for (int i = 0; i < oldChildCount; i++) {
+ final long oldChildId = oldInfo.getChildId(i);
+ if (newChildNodeIds.indexOf(oldChildId) < 0) {
+ clearSubTreeLocked(windowId, oldChildId);
+ }
+ }
+
+ newChildNodeIds.clear();
+ }
+
+ // Also be careful if the parent has changed since the new
+ // parent may be a predecessor of the old parent which will
+ // add cyclse to the cache.
+ final long oldParentId = oldInfo.getParentNodeId();
+ if (info.getParentNodeId() != oldParentId) {
+ clearSubTreeLocked(windowId, oldParentId);
+ }
+ }
+
+ // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
+ // will wipe the data of the cached info.
+ AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
+ nodes.put(sourceId, clone);
+ }
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public void clear() {
+ synchronized(mLock) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "clear()");
+ }
+ final int windowCount = mWindowCache.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ window.recycle();
+ mWindowCache.removeAt(i);
+ }
+ final int nodesForWindowCount = mNodeCache.size();
+ for (int i = 0; i < nodesForWindowCount; i++) {
+ final int windowId = mNodeCache.keyAt(i);
+ clearNodesForWindowLocked(windowId);
+ }
+ }
+ }
+
+ private void clearNodesForWindowLocked(int windowId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "clearWindowLocked(" + windowId + ")");
+ }
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes == null) {
+ return;
+ }
+ // Recycle the nodes before clearing the cache.
+ final int nodeCount = nodes.size();
+ for (int i = nodeCount - 1; i >= 0; i--) {
+ AccessibilityNodeInfo info = nodes.valueAt(i);
+ nodes.removeAt(i);
+ info.recycle();
+ }
+ mNodeCache.remove(windowId);
+ }
+
+ /**
+ * Clears a subtree rooted at the node with the given id that is
+ * hosted in a given window.
+ *
+ * @param windowId The id of the hosting window.
+ * @param rootNodeId The root id.
+ */
+ private void clearSubTreeLocked(int windowId, long rootNodeId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Clearing cached subtree.");
+ }
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+ if (nodes != null) {
+ clearSubTreeRecursiveLocked(nodes, rootNodeId);
+ }
+ }
+
+ /**
+ * Clears a subtree given a pointer to the root id and the nodes
+ * in the hosting window.
+ *
+ * @param nodes The nodes in the hosting window.
+ * @param rootNodeId The id of the root to evict.
+ */
+ private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+ long rootNodeId) {
+ AccessibilityNodeInfo current = nodes.get(rootNodeId);
+ if (current == null) {
+ return;
+ }
+ nodes.remove(rootNodeId);
+ final int childCount = current.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final long childNodeId = current.getChildId(i);
+ clearSubTreeRecursiveLocked(nodes, childNodeId);
+ }
+ }
+
+ /**
+ * Check the integrity of the cache which is nodes from different windows
+ * are not mixed, there is a single active window, there is a single focused
+ * window, for every window there are no duplicates nodes, all nodes for a
+ * window are connected, for every window there is a single input focused
+ * node, and for every window there is a single accessibility focused node.
+ */
+ public void checkIntegrity() {
+ synchronized (mLock) {
+ // Get the root.
+ if (mWindowCache.size() <= 0 && mNodeCache.size() == 0) {
+ return;
+ }
+
+ AccessibilityWindowInfo focusedWindow = null;
+ AccessibilityWindowInfo activeWindow = null;
+
+ final int windowCount = mWindowCache.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+
+ // Check for one active window.
+ if (window.isActive()) {
+ if (activeWindow != null) {
+ Log.e(LOG_TAG, "Duplicate active window:" + window);
+ } else {
+ activeWindow = window;
+ }
+ }
+
+ // Check for one focused window.
+ if (window.isFocused()) {
+ if (focusedWindow != null) {
+ Log.e(LOG_TAG, "Duplicate focused window:" + window);
+ } else {
+ focusedWindow = window;
+ }
+ }
+ }
+
+ // Traverse the tree and do some checks.
+ AccessibilityNodeInfo accessFocus = null;
+ AccessibilityNodeInfo inputFocus = null;
+
+ final int nodesForWindowCount = mNodeCache.size();
+ for (int i = 0; i < nodesForWindowCount; i++) {
+ LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.valueAt(i);
+ if (nodes.size() <= 0) {
+ continue;
+ }
+
+ ArraySet<AccessibilityNodeInfo> seen = new ArraySet<AccessibilityNodeInfo>();
+ final int windowId = mNodeCache.keyAt(i);
+
+ final int nodeCount = nodes.size();
+ for (int j = 0; j < nodeCount; j++) {
+ AccessibilityNodeInfo node = nodes.valueAt(j);
+
+ // Check for duplicates
+ if (!seen.add(node)) {
+ Log.e(LOG_TAG, "Duplicate node: " + node
+ + " in window:" + windowId);
+ }
+
+ // Check for one accessibility focus.
+ if (node.isAccessibilityFocused()) {
+ if (accessFocus != null) {
+ Log.e(LOG_TAG, "Duplicate accessibility focus:" + node
+ + " in window:" + windowId);
+ } else {
+ accessFocus = node;
+ }
+ }
+
+ // Check for one input focus.
+ if (node.isFocused()) {
+ if (inputFocus != null) {
+ Log.e(LOG_TAG, "Duplicate input focus: " + node
+ + " in window:" + windowId);
+ } else {
+ inputFocus = node;
+ }
+ }
+
+ // The node should be a child of its parent if we have the parent.
+ AccessibilityNodeInfo nodeParent = nodes.get(node.getParentNodeId());
+ if (nodeParent != null) {
+ boolean childOfItsParent = false;
+ final int childCount = nodeParent.getChildCount();
+ for (int k = 0; k < childCount; k++) {
+ AccessibilityNodeInfo child = nodes.get(nodeParent.getChildId(k));
+ if (child == node) {
+ childOfItsParent = true;
+ break;
+ }
+ }
+ if (!childOfItsParent) {
+ Log.e(LOG_TAG, "Invalid parent-child ralation between parent: "
+ + nodeParent + " and child: " + node);
+ }
+ }
+
+ // The node should be the parent of its child if we have the child.
+ final int childCount = node.getChildCount();
+ for (int k = 0; k < childCount; k++) {
+ AccessibilityNodeInfo child = nodes.get(node.getChildId(k));
+ if (child != null) {
+ AccessibilityNodeInfo parent = nodes.get(child.getParentNodeId());
+ if (parent != node) {
+ Log.e(LOG_TAG, "Invalid child-parent ralation between child: "
+ + node + " and parent: " + nodeParent);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 8b91155..417e22c 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -343,6 +343,23 @@ import java.util.List;
* view.</br>
* </p>
* <p>
+ * <b>Windows changed</b> - represents the event of changes in the windows shown on
+ * the screen such as a window appeared, a window disappeared, a window size changed,
+ * a window layer changed, etc.</br>
+ * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br>
+ * <em>Properties:</em></br>
+ * <ul>
+ * <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getEventTime()} - The event time.</li>
+ * </ul>
+ * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window
+ * source of the event via {@link AccessibilityEvent#getSource()} to get the source
+ * node on which then call {@link AccessibilityNodeInfo#getWindow()
+ * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can
+ * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows()
+ * android.accessibilityservice.AccessibilityService.getWindows()}.
+ * </p>
+ * <p>
* <b>NOTIFICATION TYPES</b></br>
* </p>
* <p>
@@ -662,6 +679,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
/**
+ * Represents the event change in the windows shown on the screen.
+ */
+ public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
+
+ /**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
* The type of change is not defined.
*/
@@ -708,6 +730,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @see #TYPE_GESTURE_DETECTION_END
* @see #TYPE_TOUCH_INTERACTION_START
* @see #TYPE_TOUCH_INTERACTION_END
+ * @see #TYPE_WINDOWS_CHANGED
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -1366,6 +1389,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
builder.append("TYPE_TOUCH_INTERACTION_END");
eventTypeCount++;
} break;
+ case TYPE_WINDOWS_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_WINDOWS_CHANGED");
+ eventTypeCount++;
+ } break;
}
}
if (eventTypeCount > 1) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 5a55e34..4dd8dcb 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -100,14 +100,11 @@ public final class AccessibilityInteractionClient
private Message mSameThreadMessage;
- // The connection cache is shared between all interrogating threads.
private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
new SparseArray<IAccessibilityServiceConnection>();
- // The connection cache is shared between all interrogating threads since
- // at any given time there is only one window allowing querying.
- private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
- new AccessibilityNodeInfoCache();
+ private static final AccessibilityCache sAccessibilityCache =
+ new AccessibilityCache();
/**
* @return The client for the current thread.
@@ -166,6 +163,86 @@ public final class AccessibilityInteractionClient
}
/**
+ * Gets the info for a window.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @return The {@link AccessibilityWindowInfo}.
+ */
+ public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ AccessibilityWindowInfo window = sAccessibilityCache.getWindow(
+ accessibilityWindowId);
+ if (window != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache hit");
+ }
+ return window;
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache miss");
+ }
+ window = connection.getWindow(accessibilityWindowId);
+ if (window != null) {
+ sAccessibilityCache.addWindow(window);
+ return window;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while calling remote getWindow", re);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the info for all windows.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @return The {@link AccessibilityWindowInfo} list.
+ */
+ public List<AccessibilityWindowInfo> getWindows(int connectionId) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
+ if (windows != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache hit");
+ }
+ return windows;
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache miss");
+ }
+ windows = connection.getWindows();
+ if (windows != null) {
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = windows.get(i);
+ sAccessibilityCache.addWindow(window);
+ }
+ return windows;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while calling remote getWindows", re);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
*
* @param connectionId The id of a connection for interacting with the system.
@@ -183,15 +260,26 @@ public final class AccessibilityInteractionClient
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
int prefetchFlags) {
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
+ && (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
+ throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
+ + " requires FLAG_PREFETCH_PREDECESSORS");
+ }
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
if (!bypassCache) {
- AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
- accessibilityNodeId);
+ AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
+ accessibilityWindowId, accessibilityNodeId);
if (cachedInfo != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Node cache hit");
+ }
return cachedInfo;
}
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Node cache miss");
+ }
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
@@ -212,10 +300,8 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfoByAccessibilityId", re);
- }
+ Log.e(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfoByAccessibilityId", re);
}
return null;
}
@@ -259,10 +345,8 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
}
return Collections.emptyList();
}
@@ -307,10 +391,8 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfosByViewText", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfosByViewText", re);
}
return Collections.emptyList();
}
@@ -352,9 +434,7 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote findFocus", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote findFocus", re);
}
return null;
}
@@ -396,9 +476,7 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
}
return null;
}
@@ -436,19 +514,21 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
- }
+ Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
}
return false;
}
public void clearCache() {
- sAccessibilityNodeInfoCache.clear();
+ sAccessibilityCache.clear();
}
public void onAccessibilityEvent(AccessibilityEvent event) {
- sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
+ sAccessibilityCache.onAccessibilityEvent(event);
+ }
+
+ public void removeWindows(int[] windowIds) {
+ sAccessibilityCache.removeWindows(windowIds);
}
/**
@@ -613,7 +693,7 @@ public final class AccessibilityInteractionClient
if (info != null) {
info.setConnectionId(connectionId);
info.setSealed(true);
- sAccessibilityNodeInfoCache.add(info);
+ sAccessibilityCache.add(info);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a711f48..116c173 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -115,9 +115,9 @@ public final class AccessibilityManager {
private static AccessibilityManager sInstance;
- private static final int DO_SET_STATE = 10;
+ private final Object mLock = new Object();
- final IAccessibilityManager mService;
+ private IAccessibilityManager mService;
final int mUserId;
@@ -166,29 +166,14 @@ public final class AccessibilityManager {
public void onTouchExplorationStateChanged(boolean enabled);
}
- final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
+ private final IAccessibilityManagerClient.Stub mClient =
+ new IAccessibilityManagerClient.Stub() {
public void setState(int state) {
- mHandler.obtainMessage(DO_SET_STATE, state, 0).sendToTarget();
- }
- };
-
- class MyHandler extends Handler {
-
- MyHandler(Looper mainLooper) {
- super(mainLooper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case DO_SET_STATE :
- setState(message.arg1);
- return;
- default :
- Log.w(LOG_TAG, "Unknown message type: " + message.what);
+ synchronized (mLock) {
+ setStateLocked(state);
}
}
- }
+ };
/**
* Get an AccessibilityManager instance (create one if necessary).
@@ -234,16 +219,8 @@ public final class AccessibilityManager {
mHandler = new MyHandler(context.getMainLooper());
mService = service;
mUserId = userId;
- if (mService == null) {
- mIsEnabled = false;
- }
- try {
- if (mService != null) {
- final int stateFlags = mService.addClient(mClient, userId);
- setState(stateFlags);
- }
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ synchronized (mLock) {
+ tryConnectToServiceLocked();
}
}
@@ -253,7 +230,11 @@ public final class AccessibilityManager {
* @return True if accessibility is enabled, false otherwise.
*/
public boolean isEnabled() {
- synchronized (mHandler) {
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
return mIsEnabled;
}
}
@@ -264,24 +245,16 @@ public final class AccessibilityManager {
* @return True if touch exploration is enabled, false otherwise.
*/
public boolean isTouchExplorationEnabled() {
- synchronized (mHandler) {
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
return mIsTouchExplorationEnabled;
}
}
/**
- * Returns the client interface this instance registers in
- * the centralized accessibility manager service.
- *
- * @return The client.
- *
- * @hide
- */
- public IAccessibilityManagerClient getClient() {
- return (IAccessibilityManagerClient) mClient.asBinder();
- }
-
- /**
* Sends an {@link AccessibilityEvent}.
*
* @param event The event to send.
@@ -295,8 +268,17 @@ public final class AccessibilityManager {
* their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
- if (!mIsEnabled) {
- throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ }
+ userId = mUserId;
}
boolean doRecycle = false;
try {
@@ -305,7 +287,7 @@ public final class AccessibilityManager {
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
- doRecycle = mService.sendAccessibilityEvent(event, mUserId);
+ doRecycle = service.sendAccessibilityEvent(event, userId);
Binder.restoreCallingIdentity(identityToken);
if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
@@ -323,11 +305,20 @@ public final class AccessibilityManager {
* Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
- if (!mIsEnabled) {
- throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ }
+ userId = mUserId;
}
try {
- mService.interrupt(mUserId);
+ service.interrupt(userId);
if (DEBUG) {
Log.i(LOG_TAG, "Requested interrupt from all services");
}
@@ -361,18 +352,30 @@ public final class AccessibilityManager {
* @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
*/
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
List<AccessibilityServiceInfo> services = null;
try {
- if (mService != null) {
- services = mService.getInstalledAccessibilityServiceList(mUserId);
- if (DEBUG) {
- Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
- }
+ services = service.getInstalledAccessibilityServiceList(userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
}
- return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST;
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
@@ -390,18 +393,30 @@ public final class AccessibilityManager {
*/
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
int feedbackTypeFlags) {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
List<AccessibilityServiceInfo> services = null;
try {
- if (mService != null) {
- services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId);
- if (DEBUG) {
- Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
- }
+ services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
}
- return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST;
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
@@ -413,6 +428,7 @@ public final class AccessibilityManager {
*/
public boolean addAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mAccessibilityStateChangeListeners.add(listener);
}
@@ -424,6 +440,7 @@ public final class AccessibilityManager {
*/
public boolean removeAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mAccessibilityStateChangeListeners.remove(listener);
}
@@ -436,6 +453,7 @@ public final class AccessibilityManager {
*/
public boolean addTouchExplorationStateChangeListener(
TouchExplorationStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mTouchExplorationStateChangeListeners.add(listener);
}
@@ -447,6 +465,7 @@ public final class AccessibilityManager {
*/
public boolean removeTouchExplorationStateChangeListener(
TouchExplorationStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mTouchExplorationStateChangeListeners.remove(listener);
}
@@ -455,50 +474,24 @@ public final class AccessibilityManager {
*
* @param stateFlags The state flags.
*/
- private void setState(int stateFlags) {
+ private void setStateLocked(int stateFlags) {
final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
final boolean touchExplorationEnabled =
(stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
- synchronized (mHandler) {
- final boolean wasEnabled = mIsEnabled;
- final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
- // Ensure listeners get current state from isZzzEnabled() calls.
- mIsEnabled = enabled;
- mIsTouchExplorationEnabled = touchExplorationEnabled;
+ final boolean wasEnabled = mIsEnabled;
+ final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
- if (wasEnabled != enabled) {
- notifyAccessibilityStateChangedLh();
- }
+ // Ensure listeners get current state from isZzzEnabled() calls.
+ mIsEnabled = enabled;
+ mIsTouchExplorationEnabled = touchExplorationEnabled;
- if (wasTouchExplorationEnabled != touchExplorationEnabled) {
- notifyTouchExplorationStateChangedLh();
- }
+ if (wasEnabled != enabled) {
+ mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
}
- }
-
- /**
- * Notifies the registered {@link AccessibilityStateChangeListener}s.
- * <p>
- * The caller must be locked on {@link #mHandler}.
- */
- private void notifyAccessibilityStateChangedLh() {
- final int listenerCount = mAccessibilityStateChangeListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
- }
- }
- /**
- * Notifies the registered {@link TouchExplorationStateChangeListener}s.
- * <p>
- * The caller must be locked on {@link #mHandler}.
- */
- private void notifyTouchExplorationStateChangedLh() {
- final int listenerCount = mTouchExplorationStateChangeListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mTouchExplorationStateChangeListeners.get(i)
- .onTouchExplorationStateChanged(mIsTouchExplorationEnabled);
+ if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+ mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
}
}
@@ -511,11 +504,17 @@ public final class AccessibilityManager {
*/
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) {
- if (mService == null) {
- return View.NO_ID;
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return View.NO_ID;
+ }
+ userId = mUserId;
}
try {
- return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId);
+ return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
}
@@ -529,12 +528,90 @@ public final class AccessibilityManager {
* @hide
*/
public void removeAccessibilityInteractionConnection(IWindow windowToken) {
- try {
- if (mService != null) {
- mService.removeAccessibilityInteractionConnection(windowToken);
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
}
+ }
+ try {
+ service.removeAccessibilityInteractionConnection(windowToken);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
}
}
+
+ private IAccessibilityManager getServiceLocked() {
+ if (mService == null) {
+ tryConnectToServiceLocked();
+ }
+ return mService;
+ }
+
+ private void tryConnectToServiceLocked() {
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ if (iBinder == null) {
+ return;
+ }
+ IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+ try {
+ final int stateFlags = service.addClient(mClient, mUserId);
+ setStateLocked(stateFlags);
+ mService = service;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ }
+ }
+
+ /**
+ * Notifies the registered {@link AccessibilityStateChangeListener}s.
+ */
+ private void handleNotifyAccessibilityStateChanged() {
+ final boolean isEnabled;
+ synchronized (mLock) {
+ isEnabled = mIsEnabled;
+ }
+ final int listenerCount = mAccessibilityStateChangeListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(isEnabled);
+ }
+ }
+
+ /**
+ * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+ */
+ private void handleNotifyTouchExplorationStateChanged() {
+ final boolean isTouchExplorationEnabled;
+ synchronized (mLock) {
+ isTouchExplorationEnabled = mIsTouchExplorationEnabled;
+ }
+ final int listenerCount = mTouchExplorationStateChangeListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mTouchExplorationStateChangeListeners.get(i)
+ .onTouchExplorationStateChanged(isTouchExplorationEnabled);
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
+ public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: {
+ handleNotifyAccessibilityStateChanged();
+ } break;
+
+ case MSG_NOTIFY_EXPLORATION_STATE_CHANGED: {
+ handleNotifyTouchExplorationStateChanged();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 560d0c9..a6904f7 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -62,13 +62,19 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final boolean DEBUG = false;
/** @hide */
- public static final int UNDEFINED = -1;
+ public static final int UNDEFINED_CONNECTION_ID = -1;
/** @hide */
- public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
+ public static final int UNDEFINED_SELECTION_INDEX = -1;
/** @hide */
- public static final int ACTIVE_WINDOW_ID = UNDEFINED;
+ public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
+
+ /** @hide */
+ public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
+
+ /** @hide */
+ public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
/** @hide */
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
@@ -504,6 +510,13 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
+ // We changed the value for undefined node to positive due to wrong
+ // global id composition (two 32-bin ints into one 64-bit long) but
+ // the value used for the host node provider view has id -1 so we
+ // remap it here.
+ if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
+ virtualDescendantId = UNDEFINED_ITEM_ID;
+ }
return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
}
@@ -515,7 +528,7 @@ public class AccessibilityNodeInfo implements Parcelable {
private boolean mSealed;
// Data.
- private int mWindowId = UNDEFINED;
+ private int mWindowId = UNDEFINED_ITEM_ID;
private long mSourceNodeId = ROOT_NODE_ID;
private long mParentNodeId = ROOT_NODE_ID;
private long mLabelForId = ROOT_NODE_ID;
@@ -536,14 +549,14 @@ public class AccessibilityNodeInfo implements Parcelable {
private int mMovementGranularities;
- private int mTextSelectionStart = UNDEFINED;
- private int mTextSelectionEnd = UNDEFINED;
+ private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
+ private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
private int mInputType = InputType.TYPE_NULL;
private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
private Bundle mExtras;
- private int mConnectionId = UNDEFINED;
+ private int mConnectionId = UNDEFINED_CONNECTION_ID;
private RangeInfo mRangeInfo;
private CollectionInfo mCollectionInfo;
@@ -567,7 +580,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param source The info source.
*/
public void setSource(View source) {
- setSource(source, UNDEFINED);
+ setSource(source, UNDEFINED_ITEM_ID);
}
/**
@@ -591,9 +604,9 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
- mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
+ mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID;
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -766,7 +779,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void addChild(View child) {
- addChildInternal(child, UNDEFINED, true);
+ addChildInternal(child, UNDEFINED_ITEM_ID, true);
}
/**
@@ -776,7 +789,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public void addChildUnchecked(View child) {
- addChildInternal(child, UNDEFINED, false);
+ addChildInternal(child, UNDEFINED_ITEM_ID, false);
}
/**
@@ -794,7 +807,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public boolean removeChild(View child) {
- return removeChild(child, UNDEFINED);
+ return removeChild(child, UNDEFINED_ITEM_ID);
}
/**
@@ -821,7 +834,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mChildNodeIds = new LongArray();
}
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
// If we're checking uniqueness and the ID already exists, abort.
if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) {
@@ -847,7 +860,7 @@ public class AccessibilityNodeInfo implements Parcelable {
return false;
}
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
final int index = childIds.indexOf(childNodeId);
if (index < 0) {
@@ -1043,6 +1056,22 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets the window to which this node belongs.
+ *
+ * @return The window.
+ *
+ * @see android.accessibilityservice.AccessibilityService#getWindows()
+ */
+ public AccessibilityWindowInfo getWindow() {
+ enforceSealed();
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.getWindow(mConnectionId, mWindowId);
+ }
+
+ /**
* Gets the parent.
* <p>
* <strong>Note:</strong> It is a client responsibility to recycle the
@@ -1059,7 +1088,8 @@ public class AccessibilityNodeInfo implements Parcelable {
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1084,7 +1114,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setParent(View parent) {
- setParent(parent, UNDEFINED);
+ setParent(parent, UNDEFINED_ITEM_ID);
}
/**
@@ -1109,7 +1139,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setParent(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -1811,7 +1841,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param labeled The view for which this info serves as a label.
*/
public void setLabelFor(View labeled) {
- setLabelFor(labeled, UNDEFINED);
+ setLabelFor(labeled, UNDEFINED_ITEM_ID);
}
/**
@@ -1836,7 +1866,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setLabelFor(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId = (root != null)
- ? root.getAccessibilityViewId() : UNDEFINED;
+ ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -1858,7 +1888,8 @@ public class AccessibilityNodeInfo implements Parcelable {
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -1868,7 +1899,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param label The view that labels this node's source.
*/
public void setLabeledBy(View label) {
- setLabeledBy(label, UNDEFINED);
+ setLabeledBy(label, UNDEFINED_ITEM_ID);
}
/**
@@ -1893,7 +1924,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setLabeledBy(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId = (root != null)
- ? root.getAccessibilityViewId() : UNDEFINED;
+ ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -1915,7 +1946,8 @@ public class AccessibilityNodeInfo implements Parcelable {
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
}
/**
@@ -2362,6 +2394,7 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mChildNodeIds == null) {
mChildNodeIds = otherChildNodeIds.clone();
} else {
+ mChildNodeIds.clear();
mChildNodeIds.addAll(otherChildNodeIds);
}
}
@@ -2474,8 +2507,8 @@ public class AccessibilityNodeInfo implements Parcelable {
mParentNodeId = ROOT_NODE_ID;
mLabelForId = ROOT_NODE_ID;
mLabeledById = ROOT_NODE_ID;
- mWindowId = UNDEFINED;
- mConnectionId = UNDEFINED;
+ mWindowId = UNDEFINED_ITEM_ID;
+ mConnectionId = UNDEFINED_CONNECTION_ID;
mMovementGranularities = 0;
if (mChildNodeIds != null) {
mChildNodeIds.clear();
@@ -2489,8 +2522,8 @@ public class AccessibilityNodeInfo implements Parcelable {
mContentDescription = null;
mViewIdResourceName = null;
mActions = 0;
- mTextSelectionStart = UNDEFINED;
- mTextSelectionEnd = UNDEFINED;
+ mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
+ mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
mInputType = InputType.TYPE_NULL;
mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
if (mExtras != null) {
@@ -2583,9 +2616,9 @@ public class AccessibilityNodeInfo implements Parcelable {
}
private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
- return (mWindowId != UNDEFINED
- && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
- && mConnectionId != UNDEFINED);
+ return (mWindowId != UNDEFINED_ITEM_ID
+ && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID
+ && mConnectionId != UNDEFINED_CONNECTION_ID);
}
@Override
@@ -2625,6 +2658,7 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append(super.toString());
if (DEBUG) {
+ builder.append("; sourceNodeId: " + mSourceNodeId);
builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
builder.append("; mParentNodeId: " + mParentNodeId);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
deleted file mode 100644
index b4944be..0000000
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.accessibility;
-
-import android.os.Build;
-import android.util.Log;
-import android.util.LongArray;
-import android.util.LongSparseArray;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Simple cache for AccessibilityNodeInfos. The cache is mapping an
- * accessibility id to an info. The cache allows storing of
- * <code>null</code> values. It also tracks accessibility events
- * and invalidates accordingly.
- *
- * @hide
- */
-public class AccessibilityNodeInfoCache {
-
- private static final String LOG_TAG = AccessibilityNodeInfoCache.class.getSimpleName();
-
- private static final boolean ENABLED = true;
-
- private static final boolean DEBUG = false;
-
- private static final boolean CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD = true;
-
- private final Object mLock = new Object();
-
- private final LongSparseArray<AccessibilityNodeInfo> mCacheImpl;
-
- private int mWindowId;
-
- public AccessibilityNodeInfoCache() {
- if (ENABLED) {
- mCacheImpl = new LongSparseArray<AccessibilityNodeInfo>();
- } else {
- mCacheImpl = null;
- }
- }
-
- /**
- * The cache keeps track of {@link AccessibilityEvent}s and invalidates
- * cached nodes as appropriate.
- *
- * @param event An event.
- */
- public void onAccessibilityEvent(AccessibilityEvent event) {
- if (ENABLED) {
- final int eventType = event.getEventType();
- switch (eventType) {
- case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
- case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
- case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
- // If the active window changes, clear the cache.
- final int windowId = event.getWindowId();
- if (mWindowId != windowId) {
- mWindowId = windowId;
- clear();
- }
- } break;
- case AccessibilityEvent.TYPE_VIEW_FOCUSED:
- case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
- case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
- case AccessibilityEvent.TYPE_VIEW_SELECTED:
- case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
- case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
- refreshCachedNode(event.getSourceNodeId());
- } break;
- case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
- synchronized (mLock) {
- clearSubTreeLocked(event.getSourceNodeId());
- }
- } break;
- case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
- synchronized (mLock) {
- final long sourceId = event.getSourceNodeId();
- if ((event.getContentChangeTypes()
- & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
- clearSubTreeLocked(sourceId);
- } else {
- refreshCachedNode(sourceId);
- }
- }
- } break;
- }
- if (CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD && Build.IS_DEBUGGABLE) {
- checkIntegrity();
- }
- }
- }
-
- private void refreshCachedNode(long sourceId) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Refreshing cached node.");
- }
- synchronized (mLock) {
- AccessibilityNodeInfo cachedInfo = mCacheImpl.get(sourceId);
- // If the source is not in the cache - nothing to do.
- if (cachedInfo == null) {
- return;
- }
- // The node changed so we will just refresh it right now.
- if (cachedInfo.refresh(true)) {
- return;
- }
- // Weird, we could not refresh. Just evict the entire sub-tree.
- clearSubTreeLocked(sourceId);
- }
- }
-
- /**
- * Gets a cached {@link AccessibilityNodeInfo} given its accessibility node id.
- *
- * @param accessibilityNodeId The info accessibility node id.
- * @return The cached {@link AccessibilityNodeInfo} or null if such not found.
- */
- public AccessibilityNodeInfo get(long accessibilityNodeId) {
- if (ENABLED) {
- synchronized(mLock) {
- AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId);
- if (info != null) {
- // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
- // will wipe the data of the cached info.
- info = AccessibilityNodeInfo.obtain(info);
- }
- if (DEBUG) {
- Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
- }
- return info;
- }
- } else {
- return null;
- }
- }
-
- /**
- * Caches an {@link AccessibilityNodeInfo} given its accessibility node id.
- *
- * @param info The {@link AccessibilityNodeInfo} to cache.
- */
- public void add(AccessibilityNodeInfo info) {
- if (ENABLED) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.i(LOG_TAG, "add(" + info + ")");
- }
-
- final long sourceId = info.getSourceNodeId();
- AccessibilityNodeInfo oldInfo = mCacheImpl.get(sourceId);
- if (oldInfo != null) {
- // If the added node is in the cache we have to be careful if
- // the new one represents a source state where some of the
- // children have been removed to avoid having disconnected
- // subtrees in the cache.
- // TODO: Runs in O(n^2), could optimize to O(n + n log n)
- final LongArray newChildrenIds = info.getChildNodeIds();
- if (newChildrenIds != null) {
- final int oldChildCount = oldInfo.getChildCount();
- for (int i = 0; i < oldChildCount; i++) {
- final long oldChildId = oldInfo.getChildId(i);
- if (newChildrenIds.indexOf(oldChildId) < 0) {
- clearSubTreeLocked(oldChildId);
- }
- }
- }
-
- // Also be careful if the parent has changed since the new
- // parent may be a predecessor of the old parent which will
- // make the cached tree cyclic.
- final long oldParentId = oldInfo.getParentNodeId();
- if (info.getParentNodeId() != oldParentId) {
- clearSubTreeLocked(oldParentId);
- }
- }
-
- // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
- // will wipe the data of the cached info.
- AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
- mCacheImpl.put(sourceId, clone);
- }
- }
- }
-
- /**
- * Clears the cache.
- */
- public void clear() {
- if (ENABLED) {
- synchronized(mLock) {
- if (DEBUG) {
- Log.i(LOG_TAG, "clear()");
- }
- // Recycle the nodes before clearing the cache.
- final int nodeCount = mCacheImpl.size();
- for (int i = 0; i < nodeCount; i++) {
- AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
- info.recycle();
- }
- mCacheImpl.clear();
- }
- }
- }
-
- /**
- * Clears a subtree rooted at the node with the given id.
- *
- * @param rootNodeId The root id.
- */
- private void clearSubTreeLocked(long rootNodeId) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Clearing cached subtree.");
- }
- clearSubTreeRecursiveLocked(rootNodeId);
- }
-
- private void clearSubTreeRecursiveLocked(long rootNodeId) {
- AccessibilityNodeInfo current = mCacheImpl.get(rootNodeId);
- if (current == null) {
- return;
- }
- mCacheImpl.remove(rootNodeId);
- final int childCount = current.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final long childNodeId = current.getChildId(i);
- clearSubTreeRecursiveLocked(childNodeId);
- }
- }
-
- /**
- * Check the integrity of the cache which is it does not have nodes
- * from more than one window, there are no duplicates, all nodes are
- * connected, there is a single input focused node, and there is a
- * single accessibility focused node.
- */
- private void checkIntegrity() {
- synchronized (mLock) {
- // Get the root.
- if (mCacheImpl.size() <= 0) {
- return;
- }
-
- // If the cache is a tree it does not matter from
- // which node we start to search for the root.
- AccessibilityNodeInfo root = mCacheImpl.valueAt(0);
- AccessibilityNodeInfo parent = root;
- while (parent != null) {
- root = parent;
- parent = mCacheImpl.get(parent.getParentNodeId());
- }
-
- // Traverse the tree and do some checks.
- final int windowId = root.getWindowId();
- AccessibilityNodeInfo accessFocus = null;
- AccessibilityNodeInfo inputFocus = null;
- HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
- Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
- fringe.add(root);
-
- while (!fringe.isEmpty()) {
- AccessibilityNodeInfo current = fringe.poll();
- // Check for duplicates
- if (!seen.add(current)) {
- Log.e(LOG_TAG, "Duplicate node: " + current);
- return;
- }
-
- // Check for one accessibility focus.
- if (current.isAccessibilityFocused()) {
- if (accessFocus != null) {
- Log.e(LOG_TAG, "Duplicate accessibility focus:" + current);
- } else {
- accessFocus = current;
- }
- }
-
- // Check for one input focus.
- if (current.isFocused()) {
- if (inputFocus != null) {
- Log.e(LOG_TAG, "Duplicate input focus: " + current);
- } else {
- inputFocus = current;
- }
- }
-
- final int childCount = current.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final long childId = current.getChildId(i);
- final AccessibilityNodeInfo child = mCacheImpl.get(childId);
- if (child != null) {
- fringe.add(child);
- }
- }
- }
-
- int disconnectedNodeCount = 0;
- // Check for disconnected nodes or ones from another window.
- for (int i = 0; i < mCacheImpl.size(); i++) {
- AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
- if (!seen.contains(info)) {
- if (info.getWindowId() == windowId) {
- if (DEBUG) {
- Log.e(LOG_TAG, "Disconnected node: " + info);
- }
- disconnectedNodeCount++;
- } else {
- Log.e(LOG_TAG, "Node from: " + info.getWindowId() + " not from:"
- + windowId + " " + info);
- }
- }
- }
- if (disconnectedNodeCount > 0) {
- Log.e(LOG_TAG, String.format("Found %d disconnected nodes", disconnectedNodeCount));
- }
- }
- }
-}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 718c32f..abcbb70 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -70,9 +70,14 @@ import java.util.List;
public abstract class AccessibilityNodeProvider {
/**
+ * The virtual id for the hosting View.
+ */
+ public static final int HOST_VIEW_ID = -1;
+
+ /**
* Returns an {@link AccessibilityNodeInfo} representing a virtual view,
* i.e. a descendant of the host View, with the given <code>virtualViewId</code>
- * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
* <p>
* A virtual descendant is an imaginary View that is reported as a part of the view
* hierarchy for accessibility purposes. This enables custom views that draw complex
@@ -99,7 +104,7 @@ public abstract class AccessibilityNodeProvider {
/**
* Performs an accessibility action on a virtual view, i.e. a descendant of the
* host View, with the given <code>virtualViewId</code> or the host View itself
- * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
*
* @param virtualViewId A client defined virtual view id.
* @param action The action to perform.
@@ -117,8 +122,8 @@ public abstract class AccessibilityNodeProvider {
/**
* Finds {@link AccessibilityNodeInfo}s by text. The match is case insensitive
* containment. The search is relative to the virtual view, i.e. a descendant of the
- * host View, with the given <code>virtualViewId</code> or the host View itself
- * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+ * host View, with the given <code>virtualViewId</code> or the host View itself
+ * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
*
* @param virtualViewId A client defined virtual view id which defined
* the root of the tree in which to perform the search.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 3fcd218..cc6a71d 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -78,7 +78,7 @@ public class AccessibilityRecord {
private boolean mIsInPool;
boolean mSealed;
- int mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
+ int mBooleanProperties = 0;
int mCurrentItemIndex = UNDEFINED;
int mItemCount = UNDEFINED;
int mFromIndex = UNDEFINED;
@@ -791,7 +791,7 @@ public class AccessibilityRecord {
*/
void clear() {
mSealed = false;
- mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
+ mBooleanProperties = 0;
mCurrentItemIndex = UNDEFINED;
mItemCount = UNDEFINED;
mFromIndex = UNDEFINED;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
new file mode 100644
index 0000000..fdb25fb
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+parcelable AccessibilityWindowInfo;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
new file mode 100644
index 0000000..80b5c50
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.LongArray;
+import android.util.Pools.SynchronizedPool;
+
+/**
+ * This class represents a state snapshot of a window for accessibility
+ * purposes. The screen content contains one or more windows where some
+ * windows can be descendants of other windows, which is the windows are
+ * hierarchically ordered. Note that there is no root window. Hence, the
+ * screen content can be seen as a collection of window trees.
+ */
+public final class AccessibilityWindowInfo implements Parcelable {
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Window type: This is an application window. Such a window shows UI for
+ * interacting with an application.
+ */
+ public static final int TYPE_APPLICATION = 1;
+
+ /**
+ * Window type: This is an input method window. Such a window shows UI for
+ * inputting text such as keyboard, suggestions, etc.
+ */
+ public static final int TYPE_INPUT_METHOD = 2;
+
+ /**
+ * Window type: This is an system window. Such a window shows UI for
+ * interacting with the system.
+ */
+ public static final int TYPE_SYSTEM = 3;
+
+ private static final int UNDEFINED = -1;
+
+ private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
+ private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
+
+ // Housekeeping.
+ private static final int MAX_POOL_SIZE = 10;
+ private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
+ new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
+
+ // Data.
+ private int mType = UNDEFINED;
+ private int mLayer = UNDEFINED;
+ private int mBooleanProperties;
+ private int mId = UNDEFINED;
+ private int mParentId = UNDEFINED;
+ private final Rect mBoundsInScreen = new Rect();
+ private LongArray mChildIds;
+
+ private int mConnectionId = UNDEFINED;
+
+ private AccessibilityWindowInfo() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Gets the type of the window.
+ *
+ * @return The type.
+ *
+ * @see #TYPE_APPLICATION
+ * @see #TYPE_INPUT_METHOD
+ * @see #TYPE_SYSTEM
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of the window.
+ *
+ * @param The type
+ *
+ * @hide
+ */
+ public void setType(int type) {
+ mType = type;
+ }
+
+ /**
+ * Gets the layer which determines the Z-order of the window. Windows
+ * with greater layer appear on top of windows with lesser layer.
+ *
+ * @return The window layer.
+ */
+ public int getLayer() {
+ return mLayer;
+ }
+
+ /**
+ * Sets the layer which determines the Z-order of the window. Windows
+ * with greater layer appear on top of windows with lesser layer.
+ *
+ * @param The window layer.
+ *
+ * @hide
+ */
+ public void setLayer(int layer) {
+ mLayer = layer;
+ }
+
+ /**
+ * Gets the root node in the window's hierarchy.
+ *
+ * @return The root node.
+ */
+ public AccessibilityNodeInfo getRoot() {
+ if (mConnectionId == UNDEFINED) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
+ mId, AccessibilityNodeInfo.ROOT_NODE_ID,
+ true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+ }
+
+ /**
+ * Gets the parent window if such.
+ *
+ * @return The parent window.
+ */
+ public AccessibilityWindowInfo getParent() {
+ if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) {
+ return null;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.getWindow(mConnectionId, mParentId);
+ }
+
+ /**
+ * Sets the parent window id.
+ *
+ * @param parentId The parent id.
+ *
+ * @hide
+ */
+ public void setParentId(int parentId) {
+ mParentId = parentId;
+ }
+
+ /**
+ * Gets the unique window id.
+ *
+ * @return windowId The window id.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Sets the unique window id.
+ *
+ * @param windowId The window id.
+ *
+ * @hide
+ */
+ public void setId(int id) {
+ mId = id;
+ }
+
+ /**
+ * Sets the unique id of the IAccessibilityServiceConnection over which
+ * this instance can send requests to the system.
+ *
+ * @param connectionId The connection id.
+ *
+ * @hide
+ */
+ public void setConnectionId(int connectionId) {
+ mConnectionId = connectionId;
+ }
+
+ /**
+ * Gets the bounds of this window in the screen.
+ *
+ * @param outBounds The out window bounds.
+ */
+ public void getBoundsInScreen(Rect outBounds) {
+ outBounds.set(mBoundsInScreen);
+ }
+
+ /**
+ * Sets the bounds of this window in the screen.
+ *
+ * @param bounds The out window bounds.
+ *
+ * @hide
+ */
+ public void setBoundsInScreen(Rect bounds) {
+ mBoundsInScreen.set(bounds);
+ }
+
+ /**
+ * Gets if this window is active. An active window is the one
+ * the user is currently touching or the window has input focus
+ * and the user is not touching any window.
+ *
+ * @return Whether this is the active window.
+ */
+ public boolean isActive() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE);
+ }
+
+ /**
+ * Sets if this window is active, which is this is the window
+ * the user is currently touching or the window has input focus
+ * and the user is not touching any window.
+ *
+ * @param Whether this is the active window.
+ *
+ * @hide
+ */
+ public void setActive(boolean active) {
+ setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active);
+ }
+
+ /**
+ * Gets if this window has input focus.
+ *
+ * @return Whether has input focus.
+ */
+ public boolean isFocused() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
+ }
+
+ /**
+ * Sets if this window has input focus.
+ *
+ * @param Whether has input focus.
+ *
+ * @hide
+ */
+ public void setFocused(boolean focused) {
+ setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
+ }
+
+ /**
+ * Gets the number of child windows.
+ *
+ * @return The child count.
+ */
+ public int getChildCount() {
+ return (mChildIds != null) ? mChildIds.size() : 0;
+ }
+
+ /**
+ * Gets the child window at a given index.
+ *
+ * @param index The index.
+ * @return The child.
+ */
+ public AccessibilityWindowInfo getChild(int index) {
+ if (mChildIds == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (mConnectionId == UNDEFINED) {
+ return null;
+ }
+ final int childId = (int) mChildIds.get(index);
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.getWindow(mConnectionId, childId);
+ }
+
+ /**
+ * Adds a child window.
+ *
+ * @param childId The child window id.
+ *
+ * @hide
+ */
+ public void addChild(int childId) {
+ if (mChildIds == null) {
+ mChildIds = new LongArray();
+ }
+ mChildIds.add(childId);
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * created.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityWindowInfo obtain() {
+ AccessibilityWindowInfo info = sPool.acquire();
+ if (info == null) {
+ info = new AccessibilityWindowInfo();
+ }
+ return info;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * created. The returned instance is initialized from the given
+ * <code>info</code>.
+ *
+ * @param info The other info.
+ * @return An instance.
+ */
+ public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
+ AccessibilityWindowInfo infoClone = obtain();
+
+ infoClone.mType = info.mType;
+ infoClone.mLayer = info.mLayer;
+ infoClone.mBooleanProperties = info.mBooleanProperties;
+ infoClone.mId = info.mId;
+ infoClone.mParentId = info.mParentId;
+ infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
+
+ if (info.mChildIds != null && info.mChildIds.size() > 0) {
+ if (infoClone.mChildIds == null) {
+ infoClone.mChildIds = info.mChildIds.clone();
+ } else {
+ infoClone.mChildIds.addAll(info.mChildIds);
+ }
+ }
+
+ infoClone.mConnectionId = info.mConnectionId;
+
+ return infoClone;
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <strong>Note:</strong> You must not touch the object after calling this function.
+ * </p>
+ *
+ * @throws IllegalStateException If the info is already recycled.
+ */
+ public void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mLayer);
+ parcel.writeInt(mBooleanProperties);
+ parcel.writeInt(mId);
+ parcel.writeInt(mParentId);
+ mBoundsInScreen.writeToParcel(parcel, flags);
+
+ final LongArray childIds = mChildIds;
+ if (childIds == null) {
+ parcel.writeInt(0);
+ } else {
+ final int childCount = childIds.size();
+ parcel.writeInt(childCount);
+ for (int i = 0; i < childCount; i++) {
+ parcel.writeInt((int) childIds.get(i));
+ }
+ }
+
+ parcel.writeInt(mConnectionId);
+ }
+
+ private void initFromParcel(Parcel parcel) {
+ mType = parcel.readInt();
+ mLayer = parcel.readInt();
+ mBooleanProperties = parcel.readInt();
+ mId = parcel.readInt();
+ mParentId = parcel.readInt();
+ mBoundsInScreen.readFromParcel(parcel);
+
+ final int childCount = parcel.readInt();
+ if (childCount > 0) {
+ if (mChildIds == null) {
+ mChildIds = new LongArray(childCount);
+ }
+ for (int i = 0; i < childCount; i++) {
+ final int childId = parcel.readInt();
+ mChildIds.add(childId);
+ }
+ }
+
+ mConnectionId = parcel.readInt();
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj;
+ return (mId == other.mId);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("AccessibilityWindowInfo[");
+ builder.append("id=").append(mId);
+ builder.append(", type=").append(typeToString(mType));
+ builder.append(", layer=").append(mLayer);
+ builder.append(", bounds=").append(mBoundsInScreen);
+ builder.append(", focused=").append(isFocused());
+ builder.append(", active=").append(isActive());
+ if (DEBUG) {
+ builder.append(", parent=").append(mParentId);
+ builder.append(", children=[");
+ if (mChildIds != null) {
+ final int childCount = mChildIds.size();
+ for (int i = 0; i < childCount; i++) {
+ builder.append(mChildIds.get(i));
+ if (i < childCount - 1) {
+ builder.append(',');
+ }
+ }
+ } else {
+ builder.append("null");
+ }
+ builder.append(']');
+ } else {
+ builder.append(", hasParent=").append(mParentId != UNDEFINED);
+ builder.append(", hasChildren=").append(mChildIds != null
+ && mChildIds.size() > 0);
+ }
+ builder.append(']');
+ return builder.toString();
+ }
+
+ /**
+ * Clears the internal state.
+ */
+ private void clear() {
+ mType = UNDEFINED;
+ mLayer = UNDEFINED;
+ mBooleanProperties = 0;
+ mId = UNDEFINED;
+ mParentId = UNDEFINED;
+ mBoundsInScreen.setEmpty();
+ if (mChildIds != null) {
+ mChildIds.clear();
+ }
+ mConnectionId = UNDEFINED;
+ }
+
+ /**
+ * Gets the value of a boolean property.
+ *
+ * @param property The property.
+ * @return The value.
+ */
+ private boolean getBooleanProperty(int property) {
+ return (mBooleanProperties & property) != 0;
+ }
+
+ /**
+ * Sets a boolean property.
+ *
+ * @param property The property.
+ * @param value The value.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ private void setBooleanProperty(int property, boolean value) {
+ if (value) {
+ mBooleanProperties |= property;
+ } else {
+ mBooleanProperties &= ~property;
+ }
+ }
+
+ private static String typeToString(int type) {
+ switch (type) {
+ case TYPE_APPLICATION: {
+ return "TYPE_APPLICATION";
+ }
+ case TYPE_INPUT_METHOD: {
+ return "TYPE_INPUT_METHOD";
+ }
+ case TYPE_SYSTEM: {
+ return "TYPE_SYSTEM";
+ }
+ default:
+ return "<UNKNOWN>";
+ }
+ }
+
+ /**
+ * Checks whether this window changed. The argument should be
+ * another state of the same window, which is have the same id
+ * and type as they never change.
+ *
+ * @param other The new state.
+ * @return Whether something changed.
+ *
+ * @hide
+ */
+ public boolean changed(AccessibilityWindowInfo other) {
+ if (other.mId != mId) {
+ throw new IllegalArgumentException("Not same window.");
+ }
+ if (other.mType != mType) {
+ throw new IllegalArgumentException("Not same type.");
+ }
+ if (!mBoundsInScreen.equals(mBoundsInScreen)) {
+ return true;
+ }
+ if (mLayer != other.mLayer) {
+ return true;
+ }
+ if (mBooleanProperties != other.mBooleanProperties) {
+ return true;
+ }
+ if (mParentId != other.mParentId) {
+ return true;
+ }
+ if (mChildIds == null) {
+ if (other.mChildIds != null) {
+ return true;
+ }
+ } else if (!mChildIds.equals(other.mChildIds)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
+ new Creator<AccessibilityWindowInfo>() {
+ @Override
+ public AccessibilityWindowInfo createFromParcel(Parcel parcel) {
+ AccessibilityWindowInfo info = obtain();
+ info.initFromParcel(parcel);
+ return info;
+ }
+
+ @Override
+ public AccessibilityWindowInfo[] newArray(int size) {
+ return new AccessibilityWindowInfo[size];
+ }
+ };
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a1a6bba..37ef539 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1913,24 +1913,12 @@
android:description="@string/permdesc_filter_events"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to retrieve info for a window from the window manager. -->
- <permission android:name="android.permission.RETRIEVE_WINDOW_INFO"
- android:label="@string/permlab_retrieve_window_info"
- android:description="@string/permdesc_retrieve_window_info"
- android:protectionLevel="signature" />
-
<!-- @hide Allows an application to temporary enable accessibility on the device. -->
<permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
android:label="@string/permlab_temporary_enable_accessibility"
android:description="@string/permdesc_temporary_enable_accessibility"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to magnify the content of a display. -->
- <permission android:name="android.permission.MAGNIFY_DISPLAY"
- android:label="@string/permlab_magnify_display"
- android:description="@string/permdesc_magnify_display"
- android:protectionLevel="signature" />
-
<!-- Allows an application to watch and control how activities are
started globally in the system. Only for is in debugging
(usually the monkey command).
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8482fdb..582ed1b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2641,6 +2641,8 @@
<flag name="flagReportViewIds" value="0x00000010" />
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FILTER_KEY_EVENTS} -->
<flag name="flagRequestFilterKeyEvents" value="0x00000020" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} -->
+ <flag name="flagRetrieveInteractiveWindows" value="0x00000040" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f1bcf65..0699e8b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -830,13 +830,6 @@
user consent.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_retrieve_window_info">retrieve window info</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_retrieve_window_info">Allows an application to retrieve
- information about the the windows from the window manager. Malicious apps may
- retrieve information that is intended for internal system usage.</string>
-
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_filter_events">filter events</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_filter_events">Allows an application to register an input filter
@@ -844,13 +837,6 @@
may control the system UI whtout user intervention.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_magnify_display">magnify display</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_magnify_display">Allows an application to magnify the content of a
- display. Malicious apps may transform the display content in a way that renders the
- device unusable.</string>
-
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_shutdown">partial shutdown</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_shutdown">Puts the activity manager into a shutdown
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index afa68da..3c23c6e 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -91,6 +91,7 @@ import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -212,6 +213,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
SearchManager mSearchManager;
+ AccessibilityManager mAccessibilityManager;
// Vibrator pattern for haptic feedback of a long press.
long[] mLongPressVibePattern;
@@ -299,7 +301,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mOrientationSensorEnabled = false;
int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mHasSoftInput = false;
- boolean mTouchExplorationEnabled = false;
boolean mTranslucentDecorEnabled = true;
int mPointerLocationMode = 0; // guarded by mLock
@@ -905,6 +906,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
com.android.internal.R.bool.config_enableTranslucentDecor);
readConfigurationDependentBehaviors();
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+
// register for dock events
IntentFilter filter = new IntentFilter();
filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
@@ -1105,7 +1109,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* navigation bar and touch exploration is not enabled
*/
private boolean canHideNavigationBar() {
- return mHasNavigationBar && !mTouchExplorationEnabled;
+ return mHasNavigationBar
+ && !mAccessibilityManager.isTouchExplorationEnabled();
}
@Override
@@ -5246,7 +5251,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* R.boolean.config_enableTranslucentDecor is false.
*/
private boolean areTranslucentBarsAllowed() {
- return mTranslucentDecorEnabled && !mTouchExplorationEnabled;
+ return mTranslucentDecorEnabled
+ && !mAccessibilityManager.isTouchExplorationEnabled();
}
// Use this instead of checking config_showNavigationBar so that it can be consistently
@@ -5297,11 +5303,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void setTouchExplorationEnabled(boolean enabled) {
- mTouchExplorationEnabled = enabled;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
&& windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 959d4a9..0edce11 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,7 +44,6 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.net.Uri;
-import android.opengl.Matrix;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -52,7 +51,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -63,24 +61,27 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
+import android.util.LongArray;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
-import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEventConsistencyVerifier;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
+import android.view.WindowInfo;
import android.view.WindowManager;
+import android.view.WindowManagerInternal;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IAccessibilityManager;
@@ -89,6 +90,7 @@ import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParserException;
@@ -166,7 +168,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final PackageManager mPackageManager;
- private final IWindowManager mWindowManagerService;
+ private final WindowManagerInternal mWindowManagerService;
private final SecurityPolicy mSecurityPolicy;
@@ -197,9 +199,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private int mCurrentUserId = UserHandle.USER_OWNER;
+ private final LongArray mTempLongArray = new LongArray();
+
//TODO: Remove this hack
private boolean mInitialized;
+ private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback;
+
private UserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -221,7 +227,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
- mWindowManagerService = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
+ mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper());
//TODO: (multi-display) We need to support multiple displays.
@@ -390,7 +396,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return true; // yes, recycle the event
}
- if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
+ if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
mSecurityPolicy.updateEventSourceLocked(event);
mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_ACTIVE_WINDOW,
event.getWindowId(), event.getEventType()).sendToTarget();
@@ -632,11 +638,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
- try {
- if (!mWindowManagerService.isKeyguardLocked()) {
- return;
- }
- } catch (RemoteException re) {
+ if (!mWindowManagerService.isKeyguardLocked()) {
return;
}
synchronized (mLock) {
@@ -739,6 +741,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param outBounds The output to which to write the bounds.
*/
boolean getActiveWindowBounds(Rect outBounds) {
+ // TODO: This should be refactored to work with accessibility
+ // focus in multiple windows.
IBinder token;
synchronized (mLock) {
final int windowId = mSecurityPolicy.mActiveWindowId;
@@ -747,13 +751,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
token = getCurrentUserStateLocked().mWindowTokens.get(windowId);
}
}
- try {
- mWindowManagerService.getWindowFrame(token, outBounds);
- if (!outBounds.isEmpty()) {
- return true;
- }
- } catch (RemoteException re) {
- /* ignore */
+ mWindowManagerService.getWindowFrame(token, outBounds);
+ if (!outBounds.isEmpty()) {
+ return true;
}
return false;
}
@@ -771,7 +771,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
void onMagnificationStateChanged() {
- notifyClearAccessibilityNodeInfoCacheLocked();
+ notifyClearAccessibilityCacheLocked();
}
private void switchUser(int userId) {
@@ -879,7 +879,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
- private void notifyClearAccessibilityNodeInfoCacheLocked() {
+ private void notifyClearAccessibilityCacheLocked() {
UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
Service service = state.mBoundServices.get(i);
@@ -887,6 +887,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
+ UserState state = getCurrentUserStateLocked();
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ Service service = state.mBoundServices.get(i);
+ if (mSecurityPolicy.canRetrieveWindowsLocked(service)) {
+ service.notifyWindowsChangedLocked(windows);
+ }
+ }
+ }
+
/**
* Removes an AccessibilityInteractionConnection.
*
@@ -994,7 +1004,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Service service = state.mBoundServices.get(i);
if (service.mIsDefault == isDefault) {
- if (canDispathEventLocked(service, event, state.mHandledFeedbackTypes)) {
+ if (canDispatchEventToServiceLocked(service, event,
+ state.mHandledFeedbackTypes)) {
state.mHandledFeedbackTypes |= service.mFeedbackType;
service.notifyAccessibilityEvent(event);
}
@@ -1043,7 +1054,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param handledFeedbackTypes The feedback types for which services have been notified.
* @return True if the listener should be notified, false otherwise.
*/
- private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
+ private boolean canDispatchEventToServiceLocked(Service service, AccessibilityEvent event,
int handledFeedbackTypes) {
if (!service.canReceiveEventsLocked()) {
@@ -1232,11 +1243,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
if (setInputFilter) {
- try {
- mWindowManagerService.setInputFilter(inputFilter);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManagerService.setInputFilter(inputFilter);
}
}
@@ -1296,6 +1303,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mInitialized = true;
updateLegacyCapabilities(userState);
updateServicesLocked(userState);
+ updateWindowsForAccessibilityCallback(userState);
updateFilterKeyEventsLocked(userState);
updateTouchExplorationLocked(userState);
updateEnhancedWebAccessibilityLocked(userState);
@@ -1304,6 +1312,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
scheduleUpdateClientsIfNeededLocked(userState);
}
+ private void updateWindowsForAccessibilityCallback(UserState userState) {
+ if (userState.mIsAccessibilityEnabled) {
+ // We observe windows for accessibility only if there is at least
+ // one bound service that can retrieve window content that specified
+ // it is interested in accessing such windows. For services that are
+ // binding we do an update pass after each bind event, so we run this
+ // code and register the callback if needed.
+ boolean boundServiceCanRetrieveInteractiveWindows = false;
+
+ List<Service> boundServices = userState.mBoundServices;
+ final int boundServiceCount = boundServices.size();
+ for (int i = 0; i < boundServiceCount; i++) {
+ Service boundService = boundServices.get(i);
+ if (mSecurityPolicy.canRetrieveWindowContentLocked(boundService)
+ && boundService.mRetrieveInteractiveWindows) {
+ boundServiceCanRetrieveInteractiveWindows = true;
+ break;
+ }
+ }
+
+ if (boundServiceCanRetrieveInteractiveWindows) {
+ if (mWindowsForAccessibilityCallback == null) {
+ mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback();
+ mWindowManagerService.setWindowsForAccessibilityCallback(
+ mWindowsForAccessibilityCallback);
+ }
+ return;
+ }
+ }
+
+ if (mWindowsForAccessibilityCallback != null) {
+ mWindowsForAccessibilityCallback = null;
+ mWindowManagerService.setWindowsForAccessibilityCallback(
+ mWindowsForAccessibilityCallback);
+ }
+ }
+
private void updateLegacyCapabilities(UserState userState) {
// Up to JB-MR1 we had a white list with services that can enable touch
// exploration. When a service is first started we show a dialog to the
@@ -1435,11 +1480,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0,
userState.mUserId);
}
- try {
- mWindowManagerService.setTouchExplorationEnabled(enabled);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
}
private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
@@ -1605,6 +1645,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
event.recycle();
} break;
+
case MSG_SEND_KEY_EVENT_TO_INPUT_FILTER: {
KeyEvent event = (KeyEvent) msg.obj;
final int policyFlags = msg.arg1;
@@ -1615,28 +1656,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
event.recycle();
} break;
+
case MSG_SEND_STATE_TO_CLIENTS: {
final int clientState = msg.arg1;
final int userId = msg.arg2;
sendStateToClients(clientState, mGlobalClients);
sendStateToClientsForUser(clientState, userId);
} break;
+
case MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER: {
final int userId = msg.arg1;
sendStateToClientsForUser(0, userId);
} break;
+
case MSG_UPDATE_ACTIVE_WINDOW: {
final int windowId = msg.arg1;
final int eventType = msg.arg2;
mSecurityPolicy.updateActiveWindow(windowId, eventType);
} break;
+
case MSG_ANNOUNCE_NEW_USER_IF_NEEDED: {
announceNewUserIfNeeded();
} break;
+
case MSG_UPDATE_INPUT_FILTER: {
UserState userState = (UserState) msg.obj;
updateInputFilter(userState);
} break;
+
case MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG: {
Service service = (Service) msg.obj;
showEnableTouchExplorationDialog(service);
@@ -1655,7 +1702,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.getText().add(message);
- event.setWindowId(mSecurityPolicy.getRetrievalAllowingWindowLocked());
sendAccessibilityEvent(event, mCurrentUserId);
}
}
@@ -1703,6 +1749,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mPendingEventPool.release(pendingEvent);
}
+ private int findWindowIdLocked(IBinder token) {
+ final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
+ if (globalIndex >= 0) {
+ return mGlobalWindowTokens.keyAt(globalIndex);
+ }
+ UserState userState = getCurrentUserStateLocked();
+ final int userIndex = userState.mWindowTokens.indexOfValue(token);
+ if (userIndex >= 0) {
+ return userState.mWindowTokens.keyAt(userIndex);
+ }
+ return -1;
+ }
+
/**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
@@ -1738,6 +1797,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mRequestFilterKeyEvents;
+ boolean mRetrieveInteractiveWindows;
+
int mFetchFlags;
long mNotificationTimeout;
@@ -1748,7 +1809,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mIsAutomation;
- final Rect mTempBounds = new Rect();
+ final Rect mTempBounds1 = new Rect();
+
+ final Rect mTempBounds2 = new Rect();
final ResolveInfo mResolveInfo;
@@ -1758,6 +1821,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
+ final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows =
+ new SparseArray<AccessibilityWindowInfo>();
+
boolean mWasConnectedAndDied;
// Handler only for dispatching accessibility events since we use event
@@ -1822,7 +1888,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mRequestEnhancedWebAccessibility = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0;
mRequestFilterKeyEvents = (info.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+ & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+ mRetrieveInteractiveWindows = (info.flags
+ & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+
+ if (!mRetrieveInteractiveWindows) {
+ clearIntrospectedWindows();
+ }
}
/**
@@ -1932,6 +2004,59 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
@Override
+ public List<AccessibilityWindowInfo> getWindows() {
+ synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return null;
+ }
+ final boolean permissionGranted =
+ mSecurityPolicy.canRetrieveWindowsLocked(this);
+ if (!permissionGranted) {
+ return null;
+ }
+ List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+ final int windowCount = mSecurityPolicy.mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i);
+ AccessibilityWindowInfo windowClone =
+ AccessibilityWindowInfo.obtain(window);
+ windowClone.setConnectionId(mId);
+ mIntrospectedWindows.put(window.getId(), windowClone);
+ windows.add(windowClone);
+ }
+ return windows;
+ }
+ }
+
+ @Override
+ public AccessibilityWindowInfo getWindow(int windowId) {
+ synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return null;
+ }
+ final boolean permissionGranted =
+ mSecurityPolicy.canRetrieveWindowsLocked(this);
+ if (!permissionGranted) {
+ return null;
+ }
+ AccessibilityWindowInfo window = mSecurityPolicy.findWindowById(windowId);
+ if (window != null) {
+ AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
+ windowClone.setConnectionId(mId);
+ mIntrospectedWindows.put(windowId, windowClone);
+ return windowClone;
+ }
+ return null;
+ }
+ }
+
+ @Override
public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1945,12 +2070,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
- final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
+ final boolean permissionGranted =
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
return false;
} else {
- resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
return false;
@@ -1989,7 +2114,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2034,7 +2158,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2079,7 +2202,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2123,7 +2245,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2167,7 +2288,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
resolvedWindowId, action, arguments);
@@ -2365,7 +2485,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
mPendingEvents.remove(eventType);
- if (mSecurityPolicy.canRetrieveWindowContent(this)) {
+ if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
event.setConnectionId(mId);
} else {
event.setSource(null);
@@ -2396,12 +2516,69 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
public void notifyClearAccessibilityNodeInfoCache() {
+ clearIntrospectedWindows();
mInvocationHandler.sendEmptyMessage(
- InvocationHandler.MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+ InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
+ }
+
+ private void clearIntrospectedWindows() {
+ final int windowCount = mIntrospectedWindows.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ mIntrospectedWindows.valueAt(i).recycle();
+ mIntrospectedWindows.removeAt(i);
+ }
+ }
+
+ public void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
+ LongArray changedWindows = mTempLongArray;
+ changedWindows.clear();
+
+ // Figure out which windows the service cares about changed.
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo newState = windows.get(i);
+ final int windowId = newState.getId();
+ AccessibilityWindowInfo oldState = mIntrospectedWindows.get(windowId);
+ if (oldState != null && oldState.changed(newState)) {
+ oldState.recycle();
+ mIntrospectedWindows.put(newState.getId(),
+ AccessibilityWindowInfo.obtain(newState));
+ changedWindows.add(windowId);
+ }
+ }
+
+ // Figure out which windows the service cares about went away.
+ final int introspectedWindowCount = mIntrospectedWindows.size();
+ for (int i = introspectedWindowCount - 1; i >= 0; i--) {
+ AccessibilityWindowInfo window = mIntrospectedWindows.valueAt(i);
+ if (changedWindows.indexOf(window.getId()) < 0 && !windows.contains(window)) {
+ changedWindows.add(window.getId());
+ mIntrospectedWindows.removeAt(i);
+ window.recycle();
+ }
+ }
+
+ int[] windowIds = null;
+
+ final int changedWindowCount = changedWindows.size();
+ if (changedWindowCount > 0) {
+ windowIds = new int[changedWindowCount];
+ for (int i = 0; i < changedWindowCount; i++) {
+ // Cast is fine as we use long array to cache ints.
+ windowIds[i] = (int) changedWindows.get(i);
+ }
+ changedWindows.clear();
+ }
+
+ mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_WINDOWS_CHANGED,
+ windowIds).sendToTarget();
}
private void notifyGestureInternal(int gestureId) {
- IAccessibilityServiceClient listener = mServiceInterface;
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
if (listener != null) {
try {
listener.onGesture(gestureId);
@@ -2416,11 +2593,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
}
- private void notifyClearAccessibilityNodeInfoCacheInternal() {
- IAccessibilityServiceClient listener = mServiceInterface;
+ private void notifyClearAccessibilityCacheInternal() {
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
if (listener != null) {
try {
- listener.clearAccessibilityNodeInfoCache();
+ listener.clearAccessibilityCache();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
+ " to be cleared.", re);
@@ -2428,6 +2608,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ private void notifyWindowsChangedInternal(int[] windowIds) {
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
+ if (listener != null) {
+ try {
+ listener.onWindowsChanged(windowIds);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error during sending windows to: " + mService, re);
+ }
+ }
+ }
+
private void sendDownAndUpKeyEvents(int keyCode) {
final long token = Binder.clearCallingIdentity();
@@ -2511,27 +2705,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private MagnificationSpec getCompatibleMagnificationSpec(int windowId) {
- try {
- IBinder windowToken = mGlobalWindowTokens.get(windowId);
- if (windowToken == null) {
- windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
- }
- if (windowToken != null) {
- return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
- windowToken);
- }
- } catch (RemoteException re) {
- /* ignore */
+ IBinder windowToken = mGlobalWindowTokens.get(windowId);
+ if (windowToken == null) {
+ windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ }
+ if (windowToken != null) {
+ return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
+ windowToken);
}
return null;
}
private final class InvocationHandler extends Handler {
-
public static final int MSG_ON_GESTURE = 1;
public static final int MSG_ON_KEY_EVENT = 2;
- public static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 3;
+ public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
+ public static final int MSG_ON_WINDOWS_CHANGED = 5;
public InvocationHandler(Looper looper) {
super(looper, null, true);
@@ -2545,18 +2735,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int gestureId = message.arg1;
notifyGestureInternal(gestureId);
} break;
+
case MSG_ON_KEY_EVENT: {
KeyEvent event = (KeyEvent) message.obj;
final int policyFlags = message.arg1;
notifyKeyEventInternal(event, policyFlags);
} break;
- case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
- notifyClearAccessibilityNodeInfoCacheInternal();
+
+ case MSG_CLEAR_ACCESSIBILITY_CACHE: {
+ notifyClearAccessibilityCacheInternal();
} break;
+
case MSG_ON_KEY_EVENT_TIMEOUT: {
PendingEvent eventState = (PendingEvent) message.obj;
setOnKeyEventResult(false, eventState.sequence);
} break;
+
+ case MSG_ON_WINDOWS_CHANGED: {
+ final int[] windowIds = (int[]) message.obj;
+ notifyWindowsChangedInternal(windowIds);
+ } break;
+
default: {
throw new IllegalArgumentException("Unknown message: " + type);
}
@@ -2700,6 +2899,113 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ final class WindowsForAccessibilityCallback implements
+ WindowManagerInternal.WindowsForAccessibilityCallback {
+
+ @Override
+ public void onWindowsForAccessibilityChanged(List<WindowInfo> windows) {
+ synchronized (mLock) {
+ List<WindowInfo> receivedWindows = (List<WindowInfo>) windows;
+
+ // Populate the windows to report.
+ List<AccessibilityWindowInfo> reportedWindows =
+ new ArrayList<AccessibilityWindowInfo>();
+ final int receivedWindowCount = receivedWindows.size();
+ for (int i = 0; i < receivedWindowCount; i++) {
+ WindowInfo receivedWindow = receivedWindows.get(i);
+ AccessibilityWindowInfo reportedWindow = populateReportedWindow(
+ receivedWindow);
+ if (reportedWindow != null) {
+ reportedWindows.add(reportedWindow);
+ }
+ }
+
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Windows changed: " + reportedWindows);
+ }
+
+ // Let the policy update the focused and active windows.
+ mSecurityPolicy.updateWindowsLocked(reportedWindows);
+ }
+ }
+
+ private AccessibilityWindowInfo populateReportedWindow(WindowInfo window) {
+ final int windowId = findWindowIdLocked(window.token);
+ if (windowId < 0) {
+ return null;
+ }
+
+ AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
+
+ reportedWindow.setId(windowId);
+ reportedWindow.setType(getTypeForWindowManagerWindowType(window.type));
+ reportedWindow.setLayer(window.layer);
+ reportedWindow.setFocused(window.focused);
+ reportedWindow.setBoundsInScreen(window.boundsInScreen);
+
+ final int parentId = findWindowIdLocked(window.parentToken);
+ if (parentId >= 0) {
+ reportedWindow.setParentId(parentId);
+ }
+
+ if (window.childTokens != null) {
+ final int childCount = window.childTokens.size();
+ for (int i = 0; i < childCount; i++) {
+ IBinder childToken = window.childTokens.get(i);
+ final int childId = findWindowIdLocked(childToken);
+ if (childId >= 0) {
+ reportedWindow.addChild(childId);
+ }
+ }
+ }
+
+ return reportedWindow;
+ }
+
+ private int getTypeForWindowManagerWindowType(int windowType) {
+ switch (windowType) {
+ case WindowManager.LayoutParams.TYPE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: {
+ return AccessibilityWindowInfo.TYPE_APPLICATION;
+ }
+
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
+ return AccessibilityWindowInfo.TYPE_INPUT_METHOD;
+ }
+
+ case WindowManager.LayoutParams.TYPE_KEYGUARD:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_STATUS_BAR:
+ case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
+ return AccessibilityWindowInfo.TYPE_SYSTEM;
+ }
+
+ default: {
+ return -1;
+ }
+ }
+ }
+ }
+
final class SecurityPolicy {
private static final int VALID_ACTIONS =
AccessibilityNodeInfo.ACTION_CLICK
@@ -2738,17 +3044,37 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
| AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
| AccessibilityEvent.TYPE_VIEW_SCROLLED
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
- | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+
+ public final List<AccessibilityWindowInfo> mWindows =
+ new ArrayList<AccessibilityWindowInfo>();
+
+ public int mActiveWindowId;
+ public int mFocusedWindowId;
+ public AccessibilityEvent mShowingFocusedWindowEvent;
- private int mActiveWindowId;
private boolean mTouchInteractionInProgress;
- private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
+ private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
final int eventType = event.getEventType();
switch (eventType) {
// All events that are for changes in a global window
// state should *always* be dispatched.
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+ if (mWindowsForAccessibilityCallback != null) {
+ // OK, this is fun. Sometimes the focused window is notified
+ // it has focus before being shown. Historically this event
+ // means that the window is focused and can be introspected.
+ // But we still have not gotten the window state from the
+ // window manager, so delay the notification until then.
+ AccessibilityWindowInfo window = findWindowById(event.getWindowId());
+ if (window == null || !window.isFocused()) {
+ mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event);
+ return false;
+ }
+ }
+ // $fall-through$
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
// All events generated by the user touching the
// screen should *always* be dispatched.
@@ -2758,15 +3084,81 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
- // These will change the active window, so dispatch.
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
return true;
}
// All events for changes in window content should be
- // dispatched *only* if this window is the active one.
- default:
- return event.getWindowId() == mActiveWindowId;
+ // dispatched *only* if this window is one of the windows
+ // the accessibility layer reports which are windows
+ // that a sighted user can touch.
+ default: {
+ return isRetrievalAllowingWindow(event.getWindowId());
+ }
+ }
+ }
+
+ public void updateWindowsLocked(List<AccessibilityWindowInfo> windows) {
+ final int oldWindowCount = mWindows.size();
+ for (int i = oldWindowCount - 1; i >= 0; i--) {
+ mWindows.remove(i).recycle();
+ }
+
+ mFocusedWindowId = -1;
+ if (!mTouchInteractionInProgress) {
+ mActiveWindowId = -1;
+ }
+
+ // If the active window goes away while the user is touch exploring we
+ // reset the active window id and wait for the next hover event from
+ // under the user's finger to determine which one is the new one. It
+ // is possible that the finger is not moving and the input system
+ // filters out such events.
+ boolean activeWindowGone = true;
+
+ final int windowCount = windows.size();
+ if (windowCount > 0) {
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = windows.get(i);
+ final int windowId = window.getId();
+ if (window.isFocused()) {
+ mFocusedWindowId = windowId;
+ if (!mTouchInteractionInProgress) {
+ mActiveWindowId = windowId;
+ window.setActive(true);
+ } else if (windowId == mActiveWindowId) {
+ activeWindowGone = false;
+ }
+ }
+ mWindows.add(window);
+ }
+
+ if (mTouchInteractionInProgress && activeWindowGone) {
+ mActiveWindowId = -1;
+ }
+
+ // Focused window may change the active one, so set the
+ // active window once we decided which it is.
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindows.get(i);
+ if (window.getId() == mActiveWindowId) {
+ window.setActive(true);
+ }
+ }
+ }
+
+ notifyWindowsChangedLocked(mWindows);
+
+ // If we are delaying a window state change event as the window
+ // source was showing when it was fired, now is the time to send.
+ if (mShowingFocusedWindowEvent != null) {
+ final int windowId = mShowingFocusedWindowEvent.getWindowId();
+ AccessibilityWindowInfo window = findWindowById(windowId);
+ if (window != null && window.isFocused()) {
+ // Sending does the recycle.
+ sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId);
+ }
+ mShowingFocusedWindowEvent = null;
}
}
@@ -2781,67 +3173,96 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// the window that the user is currently touching. If the user is
// touching a window that does not have input focus as soon as the
// the user stops touching that window the focused window becomes
- // the active one.
+ // the active one. Here we detect the touched window and make it
+ // active. In updateWindowsLocked() we update the focused window
+ // and if the user is not touching the screen, we make the focused
+ // window the active one.
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
- if (getFocusedWindowId() == windowId) {
- mActiveWindowId = windowId;
+ // If no service has the capability to introspect screen,
+ // we do not register callback in the window manager for
+ // window changes, so we have to ask the window manager
+ // what the focused window is to update the active one.
+ // The active window also determined events from which
+ // windows are delivered.
+ boolean focusedWindowActive = false;
+ synchronized (mLock) {
+ if (mWindowsForAccessibilityCallback == null) {
+ focusedWindowActive = true;
+ }
+ }
+ if (focusedWindowActive) {
+ if (windowId == getFocusedWindowId()) {
+ synchronized (mLock) {
+ mActiveWindowId = windowId;
+ }
+ }
}
} break;
+
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: {
// Do not allow delayed hover events to confuse us
// which the active window is.
- if (mTouchInteractionInProgress) {
- mActiveWindowId = windowId;
+ synchronized (mLock) {
+ if (mTouchInteractionInProgress && mActiveWindowId != windowId) {
+ setActiveWindowLocked(windowId);
+ }
}
} break;
}
}
public void onTouchInteractionStart() {
- mTouchInteractionInProgress = true;
+ synchronized (mLock) {
+ mTouchInteractionInProgress = true;
+ }
}
public void onTouchInteractionEnd() {
- mTouchInteractionInProgress = false;
- // We want to set the active window to be current immediately
- // after the user has stopped touching the screen since if the
- // user types with the IME he should get a feedback for the
- // letter typed in the text view which is in the input focused
- // window. Note that we always deliver hover accessibility events
- // (they are a result of user touching the screen) so change of
- // the active window before all hover accessibility events from
- // the touched window are delivered is fine.
- mActiveWindowId = getFocusedWindowId();
+ synchronized (mLock) {
+ mTouchInteractionInProgress = false;
+ // We want to set the active window to be current immediately
+ // after the user has stopped touching the screen since if the
+ // user types with the IME he should get a feedback for the
+ // letter typed in the text view which is in the input focused
+ // window. Note that we always deliver hover accessibility events
+ // (they are a result of user touching the screen) so change of
+ // the active window before all hover accessibility events from
+ // the touched window are delivered is fine.
+ setActiveWindowLocked(mFocusedWindowId);
+ }
}
- public int getRetrievalAllowingWindowLocked() {
- return mActiveWindowId;
+ private void setActiveWindowLocked(int windowId) {
+ if (mActiveWindowId != windowId) {
+ mActiveWindowId = windowId;
+ final int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindows.get(i);
+ window.setActive(window.getId() == windowId);
+ }
+ notifyWindowsChangedLocked(mWindows);
+ }
}
public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
- return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
+ return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
}
public boolean canPerformActionLocked(Service service, int windowId, int action,
Bundle arguments) {
- return canRetrieveWindowContent(service)
+ return canRetrieveWindowContentLocked(service)
&& isRetrievalAllowingWindow(windowId)
&& isActionPermitted(action);
}
- public boolean canRetrieveWindowContent(Service service) {
- return (service.mAccessibilityServiceInfo.getCapabilities()
- & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
+ public boolean canRetrieveWindowsLocked(Service service) {
+ return canRetrieveWindowContentLocked(service) && service.mRetrieveInteractiveWindows;
}
- public void enforceCanRetrieveWindowContent(Service service) throws RemoteException {
- // This happens due to incorrect registration so make it apparent.
- if (!canRetrieveWindowContent(service)) {
- Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " +
- "declare android:canRetrieveWindowContent.");
- throw new RemoteException();
- }
+ public boolean canRetrieveWindowContentLocked(Service service) {
+ return (service.mAccessibilityServiceInfo.getCapabilities()
+ & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
}
public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
@@ -2878,7 +3299,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private boolean isRetrievalAllowingWindow(int windowId) {
- return (mActiveWindowId == windowId);
+ if (windowId == mActiveWindowId) {
+ return true;
+ }
+ return findWindowById(windowId) != null;
+ }
+
+ private AccessibilityWindowInfo findWindowById(int windowId) {
+ final int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = mWindows.get(i);
+ if (window.getId() == windowId) {
+ return window;
+ }
+ }
+ return null;
}
private boolean isActionPermitted(int action) {
@@ -2901,35 +3336,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private int getFocusedWindowId() {
- try {
- // We call this only on window focus change or after touch
- // exploration gesture end and the shown windows are not that
- // many, so the linear look up is just fine.
- IBinder token = mWindowManagerService.getFocusedWindowToken();
- if (token != null) {
- synchronized (mLock) {
- int windowId = getFocusedWindowIdLocked(token, mGlobalWindowTokens);
- if (windowId < 0) {
- windowId = getFocusedWindowIdLocked(token,
- getCurrentUserStateLocked().mWindowTokens);
- }
- return windowId;
- }
- }
- } catch (RemoteException re) {
- /* ignore */
- }
- return -1;
- }
-
- private int getFocusedWindowIdLocked(IBinder token, SparseArray<IBinder> windows) {
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- if (windows.valueAt(i) == token) {
- return windows.keyAt(i);
- }
- }
- return -1;
+ IBinder token = mWindowManagerService.getFocusedWindowToken();
+ return findWindowIdLocked(token);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index 5f12cf4..c8b080e 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -29,8 +29,6 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
@@ -38,8 +36,6 @@ import android.util.Property;
import android.util.Slog;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.IMagnificationCallbacks;
-import android.view.IWindowManager;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
@@ -48,10 +44,12 @@ import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
import java.util.Locale;
@@ -94,8 +92,8 @@ import java.util.Locale;
*
* 6. The magnification scale will be persisted in settings and in the cloud.
*/
-public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
- implements EventStreamTransformation {
+public final class ScreenMagnifier implements WindowManagerInternal.MagnificationCallbacks,
+ EventStreamTransformation {
private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
@@ -127,7 +125,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
private final Rect mTempRect1 = new Rect();
private final Context mContext;
- private final IWindowManager mWindowManager;
+ private final WindowManagerInternal mWindowManager;
private final MagnificationController mMagnificationController;
private final ScreenStateObserver mScreenStateObserver;
@@ -191,8 +189,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) {
mContext = context;
- mWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
mAms = service;
mLongAnimationDuration = context.getResources().getInteger(
@@ -208,11 +205,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
mMagnificationController = new MagnificationController(mLongAnimationDuration);
mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
- try {
- mWindowManager.setMagnificationCallbacks(this);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManager.setMagnificationCallbacks(this);
transitionToState(STATE_DETECTING);
}
@@ -378,11 +371,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
@Override
public void onDestroy() {
mScreenStateObserver.destroy();
- try {
- mWindowManager.setMagnificationCallbacks(null);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManager.setMagnificationCallbacks(null);
}
private void handleMotionEventStateDelegating(MotionEvent event,
@@ -462,7 +451,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
}
return mTempPointerProperties;
}
-
+
private void transitionToState(int state) {
if (DEBUG_STATE_TRANSITIONS) {
switch (state) {
@@ -1120,15 +1109,10 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
if (DEBUG_SET_MAGNIFICATION_SPEC) {
Slog.i(LOG_TAG, "Sending: " + spec);
}
- try {
- mSentMagnificationSpec.scale = spec.scale;
- mSentMagnificationSpec.offsetX = spec.offsetX;
- mSentMagnificationSpec.offsetY = spec.offsetY;
- mWindowManager.setMagnificationSpec(
- MagnificationSpec.obtain(spec));
- } catch (RemoteException re) {
- /* ignore */
- }
+ mSentMagnificationSpec.scale = spec.scale;
+ mSentMagnificationSpec.offsetX = spec.offsetX;
+ mSentMagnificationSpec.offsetY = spec.offsetY;
+ mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
new file mode 100644
index 0000000..35b7f99
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -0,0 +1,1173 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Service;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.MagnificationSpec;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerInternal.MagnificationCallbacks;
+import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
+import android.view.WindowManagerPolicy;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class contains the accessibility related logic of the window manger.
+ */
+final class AccessibilityController {
+
+ private final WindowManagerService mWindowManagerService;
+
+ private static final float[] sTempFloats = new float[9];
+
+ public AccessibilityController(WindowManagerService service) {
+ mWindowManagerService = service;
+ }
+
+ private DisplayMagnifier mDisplayMagnifier;
+
+ private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
+
+ public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
+ if (callbacks != null) {
+ if (mDisplayMagnifier != null) {
+ throw new IllegalStateException("Magnification callbacks already set!");
+ }
+ mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
+ } else {
+ if (mDisplayMagnifier == null) {
+ throw new IllegalStateException("Magnification callbacks already cleared!");
+ }
+ mDisplayMagnifier.destroyLocked();
+ mDisplayMagnifier = null;
+ }
+ }
+
+ public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
+ if (callback != null) {
+ if (mWindowsForAccessibilityObserver != null) {
+ throw new IllegalStateException(
+ "Windows for accessibility callback already set!");
+ }
+ mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
+ mWindowManagerService, callback);
+ } else {
+ if (mWindowsForAccessibilityObserver == null) {
+ throw new IllegalStateException(
+ "Windows for accessibility callback already cleared!");
+ }
+ mWindowsForAccessibilityObserver = null;
+ }
+ }
+
+ public void setMagnificationSpecLocked(MagnificationSpec spec) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.setMagnificationSpecLocked(spec);
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+ }
+ // Not relevant for the window observer.
+ }
+
+ public void onWindowLayersChangedLocked() {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onWindowLayersChangedLocked();
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
+ }
+ // Not relevant for the window observer.
+ }
+
+ public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
+ }
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ public void onWindowFocusChangedLocked() {
+ // Not relevant for the display magnifier.
+
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.computeChangedWindows();
+ }
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawMagnifiedRegionBorderIfNeededLocked() {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ }
+ // Not relevant for the window observer.
+ }
+
+ public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+ if (mDisplayMagnifier != null) {
+ return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+ }
+ return null;
+ }
+
+ public boolean hasCallbacksLocked() {
+ return (mDisplayMagnifier != null
+ || mWindowsForAccessibilityObserver != null);
+ }
+
+ private static void populateTransformationMatrixLocked(WindowState windowState,
+ Matrix outMatrix) {
+ sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
+ sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
+ sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
+ sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
+ sTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
+ sTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
+ sTempFloats[Matrix.MPERSP_0] = 0;
+ sTempFloats[Matrix.MPERSP_1] = 0;
+ sTempFloats[Matrix.MPERSP_2] = 1;
+ outMatrix.setValues(sTempFloats);
+ }
+
+ /**
+ * This class encapsulates the functionality related to display magnification.
+ */
+ private static final class DisplayMagnifier {
+
+ private static final String LOG_TAG = "DisplayMagnifier";
+
+ private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
+ private static final boolean DEBUG_ROTATION = false;
+ private static final boolean DEBUG_LAYERS = false;
+ private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
+ private static final boolean DEBUG_VIEWPORT_WINDOW = false;
+
+ private final Rect mTempRect1 = new Rect();
+ private final Rect mTempRect2 = new Rect();
+
+ private final Region mTempRegion1 = new Region();
+ private final Region mTempRegion2 = new Region();
+ private final Region mTempRegion3 = new Region();
+ private final Region mTempRegion4 = new Region();
+
+ private final Context mContext;
+ private final WindowManagerService mWindowManagerService;
+ private final MagnifiedViewport mMagnifedViewport;
+ private final Handler mHandler;
+
+ private final MagnificationCallbacks mCallbacks;
+
+ private final long mLongAnimationDuration;
+
+ public DisplayMagnifier(WindowManagerService windowManagerService,
+ MagnificationCallbacks callbacks) {
+ mContext = windowManagerService.mContext;
+ mWindowManagerService = windowManagerService;
+ mCallbacks = callbacks;
+ mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+ mMagnifedViewport = new MagnifiedViewport();
+ mLongAnimationDuration = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ }
+
+ public void setMagnificationSpecLocked(MagnificationSpec spec) {
+ mMagnifedViewport.updateMagnificationSpecLocked(spec);
+ mMagnifedViewport.recomputeBoundsLocked();
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+
+ public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+ if (DEBUG_RECTANGLE_REQUESTED) {
+ Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
+ }
+ if (!mMagnifedViewport.isMagnifyingLocked()) {
+ return;
+ }
+ Rect magnifiedRegionBounds = mTempRect2;
+ mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+ if (magnifiedRegionBounds.contains(rectangle)) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = rectangle.left;
+ args.argi2 = rectangle.top;
+ args.argi3 = rectangle.right;
+ args.argi4 = rectangle.bottom;
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
+ args).sendToTarget();
+ }
+
+ public void onWindowLayersChangedLocked() {
+ if (DEBUG_LAYERS) {
+ Slog.i(LOG_TAG, "Layers changed.");
+ }
+ mMagnifedViewport.recomputeBoundsLocked();
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+
+ public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+ if (DEBUG_ROTATION) {
+ Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
+ + " displayId: " + displayContent.getDisplayId());
+ }
+ mMagnifedViewport.onRotationChangedLocked();
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+ }
+
+ public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
+ if (DEBUG_WINDOW_TRANSITIONS) {
+ Slog.i(LOG_TAG, "Window transition: "
+ + AppTransition.appTransitionToString(transition)
+ + " displayId: " + windowState.getDisplayId());
+ }
+ final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ if (magnifying) {
+ switch (transition) {
+ case AppTransition.TRANSIT_ACTIVITY_OPEN:
+ case AppTransition.TRANSIT_TASK_OPEN:
+ case AppTransition.TRANSIT_TASK_TO_FRONT:
+ case AppTransition.TRANSIT_WALLPAPER_OPEN:
+ case AppTransition.TRANSIT_WALLPAPER_CLOSE:
+ case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
+ }
+ }
+ }
+ }
+
+ public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ if (DEBUG_WINDOW_TRANSITIONS) {
+ Slog.i(LOG_TAG, "Window transition: "
+ + AppTransition.appTransitionToString(transition)
+ + " displayId: " + windowState.getDisplayId());
+ }
+ final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ final int type = windowState.mAttrs.type;
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ if (!magnifying) {
+ break;
+ }
+ switch (type) {
+ case WindowManager.LayoutParams.TYPE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
+ Rect magnifiedRegionBounds = mTempRect2;
+ mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+ magnifiedRegionBounds);
+ Rect touchableRegionBounds = mTempRect1;
+ windowState.getTouchableRegion(mTempRegion1);
+ mTempRegion1.getBounds(touchableRegionBounds);
+ if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
+ mCallbacks.onRectangleOnScreenRequested(
+ touchableRegionBounds.left,
+ touchableRegionBounds.top,
+ touchableRegionBounds.right,
+ touchableRegionBounds.bottom);
+ }
+ } break;
+ } break;
+ }
+ }
+ }
+
+ public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+ MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+ if (spec != null && !spec.isNop()) {
+ WindowManagerPolicy policy = mWindowManagerService.mPolicy;
+ final int windowType = windowState.mAttrs.type;
+ if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
+ && !policy.canMagnifyWindow(windowType)) {
+ return null;
+ }
+ if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
+ return null;
+ }
+ }
+ return spec;
+ }
+
+ public void destroyLocked() {
+ mMagnifedViewport.destroyWindow();
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawMagnifiedRegionBorderIfNeededLocked() {
+ mMagnifedViewport.drawWindowIfNeededLocked();
+ }
+
+ private final class MagnifiedViewport {
+
+ private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
+
+ private final SparseArray<WindowState> mTempWindowStates =
+ new SparseArray<WindowState>();
+
+ private final RectF mTempRectF = new RectF();
+
+ private final Point mTempPoint = new Point();
+
+ private final Matrix mTempMatrix = new Matrix();
+
+ private final Region mMagnifiedBounds = new Region();
+ private final Region mOldMagnifiedBounds = new Region();
+
+ private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
+
+ private final WindowManager mWindowManager;
+
+ private final int mBorderWidth;
+ private final int mHalfBorderWidth;
+
+ private final ViewportWindow mWindow;
+
+ private boolean mFullRedrawNeeded;
+
+ public MagnifiedViewport() {
+ mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
+ mBorderWidth = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
+ mContext.getResources().getDisplayMetrics());
+ mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
+ mWindow = new ViewportWindow(mContext);
+ recomputeBoundsLocked();
+ }
+
+ public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+ if (spec != null) {
+ mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
+ } else {
+ mMagnificationSpec.clear();
+ }
+ // If this message is pending we are in a rotation animation and do not want
+ // to show the border. We will do so when the pending message is handled.
+ if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
+ setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
+ }
+ }
+
+ public void recomputeBoundsLocked() {
+ mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ final int screenWidth = mTempPoint.x;
+ final int screenHeight = mTempPoint.y;
+
+ Region magnifiedBounds = mMagnifiedBounds;
+ magnifiedBounds.set(0, 0, 0, 0);
+
+ Region availableBounds = mTempRegion1;
+ availableBounds.set(0, 0, screenWidth, screenHeight);
+
+ Region nonMagnifiedBounds = mTempRegion4;
+ nonMagnifiedBounds.set(0, 0, 0, 0);
+
+ SparseArray<WindowState> visibleWindows = mTempWindowStates;
+ visibleWindows.clear();
+ populateWindowsOnScreenLocked(visibleWindows);
+
+ final int visibleWindowCount = visibleWindows.size();
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ WindowState windowState = visibleWindows.valueAt(i);
+ if (windowState.mAttrs.type == WindowManager
+ .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+ continue;
+ }
+
+ Region windowBounds = mTempRegion2;
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrixLocked(windowState, matrix);
+ RectF windowFrame = mTempRectF;
+
+ if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ windowFrame.set(windowState.mFrame);
+ windowFrame.offset(-windowFrame.left, -windowFrame.top);
+ matrix.mapRect(windowFrame);
+ windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ magnifiedBounds.op(windowBounds, Region.Op.UNION);
+ magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
+ } else {
+ Region touchableRegion = mTempRegion3;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect1;
+ touchableRegion.getBounds(touchableFrame);
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ matrix.mapRect(windowFrame);
+ windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+ windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
+ availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+ }
+
+ Region accountedBounds = mTempRegion2;
+ accountedBounds.set(magnifiedBounds);
+ accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
+ accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
+
+ if (accountedBounds.isRect()) {
+ Rect accountedFrame = mTempRect1;
+ accountedBounds.getBounds(accountedFrame);
+ if (accountedFrame.width() == screenWidth
+ && accountedFrame.height() == screenHeight) {
+ break;
+ }
+ }
+ }
+
+ visibleWindows.clear();
+
+ magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
+ screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
+ Region.Op.INTERSECT);
+
+ if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
+ Region bounds = Region.obtain();
+ bounds.set(magnifiedBounds);
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
+ bounds).sendToTarget();
+
+ mWindow.setBounds(magnifiedBounds);
+ Rect dirtyRect = mTempRect1;
+ if (mFullRedrawNeeded) {
+ mFullRedrawNeeded = false;
+ dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
+ screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
+ mWindow.invalidate(dirtyRect);
+ } else {
+ Region dirtyRegion = mTempRegion3;
+ dirtyRegion.set(magnifiedBounds);
+ dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
+ dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+ dirtyRegion.getBounds(dirtyRect);
+ mWindow.invalidate(dirtyRect);
+ }
+
+ mOldMagnifiedBounds.set(magnifiedBounds);
+ }
+ }
+
+ public void onRotationChangedLocked() {
+ // If we are magnifying, hide the magnified border window immediately so
+ // the user does not see strange artifacts during rotation. The screenshot
+ // used for rotation has already the border. After the rotation is complete
+ // we will show the border.
+ if (isMagnifyingLocked()) {
+ setMagnifiedRegionBorderShownLocked(false, false);
+ final long delay = (long) (mLongAnimationDuration
+ * mWindowManagerService.mWindowAnimationScale);
+ Message message = mHandler.obtainMessage(
+ MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
+ mHandler.sendMessageDelayed(message, delay);
+ }
+ recomputeBoundsLocked();
+ mWindow.updateSize();
+ }
+
+ public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+ if (shown) {
+ mFullRedrawNeeded = true;
+ mOldMagnifiedBounds.set(0, 0, 0, 0);
+ }
+ mWindow.setShown(shown, animate);
+ }
+
+ public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+ MagnificationSpec spec = mMagnificationSpec;
+ mMagnifiedBounds.getBounds(rect);
+ rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ rect.scale(1.0f / spec.scale);
+ }
+
+ public boolean isMagnifyingLocked() {
+ return mMagnificationSpec.scale > 1.0f;
+ }
+
+ public MagnificationSpec getMagnificationSpecLocked() {
+ return mMagnificationSpec;
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawWindowIfNeededLocked() {
+ recomputeBoundsLocked();
+ mWindow.drawIfNeeded();
+ }
+
+ public void destroyWindow() {
+ mWindow.releaseSurface();
+ }
+
+ private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ DisplayContent displayContent = mWindowManagerService
+ .getDefaultDisplayContentLocked();
+ WindowList windowList = displayContent.getWindowList();
+ final int windowCount = windowList.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = windowList.get(i);
+ if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
+ .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
+ && !windowState.mWinAnimator.mEnterAnimationPending) {
+ outWindows.put(windowState.mLayer, windowState);
+ }
+ }
+ }
+
+ private final class ViewportWindow {
+ private static final String SURFACE_TITLE = "Magnification Overlay";
+
+ private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+ private static final int MIN_ALPHA = 0;
+ private static final int MAX_ALPHA = 255;
+
+ private final Region mBounds = new Region();
+ private final Rect mDirtyRect = new Rect();
+ private final Paint mPaint = new Paint();
+
+ private final ValueAnimator mShowHideFrameAnimator;
+ private final SurfaceControl mSurfaceControl;
+ private final Surface mSurface = new Surface();
+
+ private boolean mShown;
+ private int mAlpha;
+
+ private boolean mInvalidated;
+
+ public ViewportWindow(Context context) {
+ SurfaceControl surfaceControl = null;
+ try {
+ mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
+ SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
+ SurfaceControl.HIDDEN);
+ } catch (OutOfResourcesException oore) {
+ /* ignore */
+ }
+ mSurfaceControl = surfaceControl;
+ mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
+ .getLayerStack());
+ mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER);
+ mSurfaceControl.setPosition(0, 0);
+ mSurface.copyFrom(mSurfaceControl);
+
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
+ typedValue, true);
+ final int borderColor = context.getResources().getColor(typedValue.resourceId);
+
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(mBorderWidth);
+ mPaint.setColor(borderColor);
+
+ Interpolator interpolator = new DecelerateInterpolator(2.5f);
+ final long longAnimationDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+
+ mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
+ MIN_ALPHA, MAX_ALPHA);
+ mShowHideFrameAnimator.setInterpolator(interpolator);
+ mShowHideFrameAnimator.setDuration(longAnimationDuration);
+ mInvalidated = true;
+ }
+
+ public void setShown(boolean shown, boolean animate) {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mShown == shown) {
+ return;
+ }
+ mShown = shown;
+ if (animate) {
+ if (mShowHideFrameAnimator.isRunning()) {
+ mShowHideFrameAnimator.reverse();
+ } else {
+ if (shown) {
+ mShowHideFrameAnimator.start();
+ } else {
+ mShowHideFrameAnimator.reverse();
+ }
+ }
+ } else {
+ mShowHideFrameAnimator.cancel();
+ if (shown) {
+ setAlpha(MAX_ALPHA);
+ } else {
+ setAlpha(MIN_ALPHA);
+ }
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
+ }
+ }
+ }
+
+ @SuppressWarnings("unused")
+ // Called reflectively from an animator.
+ public int getAlpha() {
+ synchronized (mWindowManagerService.mWindowMap) {
+ return mAlpha;
+ }
+ }
+
+ public void setAlpha(int alpha) {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mAlpha == alpha) {
+ return;
+ }
+ mAlpha = alpha;
+ invalidate(null);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
+ }
+ }
+ }
+
+ public void setBounds(Region bounds) {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mBounds.equals(bounds)) {
+ return;
+ }
+ mBounds.set(bounds);
+ invalidate(mDirtyRect);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
+ }
+ }
+ }
+
+ public void updateSize() {
+ synchronized (mWindowManagerService.mWindowMap) {
+ mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+ invalidate(mDirtyRect);
+ }
+ }
+
+ public void invalidate(Rect dirtyRect) {
+ if (dirtyRect != null) {
+ mDirtyRect.set(dirtyRect);
+ } else {
+ mDirtyRect.setEmpty();
+ }
+ mInvalidated = true;
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+
+ /** NOTE: This has to be called within a surface transaction. */
+ public void drawIfNeeded() {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (!mInvalidated) {
+ return;
+ }
+ mInvalidated = false;
+ Canvas canvas = null;
+ try {
+ // Empty dirty rectangle means unspecified.
+ if (mDirtyRect.isEmpty()) {
+ mBounds.getBounds(mDirtyRect);
+ }
+ mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
+ canvas = mSurface.lockCanvas(mDirtyRect);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
+ }
+ } catch (IllegalArgumentException iae) {
+ /* ignore */
+ } catch (Surface.OutOfResourcesException oore) {
+ /* ignore */
+ }
+ if (canvas == null) {
+ return;
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "Bounds: " + mBounds);
+ }
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mPaint.setAlpha(mAlpha);
+ Path path = mBounds.getBoundaryPath();
+ canvas.drawPath(path, mPaint);
+
+ mSurface.unlockCanvasAndPost(canvas);
+
+ if (mAlpha > 0) {
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+ }
+ }
+
+ public void releaseSurface() {
+ mSurfaceControl.release();
+ mSurface.release();
+ }
+ }
+ }
+
+ private class MyHandler extends Handler {
+ public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
+ public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
+ public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
+ public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+ public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+
+ public MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
+ Region bounds = (Region) message.obj;
+ mCallbacks.onMagnifedBoundsChanged(bounds);
+ bounds.recycle();
+ } break;
+
+ case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ final int left = args.argi1;
+ final int top = args.argi2;
+ final int right = args.argi3;
+ final int bottom = args.argi4;
+ mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
+ args.recycle();
+ } break;
+
+ case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
+ mCallbacks.onUserContextChanged();
+ } break;
+
+ case MESSAGE_NOTIFY_ROTATION_CHANGED: {
+ final int rotation = message.arg1;
+ mCallbacks.onRotationChanged(rotation);
+ } break;
+
+ case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
+ synchronized (mWindowManagerService.mWindowMap) {
+ if (mMagnifedViewport.isMagnifyingLocked()) {
+ mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+ mWindowManagerService.scheduleAnimationLocked();
+ }
+ }
+ } break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This class encapsulates the functionality related to computing the windows
+ * reported for accessibility purposes. These windows are all windows a sighted
+ * user can see on the screen.
+ */
+ private static final class WindowsForAccessibilityObserver {
+ private static final String LOG_TAG = "WindowsForAccessibilityObserver";
+
+ private static final boolean DEBUG = false;
+
+ private final SparseArray<WindowState> mTempWindowStates =
+ new SparseArray<WindowState>();
+
+ private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
+
+ private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
+
+ private final RectF mTempRectF = new RectF();
+
+ private final Matrix mTempMatrix = new Matrix();
+
+ private final Point mTempPoint = new Point();
+
+ private final Rect mTempRect = new Rect();
+
+ private final Region mTempRegion = new Region();
+
+ private final Region mTempRegion1 = new Region();
+
+ private final Context mContext;
+
+ private final WindowManagerService mWindowManagerService;
+
+ private final Handler mHandler;
+
+ private final WindowsForAccessibilityCallback mCallback;
+
+ public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
+ WindowsForAccessibilityCallback callback) {
+ mContext = windowManagerService.mContext;
+ mWindowManagerService = windowManagerService;
+ mCallback = callback;
+ mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+ computeChangedWindows();
+ }
+
+ public void computeChangedWindows() {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "computeChangedWindows()");
+ }
+
+ synchronized (mWindowManagerService.mWindowMap) {
+ WindowManager windowManager = (WindowManager)
+ mContext.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ final int screenWidth = mTempPoint.x;
+ final int screenHeight = mTempPoint.y;
+
+ Region unaccountedSpace = mTempRegion;
+ unaccountedSpace.set(0, 0, screenWidth, screenHeight);
+
+ SparseArray<WindowState> visibleWindows = mTempWindowStates;
+ populateVisibleWindowsOnScreenLocked(visibleWindows);
+
+ List<WindowInfo> windows = new ArrayList<WindowInfo>();
+
+ Set<IBinder> addedWindows = mTempBinderSet;
+ addedWindows.clear();
+
+ final int visibleWindowCount = visibleWindows.size();
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ WindowState windowState = visibleWindows.valueAt(i);
+ // Compute the window touchable frame as shown on the screen.
+
+ // Get the touchable frame.
+ Region touchableRegion = mTempRegion1;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect;
+ touchableRegion.getBounds(touchableFrame);
+
+ // Move to origin as all transforms are captured by the matrix.
+ RectF windowFrame = mTempRectF;
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+
+ // Map the frame to get what appears on the screen.
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrixLocked(windowState, matrix);
+ matrix.mapRect(windowFrame);
+
+ // Got the bounds.
+ Rect boundsInScreen = mTempRect;
+ boundsInScreen.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+
+ final int flags = windowState.mAttrs.flags;
+
+ // If the window is not touchable, do not report it but take into account
+ // the space it takes since the content behind it cannot be touched.
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 1) {
+ unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+ Region.Op.DIFFERENCE);
+ continue;
+ }
+
+ // If the window is completely covered by other windows - ignore.
+ if (unaccountedSpace.quickReject(boundsInScreen)) {
+ continue;
+ }
+
+ // Add windows of certain types not covered by modal windows.
+ if (isReportedWindowType(windowState.mAttrs.type)) {
+ // Add the window to the ones to be reported.
+ WindowInfo window = WindowInfo.obtain();
+ window.type = windowState.mAttrs.type;
+ window.layer = windowState.mLayer;
+ window.token = windowState.mClient.asBinder();
+
+ addedWindows.add(window.token);
+
+ WindowState attachedWindow = windowState.mAttachedWindow;
+ if (attachedWindow != null) {
+ window.parentToken = attachedWindow.mClient.asBinder();
+ }
+
+ window.focused = windowState.isFocused();
+ window.boundsInScreen.set(boundsInScreen);
+
+ final int childCount = windowState.mChildWindows.size();
+ if (childCount > 0) {
+ if (window.childTokens == null) {
+ window.childTokens = new ArrayList<IBinder>();
+ }
+ for (int j = 0; j < childCount; j++) {
+ WindowState child = windowState.mChildWindows.get(j);
+ window.childTokens.add(child.mClient.asBinder());
+ }
+ }
+
+ windows.add(window);
+ }
+
+ // Account for the space this window takes.
+ unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+
+ // We figured out what is touchable for the entire screen - done.
+ if (unaccountedSpace.isEmpty()) {
+ break;
+ }
+
+ // If a window is modal, no other below can be touched - done.
+ if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ break;
+ }
+ }
+
+ // Remove child/parent references to windows that were not added.
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo window = windows.get(i);
+ if (!addedWindows.contains(window.parentToken)) {
+ window.parentToken = null;
+ }
+ if (window.childTokens != null) {
+ final int childTokenCount = window.childTokens.size();
+ for (int j = childTokenCount - 1; j >= 0; j--) {
+ if (!addedWindows.contains(window.childTokens.get(j))) {
+ window.childTokens.remove(j);
+ }
+ }
+ // Leave the child token list if empty.
+ }
+ }
+
+ visibleWindows.clear();
+ addedWindows.clear();
+
+ // We computed the windows and if they changed notify the client.
+ boolean windowsChanged = false;
+ if (mOldWindows.size() != windows.size()) {
+ // Different size means something changed.
+ windowsChanged = true;
+ } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
+ // Since we always traverse windows from high to low layer
+ // the old and new windows at the same index should be the
+ // same, otherwise something changed.
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo oldWindow = mOldWindows.get(i);
+ WindowInfo newWindow = windows.get(i);
+ // We do not care for layer changes given the window
+ // order does not change. This brings no new information
+ // to the clients.
+ if (windowChangedNoLayer(oldWindow, newWindow)) {
+ windowsChanged = true;
+ break;
+ }
+ }
+ }
+
+ if (windowsChanged) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Windows changed:" + windows);
+ }
+ // Remember the old windows to detect changes.
+ cacheWindows(windows);
+ // Announce the change.
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
+ windows).sendToTarget();
+ } else {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "No windows changed.");
+ }
+ // Recycle the nodes as we do not need them.
+ clearAndRecycleWindows(windows);
+ }
+ }
+ }
+
+ private void cacheWindows(List<WindowInfo> windows) {
+ final int oldWindowCount = mOldWindows.size();
+ for (int i = oldWindowCount - 1; i >= 0; i--) {
+ mOldWindows.remove(i).recycle();
+ }
+ final int newWindowCount = windows.size();
+ for (int i = 0; i < newWindowCount; i++) {
+ WindowInfo newWindow = windows.get(i);
+ mOldWindows.add(WindowInfo.obtain(newWindow));
+ }
+ }
+
+ private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
+ if (oldWindow == newWindow) {
+ return false;
+ }
+ if (oldWindow == null && newWindow != null) {
+ return true;
+ }
+ if (oldWindow != null && newWindow == null) {
+ return true;
+ }
+ if (oldWindow.type != newWindow.type) {
+ return true;
+ }
+ if (oldWindow.focused != newWindow.focused) {
+ return true;
+ }
+ if (oldWindow.token == null) {
+ if (newWindow.token != null) {
+ return true;
+ }
+ } else if (!oldWindow.token.equals(newWindow.token)) {
+ return true;
+ }
+ if (oldWindow.parentToken == null) {
+ if (newWindow.parentToken != null) {
+ return true;
+ }
+ } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
+ return true;
+ }
+ if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
+ return true;
+ }
+ if (oldWindow.childTokens != null && newWindow.childTokens != null
+ && !oldWindow.childTokens.equals(newWindow.childTokens)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void clearAndRecycleWindows(List<WindowInfo> windows) {
+ final int windowCount = windows.size();
+ for (int i = windowCount - 1; i >= 0; i--) {
+ windows.remove(i).recycle();
+ }
+ }
+
+ private static boolean isReportedWindowType(int windowType) {
+ return (windowType != WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
+ && windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
+ && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
+ && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_DRAG
+ && windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
+ && windowType != WindowManager.LayoutParams.TYPE_POINTER
+ && windowType != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
+ && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+ }
+
+ private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ DisplayContent displayContent = mWindowManagerService
+ .getDefaultDisplayContentLocked();
+ WindowList windowList = displayContent.getWindowList();
+ final int windowCount = windowList.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = windowList.get(i);
+ if (windowState.isVisibleLw()) {
+ outWindows.put(windowState.mLayer, windowState);
+ }
+ }
+ }
+
+ private class MyHandler extends Handler {
+ public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
+ List<WindowInfo> windows = (List<WindowInfo>) message.obj;
+ mCallback.onWindowsForAccessibilityChanged(windows);
+ clearAndRecycleWindows(windows);
+ } break;
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayMagnifier.java b/services/core/java/com/android/server/wm/DisplayMagnifier.java
deleted file mode 100644
index 382d7b4..0000000
--- a/services/core/java/com/android/server/wm/DisplayMagnifier.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.app.Service;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Pools.SimplePool;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.IMagnificationCallbacks;
-import android.view.MagnificationSpec;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import com.android.internal.R;
-import com.android.internal.os.SomeArgs;
-
-/**
- * This class is a part of the window manager and encapsulates the
- * functionality related to display magnification.
- */
-final class DisplayMagnifier {
- private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName();
-
- private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
- private static final boolean DEBUG_ROTATION = false;
- private static final boolean DEBUG_LAYERS = false;
- private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
- private static final boolean DEBUG_VIEWPORT_WINDOW = false;
-
- private final Rect mTempRect1 = new Rect();
- private final Rect mTempRect2 = new Rect();
-
- private final Region mTempRegion1 = new Region();
- private final Region mTempRegion2 = new Region();
- private final Region mTempRegion3 = new Region();
- private final Region mTempRegion4 = new Region();
-
- private final Context mContext;
- private final WindowManagerService mWindowManagerService;
- private final MagnifiedViewport mMagnifedViewport;
- private final Handler mHandler;
-
- private final IMagnificationCallbacks mCallbacks;
-
- private final long mLongAnimationDuration;
-
- public DisplayMagnifier(WindowManagerService windowManagerService,
- IMagnificationCallbacks callbacks) {
- mContext = windowManagerService.mContext;
- mWindowManagerService = windowManagerService;
- mCallbacks = callbacks;
- mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
- mMagnifedViewport = new MagnifiedViewport();
- mLongAnimationDuration = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
- }
-
- public void setMagnificationSpecLocked(MagnificationSpec spec) {
- mMagnifedViewport.updateMagnificationSpecLocked(spec);
- mMagnifedViewport.recomputeBoundsLocked();
- mWindowManagerService.scheduleAnimationLocked();
- }
-
- public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
- if (DEBUG_RECTANGLE_REQUESTED) {
- Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
- }
- if (!mMagnifedViewport.isMagnifyingLocked()) {
- return;
- }
- Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
- if (magnifiedRegionBounds.contains(rectangle)) {
- return;
- }
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = rectangle.left;
- args.argi2 = rectangle.top;
- args.argi3 = rectangle.right;
- args.argi4 = rectangle.bottom;
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
- args).sendToTarget();
- }
-
- public void onWindowLayersChangedLocked() {
- if (DEBUG_LAYERS) {
- Slog.i(LOG_TAG, "Layers changed.");
- }
- mMagnifedViewport.recomputeBoundsLocked();
- mWindowManagerService.scheduleAnimationLocked();
- }
-
- public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
- if (DEBUG_ROTATION) {
- Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
- + " displayId: " + displayContent.getDisplayId());
- }
- mMagnifedViewport.onRotationChangedLocked();
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
- }
-
- public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
- if (DEBUG_WINDOW_TRANSITIONS) {
- Slog.i(LOG_TAG, "Window transition: "
- + AppTransition.appTransitionToString(transition)
- + " displayId: " + windowState.getDisplayId());
- }
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
- if (magnifying) {
- switch (transition) {
- case AppTransition.TRANSIT_ACTIVITY_OPEN:
- case AppTransition.TRANSIT_TASK_OPEN:
- case AppTransition.TRANSIT_TASK_TO_FRONT:
- case AppTransition.TRANSIT_WALLPAPER_OPEN:
- case AppTransition.TRANSIT_WALLPAPER_CLOSE:
- case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
- }
- }
- }
- }
-
- public void onWindowTransitionLocked(WindowState windowState, int transition) {
- if (DEBUG_WINDOW_TRANSITIONS) {
- Slog.i(LOG_TAG, "Window transition: "
- + AppTransition.appTransitionToString(transition)
- + " displayId: " + windowState.getDisplayId());
- }
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
- final int type = windowState.mAttrs.type;
- switch (transition) {
- case WindowManagerPolicy.TRANSIT_ENTER:
- case WindowManagerPolicy.TRANSIT_SHOW: {
- if (!magnifying) {
- break;
- }
- switch (type) {
- case WindowManager.LayoutParams.TYPE_APPLICATION:
- case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
- case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
- case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
- case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
- case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
- case WindowManager.LayoutParams.TYPE_PHONE:
- case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
- case WindowManager.LayoutParams.TYPE_TOAST:
- case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
- case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
- case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
- case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
- case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
- case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
- case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
- case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
- Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
- magnifiedRegionBounds);
- Rect touchableRegionBounds = mTempRect1;
- windowState.getTouchableRegion(mTempRegion1);
- mTempRegion1.getBounds(touchableRegionBounds);
- if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
- try {
- mCallbacks.onRectangleOnScreenRequested(
- touchableRegionBounds.left,
- touchableRegionBounds.top,
- touchableRegionBounds.right,
- touchableRegionBounds.bottom);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- } break;
- } break;
- }
- }
- }
-
- public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
- MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
- if (spec != null && !spec.isNop()) {
- WindowManagerPolicy policy = mWindowManagerService.mPolicy;
- final int windowType = windowState.mAttrs.type;
- if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
- && !policy.canMagnifyWindow(windowType)) {
- return null;
- }
- if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
- return null;
- }
- }
- return spec;
- }
-
- public void destroyLocked() {
- mMagnifedViewport.destroyWindow();
- }
-
- /** NOTE: This has to be called within a surface transaction. */
- public void drawMagnifiedRegionBorderIfNeededLocked() {
- mMagnifedViewport.drawWindowIfNeededLocked();
- }
-
- private final class MagnifiedViewport {
-
- private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
-
- private final SparseArray<WindowStateInfo> mTempWindowStateInfos =
- new SparseArray<WindowStateInfo>();
-
- private final float[] mTempFloats = new float[9];
-
- private final RectF mTempRectF = new RectF();
-
- private final Point mTempPoint = new Point();
-
- private final Matrix mTempMatrix = new Matrix();
-
- private final Region mMagnifiedBounds = new Region();
- private final Region mOldMagnifiedBounds = new Region();
-
- private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
-
- private final WindowManager mWindowManager;
-
- private final int mBorderWidth;
- private final int mHalfBorderWidth;
-
- private final ViewportWindow mWindow;
-
- private boolean mFullRedrawNeeded;
-
- public MagnifiedViewport() {
- mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
- mBorderWidth = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
- mContext.getResources().getDisplayMetrics());
- mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
- mWindow = new ViewportWindow(mContext);
- recomputeBoundsLocked();
- }
-
- public void updateMagnificationSpecLocked(MagnificationSpec spec) {
- if (spec != null) {
- mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
- } else {
- mMagnificationSpec.clear();
- }
- // If this message is pending we are in a rotation animation and do not want
- // to show the border. We will do so when the pending message is handled.
- if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
- }
- }
-
- public void recomputeBoundsLocked() {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- final int screenWidth = mTempPoint.x;
- final int screenHeight = mTempPoint.y;
-
- Region magnifiedBounds = mMagnifiedBounds;
- magnifiedBounds.set(0, 0, 0, 0);
-
- Region availableBounds = mTempRegion1;
- availableBounds.set(0, 0, screenWidth, screenHeight);
-
- Region nonMagnifiedBounds = mTempRegion4;
- nonMagnifiedBounds.set(0, 0, 0, 0);
-
- SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos;
- visibleWindows.clear();
- getWindowsOnScreenLocked(visibleWindows);
-
- final int visibleWindowCount = visibleWindows.size();
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
- WindowStateInfo info = visibleWindows.valueAt(i);
- if (info.mWindowState.mAttrs.type == WindowManager
- .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
- continue;
- }
-
- Region windowBounds = mTempRegion2;
- Matrix matrix = mTempMatrix;
- populateTransformationMatrix(info.mWindowState, matrix);
- RectF windowFrame = mTempRectF;
-
- if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) {
- windowFrame.set(info.mWindowState.mFrame);
- windowFrame.offset(-windowFrame.left, -windowFrame.top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- magnifiedBounds.op(windowBounds, Region.Op.UNION);
- magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
- } else {
- windowFrame.set(info.mTouchableRegion);
- windowFrame.offset(-info.mWindowState.mFrame.left,
- -info.mWindowState.mFrame.top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
- windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
- availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
- }
-
- Region accountedBounds = mTempRegion2;
- accountedBounds.set(magnifiedBounds);
- accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
- accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
-
- if (accountedBounds.isRect()) {
- Rect accountedFrame = mTempRect1;
- accountedBounds.getBounds(accountedFrame);
- if (accountedFrame.width() == screenWidth
- && accountedFrame.height() == screenHeight) {
- break;
- }
- }
- }
-
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
- WindowStateInfo info = visibleWindows.valueAt(i);
- info.recycle();
- visibleWindows.removeAt(i);
- }
-
- magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
- screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
- Region.Op.INTERSECT);
-
- if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
- Region bounds = Region.obtain();
- bounds.set(magnifiedBounds);
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
- bounds).sendToTarget();
-
- mWindow.setBounds(magnifiedBounds);
- Rect dirtyRect = mTempRect1;
- if (mFullRedrawNeeded) {
- mFullRedrawNeeded = false;
- dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
- screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
- mWindow.invalidate(dirtyRect);
- } else {
- Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(magnifiedBounds);
- dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
- dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
- dirtyRegion.getBounds(dirtyRect);
- mWindow.invalidate(dirtyRect);
- }
-
- mOldMagnifiedBounds.set(magnifiedBounds);
- }
- }
-
- private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) {
- mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
- mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
- mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
- mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
- mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
- mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
- mTempFloats[Matrix.MPERSP_0] = 0;
- mTempFloats[Matrix.MPERSP_1] = 0;
- mTempFloats[Matrix.MPERSP_2] = 1;
- outMatrix.setValues(mTempFloats);
- }
-
- private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) {
- DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked();
- WindowList windowList = displayContent.getWindowList();
- final int windowCount = windowList.size();
- for (int i = 0; i < windowCount; i++) {
- WindowState windowState = windowList.get(i);
- if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
- .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
- && !windowState.mWinAnimator.mEnterAnimationPending) {
- outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState));
- }
- }
- }
-
- public void onRotationChangedLocked() {
- // If we are magnifying, hide the magnified border window immediately so
- // the user does not see strange artifacts during rotation. The screenshot
- // used for rotation has already the border. After the rotation is complete
- // we will show the border.
- if (isMagnifyingLocked()) {
- setMagnifiedRegionBorderShownLocked(false, false);
- final long delay = (long) (mLongAnimationDuration
- * mWindowManagerService.mWindowAnimationScale);
- Message message = mHandler.obtainMessage(
- MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
- mHandler.sendMessageDelayed(message, delay);
- }
- recomputeBoundsLocked();
- mWindow.updateSize();
- }
-
- public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
- if (shown) {
- mFullRedrawNeeded = true;
- mOldMagnifiedBounds.set(0, 0, 0, 0);
- }
- mWindow.setShown(shown, animate);
- }
-
- public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
- MagnificationSpec spec = mMagnificationSpec;
- mMagnifiedBounds.getBounds(rect);
- rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
- rect.scale(1.0f / spec.scale);
- }
-
- public boolean isMagnifyingLocked() {
- return mMagnificationSpec.scale > 1.0f;
- }
-
- public MagnificationSpec getMagnificationSpecLocked() {
- return mMagnificationSpec;
- }
-
- /** NOTE: This has to be called within a surface transaction. */
- public void drawWindowIfNeededLocked() {
- recomputeBoundsLocked();
- mWindow.drawIfNeeded();
- }
-
- public void destroyWindow() {
- mWindow.releaseSurface();
- }
-
- private final class ViewportWindow {
- private static final String SURFACE_TITLE = "Magnification Overlay";
-
- private static final String PROPERTY_NAME_ALPHA = "alpha";
-
- private static final int MIN_ALPHA = 0;
- private static final int MAX_ALPHA = 255;
-
- private final Region mBounds = new Region();
- private final Rect mDirtyRect = new Rect();
- private final Paint mPaint = new Paint();
-
- private final ValueAnimator mShowHideFrameAnimator;
- private final SurfaceControl mSurfaceControl;
- private final Surface mSurface = new Surface();
-
- private boolean mShown;
- private int mAlpha;
-
- private boolean mInvalidated;
-
- public ViewportWindow(Context context) {
- SurfaceControl surfaceControl = null;
- try {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
- mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
- } catch (OutOfResourcesException oore) {
- /* ignore */
- }
- mSurfaceControl = surfaceControl;
- mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
- mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
- WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
- * WindowManagerService.TYPE_LAYER_MULTIPLIER);
- mSurfaceControl.setPosition(0, 0);
- mSurface.copyFrom(mSurfaceControl);
-
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
- typedValue, true);
- final int borderColor = context.getResources().getColor(typedValue.resourceId);
-
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(mBorderWidth);
- mPaint.setColor(borderColor);
-
- Interpolator interpolator = new DecelerateInterpolator(2.5f);
- final long longAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
-
- mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
- MIN_ALPHA, MAX_ALPHA);
- mShowHideFrameAnimator.setInterpolator(interpolator);
- mShowHideFrameAnimator.setDuration(longAnimationDuration);
- mInvalidated = true;
- }
-
- public void setShown(boolean shown, boolean animate) {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mShown == shown) {
- return;
- }
- mShown = shown;
- if (animate) {
- if (mShowHideFrameAnimator.isRunning()) {
- mShowHideFrameAnimator.reverse();
- } else {
- if (shown) {
- mShowHideFrameAnimator.start();
- } else {
- mShowHideFrameAnimator.reverse();
- }
- }
- } else {
- mShowHideFrameAnimator.cancel();
- if (shown) {
- setAlpha(MAX_ALPHA);
- } else {
- setAlpha(MIN_ALPHA);
- }
- }
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
- }
- }
- }
-
- @SuppressWarnings("unused")
- // Called reflectively from an animator.
- public int getAlpha() {
- synchronized (mWindowManagerService.mWindowMap) {
- return mAlpha;
- }
- }
-
- public void setAlpha(int alpha) {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mAlpha == alpha) {
- return;
- }
- mAlpha = alpha;
- invalidate(null);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
- }
- }
- }
-
- public void setBounds(Region bounds) {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mBounds.equals(bounds)) {
- return;
- }
- mBounds.set(bounds);
- invalidate(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
- }
- }
- }
-
- public void updateSize() {
- synchronized (mWindowManagerService.mWindowMap) {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
- invalidate(mDirtyRect);
- }
- }
-
- public void invalidate(Rect dirtyRect) {
- if (dirtyRect != null) {
- mDirtyRect.set(dirtyRect);
- } else {
- mDirtyRect.setEmpty();
- }
- mInvalidated = true;
- mWindowManagerService.scheduleAnimationLocked();
- }
-
- /** NOTE: This has to be called within a surface transaction. */
- public void drawIfNeeded() {
- synchronized (mWindowManagerService.mWindowMap) {
- if (!mInvalidated) {
- return;
- }
- mInvalidated = false;
- Canvas canvas = null;
- try {
- // Empty dirty rectangle means unspecified.
- if (mDirtyRect.isEmpty()) {
- mBounds.getBounds(mDirtyRect);
- }
- mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
- canvas = mSurface.lockCanvas(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
- }
- } catch (IllegalArgumentException iae) {
- /* ignore */
- } catch (Surface.OutOfResourcesException oore) {
- /* ignore */
- }
- if (canvas == null) {
- return;
- }
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Bounds: " + mBounds);
- }
- canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
- mPaint.setAlpha(mAlpha);
- Path path = mBounds.getBoundaryPath();
- canvas.drawPath(path, mPaint);
-
- mSurface.unlockCanvasAndPost(canvas);
-
- if (mAlpha > 0) {
- mSurfaceControl.show();
- } else {
- mSurfaceControl.hide();
- }
- }
- }
-
- public void releaseSurface() {
- mSurfaceControl.release();
- mSurface.release();
- }
- }
- }
-
- private static final class WindowStateInfo {
- private static final int MAX_POOL_SIZE = 30;
-
- private static final SimplePool<WindowStateInfo> sPool =
- new SimplePool<WindowStateInfo>(MAX_POOL_SIZE);
-
- private static final Region mTempRegion = new Region();
-
- public WindowState mWindowState;
- public final Rect mTouchableRegion = new Rect();
-
- public static WindowStateInfo obtain(WindowState windowState) {
- WindowStateInfo info = sPool.acquire();
- if (info == null) {
- info = new WindowStateInfo();
- }
- info.mWindowState = windowState;
- windowState.getTouchableRegion(mTempRegion);
- mTempRegion.getBounds(info.mTouchableRegion);
- return info;
- }
-
- public void recycle() {
- mWindowState = null;
- mTouchableRegion.setEmpty();
- sPool.release(this);
- }
- }
-
- private class MyHandler extends Handler {
- public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
- public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
- public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
- public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
- public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
-
- public MyHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
- Region bounds = (Region) message.obj;
- try {
- mCallbacks.onMagnifedBoundsChanged(bounds);
- } catch (RemoteException re) {
- /* ignore */
- } finally {
- bounds.recycle();
- }
- } break;
- case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
- SomeArgs args = (SomeArgs) message.obj;
- final int left = args.argi1;
- final int top = args.argi2;
- final int right = args.argi3;
- final int bottom = args.argi4;
- try {
- mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
- } catch (RemoteException re) {
- /* ignore */
- } finally {
- args.recycle();
- }
- } break;
- case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
- try {
- mCallbacks.onUserContextChanged();
- } catch (RemoteException re) {
- /* ignore */
- }
- } break;
- case MESSAGE_NOTIFY_ROTATION_CHANGED: {
- final int rotation = message.arg1;
- try {
- mCallbacks.onRotationChanged(rotation);
- } catch (RemoteException re) {
- /* ignore */
- }
- } break;
- case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
- synchronized (mWindowManagerService.mWindowMap) {
- if (mMagnifedViewport.isMagnifyingLocked()) {
- mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
- mWindowManagerService.scheduleAnimationLocked();
- }
- }
- } break;
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 0c68258..266527d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -529,8 +529,9 @@ public class WindowAnimator {
mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
- mService.mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ if (mService.mAccessibilityController != null
+ && displayId == Display.DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a4f960e..d6a1e5d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -103,12 +103,12 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IInputFilter;
-import android.view.IMagnificationCallbacks;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
+import android.view.IWindowsForAccessibilityCallback;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -418,7 +418,7 @@ public class WindowManagerService extends IWindowManager.Stub
IInputMethodManager mInputMethodManager;
- DisplayMagnifier mDisplayMagnifier;
+ AccessibilityController mAccessibilityController;
final SurfaceSession mFxSession;
Watermark mWatermark;
@@ -2439,9 +2439,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mExiting = true;
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win, transit);
+ mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
if (win.mExiting || win.mWinAnimator.isAnimating()) {
@@ -2759,11 +2759,12 @@ public class WindowManagerService extends IWindowManager.Stub
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
synchronized (mWindowMap) {
- if (mDisplayMagnifier != null) {
+ if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
//TODO (multidisplay): Magnification is supported only for the default display.
if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+ mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle,
+ immediate);
}
}
}
@@ -2998,9 +2999,9 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.destroySurfaceLocked();
}
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win, transit);
+ mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
}
@@ -3143,86 +3144,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public void getWindowFrame(IBinder token, Rect outBounds) {
- if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
- "getWindowInfo()")) {
- throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = mWindowMap.get(token);
- if (windowState != null) {
- outBounds.set(windowState.mFrame);
- } else {
- outBounds.setEmpty();
- }
- }
- }
-
- @Override
- public void setMagnificationSpec(MagnificationSpec spec) {
- if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
- "setMagnificationSpec()")) {
- throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
- }
- synchronized (mWindowMap) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.setMagnificationSpecLocked(spec);
- } else {
- throw new IllegalStateException("Magnification callbacks not set!");
- }
- }
- if (Binder.getCallingPid() != android.os.Process.myPid()) {
- spec.recycle();
- }
- }
-
- @Override
- public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
- if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
- "getCompatibleMagnificationSpecForWindow()")) {
- throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = mWindowMap.get(windowToken);
- if (windowState == null) {
- return null;
- }
- MagnificationSpec spec = null;
- if (mDisplayMagnifier != null) {
- spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
- }
- if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
- return null;
- }
- spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
- spec.scale *= windowState.mGlobalScale;
- return spec;
- }
- }
-
- @Override
- public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
- if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
- "setMagnificationCallbacks()")) {
- throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
- }
- synchronized (mWindowMap) {
- if (mDisplayMagnifier == null) {
- mDisplayMagnifier = new DisplayMagnifier(this, callbacks);
- } else {
- if (callbacks == null) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.destroyLocked();
- mDisplayMagnifier = null;
- }
- } else {
- throw new IllegalStateException("Magnification callbacks already set!");
- }
- }
- }
- }
-
private boolean applyAnimationLocked(AppWindowToken atoken,
WindowManager.LayoutParams lp, int transit, boolean enter) {
// Only apply an animation if the display isn't frozen. If it is
@@ -3402,8 +3323,8 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null && win.isDefaultDisplay()) {
- mDisplayMagnifier.onWindowTransitionLocked(win,
+ if (mAccessibilityController != null && win.isDefaultDisplay()) {
+ mAccessibilityController.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_EXIT);
}
changed = true;
@@ -4259,9 +4180,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
WindowState window = wtoken.findMainWindow();
//TODO (multidisplay): Magnification is supported only for the default display.
- if (window != null && mDisplayMagnifier != null
+ if (window != null && mAccessibilityController != null
&& window.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onAppWindowTransitionLocked(window, transit);
+ mAccessibilityController.onAppWindowTransitionLocked(window, transit);
}
changed = true;
}
@@ -4281,9 +4202,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_ENTER, true);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win,
+ mAccessibilityController.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_ENTER);
}
}
@@ -4298,9 +4219,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_EXIT, false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowTransitionLocked(win,
+ mAccessibilityController.onWindowTransitionLocked(win,
WindowManagerPolicy.TRANSIT_EXIT);
}
}
@@ -5278,19 +5199,6 @@ public class WindowManagerService extends IWindowManager.Stub
ShutdownThread.rebootSafeMode(mContext, confirm);
}
- @Override
- public void setInputFilter(IInputFilter filter) {
- if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) {
- throw new SecurityException("Requires FILTER_EVENTS permission");
- }
- mInputManager.setInputFilter(filter);
- }
-
- @Override
- public void setTouchExplorationEnabled(boolean enabled) {
- mPolicy.setTouchExplorationEnabled(enabled);
- }
-
public void updateRelatedUserIds(final int[] relatedUserIds) {
synchronized (mWindowMap) {
mRelatedUserIds = relatedUserIds;
@@ -6106,9 +6014,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mDisplayMagnifier != null
+ if (mAccessibilityController != null
&& displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
+ mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
}
return true;
@@ -7015,21 +6923,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public IBinder getFocusedWindowToken() {
- if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
- "getFocusedWindowToken()")) {
- throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = getFocusedWindowLocked();
- if (windowState != null) {
- return windowState.mClient.asBinder();
- }
- return null;
- }
- }
-
private WindowState getFocusedWindow() {
synchronized (mWindowMap) {
return getFocusedWindowLocked();
@@ -8209,9 +8102,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mDisplayMagnifier != null && anyLayerChanged
+ if (mAccessibilityController != null && anyLayerChanged
&& windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
- mDisplayMagnifier.onWindowLayersChangedLocked();
+ mAccessibilityController.onWindowLayersChangedLocked();
}
}
@@ -9844,6 +9737,11 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
+
+ if (mAccessibilityController != null) {
+ mAccessibilityController.onWindowFocusChangedLocked();
+ }
+
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
@@ -10957,5 +10855,100 @@ public class WindowManagerService extends IWindowManager.Stub
public void requestTraversalFromDisplayManager() {
requestTraversal();
}
+
+ @Override
+ public void setMagnificationSpec(MagnificationSpec spec) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController != null) {
+ mAccessibilityController.setMagnificationSpecLocked(spec);
+ } else {
+ throw new IllegalStateException("Magnification callbacks not set!");
+ }
+ }
+ if (Binder.getCallingPid() != android.os.Process.myPid()) {
+ spec.recycle();
+ }
+ }
+
+ @Override
+ public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
+ synchronized (mWindowMap) {
+ WindowState windowState = mWindowMap.get(windowToken);
+ if (windowState == null) {
+ return null;
+ }
+ MagnificationSpec spec = null;
+ if (mAccessibilityController != null) {
+ spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState);
+ }
+ if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
+ return null;
+ }
+ spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
+ spec.scale *= windowState.mGlobalScale;
+ return spec;
+ }
+ }
+
+ @Override
+ public void setMagnificationCallbacks(MagnificationCallbacks callbacks) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController == null) {
+ mAccessibilityController = new AccessibilityController(
+ WindowManagerService.this);
+ }
+ mAccessibilityController.setMagnificationCallbacksLocked(callbacks);
+ if (!mAccessibilityController.hasCallbacksLocked()) {
+ mAccessibilityController = null;
+ }
+ }
+ }
+
+ @Override
+ public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController == null) {
+ mAccessibilityController = new AccessibilityController(
+ WindowManagerService.this);
+ }
+ mAccessibilityController.setWindowsForAccessibilityCallback(callback);
+ if (!mAccessibilityController.hasCallbacksLocked()) {
+ mAccessibilityController = null;
+ }
+ }
+ }
+
+ @Override
+ public void setInputFilter(IInputFilter filter) {
+ mInputManager.setInputFilter(filter);
+ }
+
+ @Override
+ public IBinder getFocusedWindowToken() {
+ synchronized (mWindowMap) {
+ WindowState windowState = getFocusedWindowLocked();
+ if (windowState != null) {
+ return windowState.mClient.asBinder();
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isKeyguardLocked() {
+ return isKeyguardLocked();
+ }
+
+ @Override
+ public void getWindowFrame(IBinder token, Rect outBounds) {
+ synchronized (mWindowMap) {
+ WindowState windowState = mWindowMap.get(token);
+ if (windowState != null) {
+ outBounds.set(windowState.mFrame);
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 93f6d22..2cd6000 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -952,8 +952,8 @@ class WindowStateAnimator {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
- MagnificationSpec spec = mService.mDisplayMagnifier
+ if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+ MagnificationSpec spec = mService.mAccessibilityController
.getMagnificationSpecForWindowLocked(mWin);
if (spec != null && !spec.isNop()) {
tmpMatrix.postScale(spec.scale, spec.scale);
@@ -1032,8 +1032,8 @@ class WindowStateAnimator {
&& mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
MagnificationSpec spec = null;
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
- spec = mService.mDisplayMagnifier.getMagnificationSpecForWindowLocked(mWin);
+ if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+ spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
}
if (applyUniverseTransformation || spec != null) {
final Rect frame = mWin.mFrame;
@@ -1565,9 +1565,9 @@ class WindowStateAnimator {
}
applyAnimationLocked(transit, true);
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mDisplayMagnifier != null
+ if (mService.mAccessibilityController != null
&& mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mService.mDisplayMagnifier.onWindowTransitionLocked(mWin, transit);
+ mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
}
}