Merge "Import translations. DO NOT MERGE"
diff --git a/Android.mk b/Android.mk
index a691987..d279704 100644
--- a/Android.mk
+++ b/Android.mk
@@ -196,7 +196,6 @@
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 @@
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/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 e72354b..e41b3fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2328,6 +2328,7 @@
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 @@
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 @@
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);
@@ -19546,8 +19549,10 @@
public abstract class Vibrator {
method public abstract void cancel();
method public abstract boolean hasVibrator();
- method public abstract void vibrate(long);
- method public abstract void vibrate(long[], int);
+ method public void vibrate(long);
+ method public void vibrate(long, int);
+ method public void vibrate(long[], int);
+ method public void vibrate(long[], int, int);
}
public class WorkSource implements android.os.Parcelable {
@@ -30398,6 +30403,7 @@
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
}
@@ -30461,6 +30467,7 @@
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();
@@ -30607,6 +30614,7 @@
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 {
@@ -30658,6 +30666,28 @@
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/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b9afe40..cb7e1a0 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -1,4 +1,6 @@
LOCAL_PATH:= $(call my-dir)
+
+# 32-bit app_process
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -12,9 +14,29 @@
libandroid_runtime
LOCAL_MODULE:= app_process
-
+LOCAL_32_BIT_ONLY := true
include $(BUILD_EXECUTABLE)
+ifeq ($(TARGET_IS_64_BIT),true)
+
+# 64-bit app_process64
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ app_main.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ liblog \
+ libbinder \
+ libandroid_runtime
+
+LOCAL_MODULE:= app_process64
+LOCAL_NO_2ND_ARCH := true
+include $(BUILD_EXECUTABLE)
+
+endif # TARGET_IS_64_BIT
# Build a variant of app_process binary linked with ASan runtime.
# ARM-only at the moment.
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.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 @@
* 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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
} 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 bdc4fdde..4f9ba59 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -284,6 +284,27 @@
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 @@
* @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 @@
* @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 @@
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 @@
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 @@
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityWindowInfo;
import android.view.KeyEvent;
/**
@@ -35,7 +36,9 @@
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.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 @@
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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 079cf7a..b616c1e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -780,6 +780,25 @@
}
}
+ /**
+ * Set a non-persisted restriction on an audio operation at a stream-level.
+ * Restrictions are temporary additional constraints imposed on top of the persisted rules
+ * defined by {@link #setMode}.
+ *
+ * @param code The operation to restrict.
+ * @param stream The {@link android.media.AudioManager} stream type.
+ * @param mode The restriction mode (MODE_IGNORED,MODE_ERRORED) or MODE_ALLOWED to unrestrict.
+ * @param exceptionPackages Optional list of packages to exclude from the restriction.
+ * @hide
+ */
+ public void setRestriction(int code, int stream, int mode, String[] exceptionPackages) {
+ try {
+ final int uid = Binder.getCallingUid();
+ mService.setAudioRestriction(code, stream, uid, mode, exceptionPackages);
+ } catch (RemoteException e) {
+ }
+ }
+
/** @hide */
public void resetAllModes() {
try {
@@ -1009,6 +1028,35 @@
}
/**
+ * Like {@link #checkOp} but at a stream-level for audio operations.
+ * @hide
+ */
+ public int checkAudioOp(int op, int stream, int uid, String packageName) {
+ try {
+ final int mode = mService.checkAudioOperation(op, stream, uid, packageName);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
+ }
+ return mode;
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ /**
+ * Like {@link #checkAudioOp} but instead of throwing a {@link SecurityException} it
+ * returns {@link #MODE_ERRORED}.
+ * @hide
+ */
+ public int checkAudioOpNoThrow(int op, int stream, int uid, String packageName) {
+ try {
+ return mService.checkAudioOperation(op, stream, uid, packageName);
+ } catch (RemoteException e) {
+ }
+ return MODE_IGNORED;
+ }
+
+ /**
* Make note of an application performing an operation. Note that you must pass
* in both the uid and name of the application to be checked; this function will verify
* that these two match, and if not, return {@link #MODE_IGNORED}. If this call
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.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 @@
* @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 @@
}
/**
+ * 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 @@
* 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/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 38a71aa..a4374b8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -19,7 +19,9 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -180,43 +182,6 @@
"android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
/**
- * Activity Action: Show a system activity to request BLE advertising.<br>
- * If the device is not doing BLE advertising, this activity will start BLE advertising for the
- * device, otherwise it will continue BLE advertising using the current
- * {@link BluetoothAdvScanData}. <br>
- * Note this activity will also request the user to turn on Bluetooth if it's not currently
- * enabled.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_START_ADVERTISING =
- "android.bluetooth.adapter.action.START_ADVERTISING";
-
- /**
- * Activity Action: Stop the current BLE advertising.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_STOP_ADVERTISING =
- "android.bluetooth.adapter.action.STOP_ADVERTISING";
-
- /**
- * Broadcast Action: Indicate BLE Advertising is started.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
- "android.bluetooth.adapter.action.ADVERTISING_STARTED";
-
- /**
- * Broadcast Action: Indicated BLE Advertising is stopped.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
- "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
-
- /**
* Activity Action: Show a system activity that allows the user to turn on
* Bluetooth.
* <p>This system activity will return once Bluetooth has completed turning
@@ -248,6 +213,22 @@
"android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
/**
+ * Broadcast Action: Indicate BLE Advertising is started.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
+ "android.bluetooth.adapter.action.ADVERTISING_STARTED";
+
+ /**
+ * Broadcast Action: Indicated BLE Advertising is stopped.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
+ "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
+
+ /**
* Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
* intents to request the current scan mode. Possible values are:
* {@link #SCAN_MODE_NONE},
@@ -383,9 +364,27 @@
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING = 3;
+ /** States for Bluetooth LE advertising */
+ /** @hide */
+ public static final int STATE_ADVERTISE_STARTING = 0;
+ /** @hide */
+ public static final int STATE_ADVERTISE_STARTED = 1;
+ /** @hide */
+ public static final int STATE_ADVERTISE_STOPPING = 2;
+ /** @hide */
+ public static final int STATE_ADVERTISE_STOPPED = 3;
+ /**
+ * Force stopping advertising without callback in case the advertising app dies.
+ * @hide
+ */
+ public static final int STATE_ADVERTISE_FORCE_STOPPING = 4;
+
/** @hide */
public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+ /** @hide */
+ public static final int ADVERTISE_CALLBACK_SUCCESS = 0;
+
private static final int ADDRESS_LENGTH = 17;
/**
@@ -399,7 +398,9 @@
private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
private BluetoothAdvScanData mBluetoothAdvScanData = null;
- private GattCallbackWrapper mAdvertisingCallback;
+ private GattCallbackWrapper mAdvertisingGattCallback;
+ private final Handler mHandler; // Handler to post the advertise callback to run on main thread.
+ private final Object mLock = new Object();
/**
* Get a handle to the default local Bluetooth adapter.
@@ -435,6 +436,7 @@
} catch (RemoteException e) {Log.e(TAG, "", e);}
mManagerService = managerService;
mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
+ mHandler = new Handler(Looper.getMainLooper());
}
/**
@@ -474,6 +476,7 @@
/**
* Returns a {@link BluetoothAdvScanData} object representing advertising data.
+ * Data will be reset when bluetooth service is turned off.
* @hide
*/
public BluetoothAdvScanData getAdvScanData() {
@@ -494,19 +497,34 @@
}
}
+ /**
+ * Interface for BLE advertising callback.
+ *
+ * @hide
+ */
+ public interface AdvertiseCallback {
+ /**
+ * Callback when advertise starts.
+ * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+ */
+ void onAdvertiseStart(int status);
+ /**
+ * Callback when advertise stops.
+ * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+ */
+ void onAdvertiseStop(int status);
+ }
/**
* Start BLE advertising using current {@link BluetoothAdvScanData}.
- * An app should start advertising by requesting
- * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
*
- * @return true if BLE avertising succeeds, false otherwise.
+ * @param callback - {@link AdvertiseCallback}
+ * @return true if BLE advertising succeeds, false otherwise.
* @hide
*/
- public boolean startAdvertising() {
+ public boolean startAdvertising(final AdvertiseCallback callback) {
if (getState() != STATE_ON) return false;
-
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
@@ -516,18 +534,31 @@
// Restart/reset advertising packets if advertising is in progress.
if (isAdvertising()) {
// Invalid advertising callback.
- if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) {
+ if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) {
Log.e(TAG, "failed to restart advertising, invalid callback");
return false;
}
- iGatt.startAdvertising(mAdvertisingCallback.mLeHandle);
+ iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle);
+ // Run the callback from main thread.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // callback with status success.
+ callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS);
+ }
+ });
return true;
}
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper =
- new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV);
+ new GattCallbackWrapper(this, null, null, callback);
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
- mAdvertisingCallback = wrapper;
+ if (!wrapper.advertiseStarted()) {
+ return false;
+ }
+ synchronized (mLock) {
+ mAdvertisingGattCallback = wrapper;
+ }
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -537,25 +568,28 @@
/**
* Stop BLE advertising.
- * An app should stop advertising by requesting
- * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
+ *
+ * @param callback - {@link AdvertiseCallback}
* @return true if BLE advertising stops, false otherwise.
* @hide
*/
- public boolean stopAdvertisting() {
+ public boolean stopAdvertising(AdvertiseCallback callback) {
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return false;
}
- if (mAdvertisingCallback == null) {
+ if (mAdvertisingGattCallback == null) {
// no callback.
return false;
}
- mAdvertisingCallback.stopAdvertising();
- mAdvertisingCallback = null;
+ // Make sure same callback is used for start and stop advertising.
+ if (callback != mAdvertisingGattCallback.mAdvertiseCallback) {
+ Log.e(TAG, "must use the same callback for star/stop advertising");
+ return false;
+ }
+ mAdvertisingGattCallback.stopAdvertising();
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1415,6 +1449,8 @@
if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
synchronized (mManagerCallback) {
mService = null;
+ // Reset bluetooth adv scan data when Gatt service is down.
+ mBluetoothAdvScanData = null;
for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
try {
if (cb != null) {
@@ -1689,11 +1725,9 @@
private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
- private static final int CALLBACK_TYPE_SCAN = 0;
- private static final int CALLBACK_TYPE_ADV = 1;
+ private final AdvertiseCallback mAdvertiseCallback;
private final LeScanCallback mLeScanCb;
- private int mCallbackType;
// mLeHandle 0: not registered
// -1: scan stopped
@@ -1708,26 +1742,34 @@
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
- mCallbackType = CALLBACK_TYPE_SCAN;
+ mAdvertiseCallback = null;
}
public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
- UUID[] uuid, int type) {
+ UUID[] uuid, AdvertiseCallback callback) {
mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
- mCallbackType = type;
+ mAdvertiseCallback = callback;
}
public boolean scanStarted() {
+ return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
+ }
+
+ public boolean advertiseStarted() {
+ // Wait for registeration callback.
+ return waitForRegisteration(1);
+ }
+
+ private boolean waitForRegisteration(int maxWaitCount) {
boolean started = false;
synchronized(this) {
if (mLeHandle == -1) return false;
-
int count = 0;
// wait for callback registration and LE scan to start
- while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) {
+ while (mLeHandle == 0 && count < maxWaitCount) {
try {
wait(LE_CALLBACK_REG_TIMEOUT);
} catch (InterruptedException e) {
@@ -1751,15 +1793,12 @@
try {
IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
iGatt.stopAdvertising();
- Log.d(TAG, "unregeistering client " + mLeHandle);
- iGatt.unregisterClient(mLeHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to stop advertising and unregister" + e);
+ Log.e(TAG, "Failed to stop advertising" + e);
}
} else {
Log.e(TAG, "stopAdvertising, BluetoothAdapter is null");
}
- mLeHandle = -1;
notifyAll();
}
}
@@ -1805,7 +1844,7 @@
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter != null) {
iGatt = adapter.getBluetoothManager().getBluetoothGatt();
- if (mCallbackType == CALLBACK_TYPE_ADV) {
+ if (mAdvertiseCallback != null) {
iGatt.startAdvertising(mLeHandle);
} else {
if (mScanFilter == null) {
@@ -1855,7 +1894,7 @@
* @hide
*/
public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+ if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
// Check null in case the scan has been stopped
synchronized(this) {
@@ -1944,9 +1983,33 @@
// no op
}
- public void onListen(int status) {
- // no op
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status);
+ if (advertiseState == STATE_ADVERTISE_STARTED) {
+ mAdvertiseCallback.onAdvertiseStart(status);
+ } else {
+ synchronized (this) {
+ if (status == ADVERTISE_CALLBACK_SUCCESS) {
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter != null) {
+ try {
+ IBluetoothGatt iGatt =
+ adapter.getBluetoothManager().getBluetoothGatt();
+ Log.d(TAG, "unregistering client " + mLeHandle);
+ iGatt.unregisterClient(mLeHandle);
+ // Reset advertise app handle.
+ mLeHandle = -1;
+ adapter.mAdvertisingGattCallback = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister client" + e);
+ }
+ } else {
+ Log.e(TAG, "cannot unregister client, BluetoothAdapter is null");
+ }
+ }
+ }
+ mAdvertiseCallback.onAdvertiseStop(status);
+ }
}
}
-
}
diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java
index a97b0a8..df2c256 100644
--- a/core/java/android/bluetooth/BluetoothAdvScanData.java
+++ b/core/java/android/bluetooth/BluetoothAdvScanData.java
@@ -77,6 +77,7 @@
try {
return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to set manufacturer id and data.", e);
return false;
}
}
@@ -92,6 +93,7 @@
try {
return mBluetoothGatt.setAdvServiceData(serviceData);
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to set service data.", e);
return false;
}
}
@@ -103,6 +105,7 @@
try {
return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get service uuids.", e);
return null;
}
}
@@ -115,6 +118,7 @@
try {
return mBluetoothGatt.getAdvManufacturerData();
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get manufacturer data.", e);
return null;
}
}
@@ -127,6 +131,7 @@
try {
return mBluetoothGatt.getAdvServiceData();
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get service data.", e);
return null;
}
}
@@ -140,7 +145,7 @@
try {
mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
} catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e);
}
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index e3820a2..39305b0 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,8 +16,6 @@
package android.bluetooth;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -544,6 +542,15 @@
Log.w(TAG, "Unhandled exception in callback", ex);
}
}
+
+ /**
+ * Advertise state change callback
+ * @hide
+ */
+ public void onAdvertiseStateChange(int state, int status) {
+ if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
+ + state + " status=" + status);
+ }
};
/*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index e3563fc..7c69a06 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -63,4 +63,5 @@
in int charInstId, in ParcelUuid charUuid,
in byte[] value);
void onReadRemoteRssi(in String address, in int rssi, in int status);
+ oneway void onAdvertiseStateChange(in int advertiseState, in int status);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 6e2a099..35c86e7 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -45,6 +45,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
/**
@@ -2149,10 +2150,20 @@
private static final String PIXEL_FORMAT_JPEG = "jpeg";
private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb";
- private HashMap<String, String> mMap;
+ /**
+ * Order matters: Keys that are {@link #set(String, String) set} later
+ * will take precedence over keys that are set earlier (if the two keys
+ * conflict with each other).
+ *
+ * <p>One example is {@link #setPreviewFpsRange(int, int)} , since it
+ * conflicts with {@link #setPreviewFrameRate(int)} whichever key is set later
+ * is the one that will take precedence.
+ * </p>
+ */
+ private final LinkedHashMap<String, String> mMap;
private Parameters() {
- mMap = new HashMap<String, String>(64);
+ mMap = new LinkedHashMap<String, String>(/*initialCapacity*/64);
}
/**
@@ -2232,7 +2243,7 @@
return;
}
- mMap.put(key, value);
+ put(key, value);
}
/**
@@ -2242,7 +2253,18 @@
* @param value the int value of the parameter
*/
public void set(String key, int value) {
- mMap.put(key, Integer.toString(value));
+ put(key, Integer.toString(value));
+ }
+
+ private void put(String key, String value) {
+ /*
+ * Remove the key if it already exists.
+ *
+ * This way setting a new value for an already existing key will always move
+ * that key to be ordered the latest in the map.
+ */
+ mMap.remove(key);
+ mMap.put(key, value);
}
private void set(String key, List<Area> areas) {
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index e3a3830..0c0dfe9 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -853,13 +853,21 @@
return true;
}
+ /**
+ * @hide
+ */
@Override
- public void vibrate(long milliseconds) {
+ public void vibrate(int owningUid, String owningPackage, long milliseconds,
+ int streamHint) {
vibrate(new long[] { 0, milliseconds}, -1);
}
+ /**
+ * @hide
+ */
@Override
- public void vibrate(long[] pattern, int repeat) {
+ public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+ int streamHint) {
if (repeat >= pattern.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -870,22 +878,6 @@
}
}
- /**
- * @hide
- */
- @Override
- public void vibrate(int owningUid, String owningPackage, long milliseconds) {
- vibrate(milliseconds);
- }
-
- /**
- * @hide
- */
- @Override
- public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
- vibrate(pattern, repeat);
- }
-
@Override
public void cancel() {
try {
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 6c61046..10b5d0b 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -183,8 +183,10 @@
Thread dhcpThread = new Thread(new Runnable() {
public void run() {
DhcpResults dhcpResults = new DhcpResults();
+ mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+ mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
return;
}
mLinkProperties = dhcpResults.linkProperties;
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 3c3d8ec..a470e88 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -561,6 +561,17 @@
return false;
}
+
+ public void setInternalDataEnable(boolean enabled) {
+ if (DBG) log("setInternalDataEnable: E enabled=" + enabled);
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE,
+ enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
+ }
+ if (VDBG) log("setInternalDataEnable: X enabled=" + enabled);
+ }
+
@Override
public void setUserDataEnable(boolean enabled) {
if (DBG) log("setUserDataEnable: E enabled=" + enabled);
diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java
index 346f5de..76b0dc4 100644
--- a/core/java/android/os/BatteryProperty.java
+++ b/core/java/android/os/BatteryProperty.java
@@ -29,6 +29,7 @@
public static final int BATTERY_PROP_CHARGE_COUNTER = 1;
public static final int BATTERY_PROP_CURRENT_NOW = 2;
public static final int BATTERY_PROP_CURRENT_AVG = 3;
+ public static final int BATTERY_PROP_CAPACITY = 4;
public int valueInt;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7db4ac2..b0d94d5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -570,13 +570,15 @@
public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
public static final int STATE_WIFI_ON_FLAG = 1<<17;
public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
-
+
public static final int MOST_INTERESTING_STATES =
STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
| STATE_GPS_ON_FLAG | STATE_PHONE_IN_CALL_FLAG;
public int states;
+ public int states2;
+
// The wake lock that was acquired at this point.
public HistoryTag wakelockTag;
@@ -1216,6 +1218,11 @@
public abstract int getHighDischargeAmountSinceCharge();
/**
+ * Retrieve the discharge amount over the selected discharge period <var>which</var>.
+ */
+ public abstract int getDischargeAmount(int which);
+
+ /**
* Get the amount the battery has discharged while the screen was on,
* since the last time power was unplugged.
*/
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 54e2c0b..e96398a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -41,6 +41,7 @@
private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+ private static final String ENV_OEM_ROOT = "OEM_ROOT";
/** {@hide} */
public static final String DIR_ANDROID = "Android";
@@ -55,6 +56,7 @@
public static final String DIRECTORY_ANDROID = DIR_ANDROID;
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+ private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
@@ -205,13 +207,24 @@
}
/**
- * Gets the Android root directory.
+ * Return root of the "system" partition holding the core Android OS.
+ * Always present and mounted read-only.
*/
public static File getRootDirectory() {
return DIR_ANDROID_ROOT;
}
/**
+ * Return root directory of the "oem" partition holding OEM customizations,
+ * if any. If present, the partition is mounted read-only.
+ *
+ * @hide
+ */
+ public static File getOemDirectory() {
+ return DIR_OEM_ROOT;
+ }
+
+ /**
* Gets the system directory available for secure storage.
* If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
* Otherwise, it returns the unencrypted /data/system directory.
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 456ffb1..4854bc0 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -20,8 +20,8 @@
interface IVibratorService
{
boolean hasVibrator();
- void vibrate(int uid, String packageName, long milliseconds, IBinder token);
- void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, IBinder token);
+ void vibrate(int uid, String packageName, long milliseconds, int streamHint, IBinder token);
+ void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, int streamHint, IBinder token);
void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
index af90bdb..536da32 100644
--- a/core/java/android/os/NullVibrator.java
+++ b/core/java/android/os/NullVibrator.java
@@ -36,22 +36,11 @@
return false;
}
- @Override
- public void vibrate(long milliseconds) {
- }
-
- @Override
- public void vibrate(long[] pattern, int repeat) {
- if (repeat >= pattern.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- }
-
/**
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long milliseconds) {
+ public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) {
vibrate(milliseconds);
}
@@ -59,8 +48,11 @@
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
- vibrate(pattern, repeat);
+ public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+ int streamHint) {
+ if (repeat >= pattern.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
}
@Override
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 700f80d..13bc4f6 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,7 +16,6 @@
package android.os;
-import android.app.ActivityThread;
import android.content.Context;
import android.util.Log;
@@ -28,18 +27,16 @@
public class SystemVibrator extends Vibrator {
private static final String TAG = "Vibrator";
- private final String mPackageName;
private final IVibratorService mService;
private final Binder mToken = new Binder();
public SystemVibrator() {
- mPackageName = ActivityThread.currentPackageName();
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
public SystemVibrator(Context context) {
- mPackageName = context.getOpPackageName();
+ super(context);
mService = IVibratorService.Stub.asInterface(
ServiceManager.getService("vibrator"));
}
@@ -57,27 +54,17 @@
return false;
}
- @Override
- public void vibrate(long milliseconds) {
- vibrate(Process.myUid(), mPackageName, milliseconds);
- }
-
- @Override
- public void vibrate(long[] pattern, int repeat) {
- vibrate(Process.myUid(), mPackageName, pattern, repeat);
- }
-
/**
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long milliseconds) {
+ public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return;
}
try {
- mService.vibrate(owningUid, owningPackage, milliseconds, mToken);
+ mService.vibrate(owningUid, owningPackage, milliseconds, streamHint, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
@@ -87,7 +74,8 @@
* @hide
*/
@Override
- public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
+ public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+ int streamHint) {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return;
@@ -97,7 +85,8 @@
// anyway
if (repeat < pattern.length) {
try {
- mService.vibratePattern(owningUid, owningPackage, pattern, repeat, mToken);
+ mService.vibratePattern(owningUid, owningPackage, pattern, repeat, streamHint,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 5d55143..8845ba3 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,7 +16,9 @@
package android.os;
+import android.app.ActivityThread;
import android.content.Context;
+import android.media.AudioManager;
/**
* Class that operates the vibrator on the device.
@@ -28,10 +30,21 @@
* {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument.
*/
public abstract class Vibrator {
+
+ private final String mPackageName;
+
/**
* @hide to prevent subclassing from outside of the framework
*/
public Vibrator() {
+ mPackageName = ActivityThread.currentPackageName();
+ }
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ protected Vibrator(Context context) {
+ mPackageName = context.getOpPackageName();
}
/**
@@ -40,7 +53,7 @@
* @return True if the hardware has a vibrator, else false.
*/
public abstract boolean hasVibrator();
-
+
/**
* Vibrate constantly for the specified period of time.
* <p>This method requires the caller to hold the permission
@@ -48,7 +61,23 @@
*
* @param milliseconds The number of milliseconds to vibrate.
*/
- public abstract void vibrate(long milliseconds);
+ public void vibrate(long milliseconds) {
+ vibrate(milliseconds, AudioManager.USE_DEFAULT_STREAM_TYPE);
+ }
+
+ /**
+ * Vibrate constantly for the specified period of time.
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#VIBRATE}.
+ *
+ * @param milliseconds The number of milliseconds to vibrate.
+ * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
+ * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
+ * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+ */
+ public void vibrate(long milliseconds, int streamHint) {
+ vibrate(Process.myUid(), mPackageName, milliseconds, streamHint);
+ }
/**
* Vibrate with a given pattern.
@@ -70,21 +99,52 @@
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
*/
- public abstract void vibrate(long[] pattern, int repeat);
+ public void vibrate(long[] pattern, int repeat) {
+ vibrate(pattern, repeat, AudioManager.USE_DEFAULT_STREAM_TYPE);
+ }
+
+ /**
+ * Vibrate with a given pattern.
+ *
+ * <p>
+ * Pass in an array of ints that are the durations for which to turn on or off
+ * the vibrator in milliseconds. The first value indicates the number of milliseconds
+ * to wait before turning the vibrator on. The next value indicates the number of milliseconds
+ * for which to keep the vibrator on before turning it off. Subsequent values alternate
+ * between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
+ * </p><p>
+ * To cause the pattern to repeat, pass the index into the pattern array at which
+ * to start the repeat, or -1 to disable repeating.
+ * </p>
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#VIBRATE}.
+ *
+ * @param pattern an array of longs of times for which to turn the vibrator on or off.
+ * @param repeat the index into pattern at which to repeat, or -1 if
+ * you don't want to repeat.
+ * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
+ * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
+ * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+ */
+ public void vibrate(long[] pattern, int repeat, int streamHint) {
+ vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint);
+ }
/**
* @hide
- * Like {@link #vibrate(long)}, but allowing the caller to specify that
+ * Like {@link #vibrate(long, int)}, but allowing the caller to specify that
* the vibration is owned by someone else.
*/
- public abstract void vibrate(int owningUid, String owningPackage, long milliseconds);
+ public abstract void vibrate(int owningUid, String owningPackage,
+ long milliseconds, int streamHint);
/**
* @hide
- * Like {@link #vibrate(long[], int)}, but allowing the caller to specify that
+ * Like {@link #vibrate(long[], int, int)}, but allowing the caller to specify that
* the vibration is owned by someone else.
*/
- public abstract void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat);
+ public abstract void vibrate(int owningUid, String owningPackage,
+ long[] pattern, int repeat, int streamHint);
/**
* Turn the vibrator off.
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
new file mode 100644
index 0000000..6381884
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -0,0 +1,117 @@
+/*
+ * 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.provider;
+
+import android.content.Context;
+
+import java.util.Locale;
+
+/**
+ * The Indexable data for Search. This abstract class defines the common parts for all search
+ * indexable data.
+ *
+ * @hide
+ */
+public abstract class SearchIndexableData {
+
+ /**
+ * The context for the data. Will usually allow to retrieve some resources.
+ *
+ * @see Context
+ */
+ public Context context;
+
+ /**
+ * The locale for the data
+ */
+ public Locale locale;
+
+ /**
+ * The rank for the data. This is application specific.
+ */
+ public int rank;
+
+ /**
+ * The class name associated with the data. Generally this is a Fragment class name for
+ * referring where the data is coming from and for launching the associated Fragment for
+ * displaying the data. This is used only when the data is provided "locally".
+ *
+ * If the data is provided "externally", the relevant information come from the
+ * {@link SearchIndexableData#intentAction} and {@link SearchIndexableData#intentTargetPackage}
+ * and {@link SearchIndexableData#intentTargetClass}.
+ *
+ * @see SearchIndexableData#intentAction
+ * @see SearchIndexableData#intentTargetPackage
+ * @see SearchIndexableData#intentTargetClass
+ */
+ public String className;
+
+ /**
+ * The package name for retrieving the icon associated with the data.
+ *
+ * @see SearchIndexableData#iconResId
+ */
+ public String packageName;
+
+ /**
+ * The icon resource ID associated with the data.
+ *
+ * @see SearchIndexableData#packageName
+ */
+ public int iconResId;
+
+ /**
+ * The Intent action associated with the data. This is used when the
+ * {@link SearchIndexableData#className} is not relevant.
+ *
+ * @see SearchIndexableData#intentTargetPackage
+ * @see SearchIndexableData#intentTargetClass
+ */
+ public String intentAction;
+
+ /**
+ * The Intent target package associated with the data.
+ *
+ * @see SearchIndexableData#intentAction
+ * @see SearchIndexableData#intentTargetClass
+ */
+ public String intentTargetPackage;
+
+ /**
+ * The Intent target class associated with the data.
+ *
+ * @see SearchIndexableData#intentAction
+ * @see SearchIndexableData#intentTargetPackage
+ */
+ public String intentTargetClass;
+
+ /**
+ * Default constructor.
+ */
+ public SearchIndexableData() {
+ }
+
+ /**
+ * Constructor with a {@link Context}.
+ *
+ * @param ctx the Context
+ */
+ public SearchIndexableData(Context ctx) {
+ context = ctx;
+ locale = Locale.getDefault();
+ }
+}
diff --git a/core/java/android/provider/SearchIndexableResource.java b/core/java/android/provider/SearchIndexableResource.java
new file mode 100644
index 0000000..ba3bd4f
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableResource.java
@@ -0,0 +1,65 @@
+/*
+ * 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.provider;
+
+import android.content.Context;
+
+/**
+ * Search Indexable Resource.
+ *
+ * This class wraps a set of reference information representing data that can be indexed from a
+ * resource which would typically be a {@link android.preference.PreferenceScreen}.
+ *
+ * xmlResId: the resource ID of a {@link android.preference.PreferenceScreen} XML file.
+ *
+ * @see SearchIndexableData
+ * @see android.preference.PreferenceScreen
+ *
+ * @hide
+ */
+public class SearchIndexableResource extends SearchIndexableData {
+
+ /**
+ * Resource ID of the associated {@link android.preference.PreferenceScreen} XML file.
+ */
+ public int xmlResId;
+
+ /**
+ * Constructor.
+ *
+ * @param rank the rank of the data.
+ * @param xmlResId the resource ID of a {@link android.preference.PreferenceScreen} XML file.
+ * @param className the class name associated with the data (generally a
+ * {@link android.app.Fragment}).
+ * @param iconResId the resource ID associated with the data.
+ */
+ public SearchIndexableResource(int rank, int xmlResId, String className, int iconResId) {
+ this.rank = rank;
+ this.xmlResId = xmlResId;
+ this.className = className;
+ this.iconResId = iconResId;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context the Context associated with the data.
+ */
+ public SearchIndexableResource(Context context) {
+ super(context);
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
new file mode 100644
index 0000000..b8635b8
--- /dev/null
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -0,0 +1,179 @@
+/*
+ * 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.provider;
+
+import android.content.ContentResolver;
+
+/**
+ * Describe the contract for an Indexable data.
+ *
+ * @hide
+ */
+public class SearchIndexablesContract {
+
+ /**
+ * Intent action used to identify {@link SearchIndexablesProvider}
+ * instances. This is used in the {@code <intent-filter>} of a {@code <provider>}.
+ */
+ public static final String PROVIDER_INTERFACE =
+ "android.content.action.SEARCH_INDEXABLES_PROVIDER";
+
+ private static final String SETTINGS = "settings";
+
+ /**
+ * Indexable references name.
+ */
+ public static final String INDEXABLES_XML_RES = "indexables_xml_res";
+
+ /**
+ * ContentProvider path for indexable xml resources.
+ */
+ public static final String INDEXABLES_XML_RES_PATH = SETTINGS + "/" + INDEXABLES_XML_RES;
+
+ /**
+ * Indexable raw data name.
+ */
+ public static final String INDEXABLES_RAW = "indexables_raw";
+
+ /**
+ * ContentProvider path for indexable raw data.
+ */
+ public static final String INDEXABLES_RAW_PATH = SETTINGS + "/" + INDEXABLES_RAW;
+
+ /**
+ * Indexable xml resources colums.
+ */
+ public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] {
+ XmlResource.COLUMN_RANK,
+ XmlResource.COLUMN_XML_RESID,
+ XmlResource.COLUMN_CLASS_NAME,
+ XmlResource.COLUMN_ICON_RESID,
+ XmlResource.COLUMN_INTENT_ACTION,
+ XmlResource.COLUMN_INTENT_TARGET_PACKAGE,
+ XmlResource.COLUMN_INTENT_TARGET_CLASS
+ };
+
+ /**
+ * Indexable raw data colums.
+ */
+ public static final String[] INDEXABLES_RAW_COLUMNS = new String[] {
+ RawData.COLUMN_RANK,
+ RawData.COLUMN_TITLE,
+ RawData.COLUMN_SUMMARY,
+ RawData.COLUMN_KEYWORDS,
+ RawData.COLUMN_SCREEN_TITLE,
+ RawData.COLUMN_CLASS_NAME,
+ RawData.COLUMN_ICON_RESID,
+ RawData.COLUMN_INTENT_ACTION,
+ RawData.COLUMN_INTENT_TARGET_PACKAGE,
+ RawData.COLUMN_INTENT_TARGET_CLASS,
+ };
+
+ /**
+ * Constants related to a {@link SearchIndexableResource}.
+ *
+ * This is a description of
+ */
+ public static final class XmlResource extends BaseColumns {
+ private XmlResource() {
+ }
+
+ public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+ "/" + INDEXABLES_XML_RES;
+
+ /**
+ * XML resource ID for the {@link android.preference.PreferenceScreen} to load and index.
+ */
+ public static final String COLUMN_XML_RESID = "xmlResId";
+ }
+
+ /**
+ * Constants related to a {@link SearchIndexableData}.
+ *
+ * This is the raw data that is stored into an Index. This is related to
+ * {@link android.preference.Preference} and its attributes like
+ * {@link android.preference.Preference#getTitle()},
+ * {@link android.preference.Preference#getSummary()}, etc.
+ *
+ */
+ public static final class RawData extends BaseColumns {
+ private RawData() {
+ }
+
+ public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+ "/" + INDEXABLES_RAW;
+
+ /**
+ * Title's raw data.
+ */
+ public static final String COLUMN_TITLE = "title";
+
+ /**
+ * Summary's raw data.
+ */
+ public static final String COLUMN_SUMMARY = "summary";
+
+ /**
+ * Keywords' raw data.
+ */
+ public static final String COLUMN_KEYWORDS = "keywords";
+
+ /**
+ * Fragment's title associated with the raw data.
+ */
+ public static final String COLUMN_SCREEN_TITLE = "screenTitle";
+ }
+
+ /**
+ * The base columns.
+ */
+ private static class BaseColumns {
+ private BaseColumns() {
+ }
+
+ /**
+ * Rank of the data. This is an integer used for ranking the search results. This is
+ * application specific.
+ */
+ public static final String COLUMN_RANK = "rank";
+
+ /**
+ * Class name associated with the data (usually a Fragment class name).
+ */
+ public static final String COLUMN_CLASS_NAME = "className";
+
+ /**
+ * Icon resource ID for the data.
+ */
+ public static final String COLUMN_ICON_RESID = "iconResId";
+
+ /**
+ * Intent action associated with the data.
+ */
+ public static final String COLUMN_INTENT_ACTION = "intentAction";
+
+ /**
+ * Intent target package associated with the data.
+ */
+ public static final String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";
+
+ /**
+ * Intent target class associated with the data.
+ */
+ public static final String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
+ }
+}
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
new file mode 100644
index 0000000..2e358e4
--- /dev/null
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -0,0 +1,174 @@
+/*
+ * 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.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Base class for a search indexable provider. Such provider offers data to be indexed either
+ * as a reference to an XML file (like a {@link android.preference.PreferenceScreen}) or either
+ * as some raw data.
+ *
+ * @see SearchIndexableResource
+ * @see SearchIndexableData
+ * @see SearchIndexablesContract
+ *
+ * To create a search indexables provider, extend this class, then implement the abstract methods,
+ * and add it to your manifest like this:
+ *
+ * <pre class="prettyprint"><manifest>
+ * ...
+ * <application>
+ * ...
+ * <provider
+ * android:name="com.example.MyIndexablesProvider"
+ * android:authorities="com.example.myindexablesprovider"
+ * android:exported="true"
+ * android:grantUriPermissions="true"
+ * android:permission="android.permission.READ_SEARCH_INDEXABLES"
+ * <intent-filter>
+ * <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
+ * </intent-filter>
+ * </provider>
+ * ...
+ * </application>
+ *</manifest></pre>
+ * <p>
+ * When defining your provider, you must protect it with
+ * {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}, which is a permission only the system
+ * can obtain.
+ * </p>
+ *
+ * @hide
+ */
+public abstract class SearchIndexablesProvider extends ContentProvider {
+ private static final String TAG = "IndexablesProvider";
+
+ private String mAuthority;
+ private UriMatcher mMatcher;
+
+ private static final int MATCH_RES_CODE = 1;
+ private static final int MATCH_RAW_CODE = 2;
+
+ /**
+ * Implementation is provided by the parent class.
+ */
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ mAuthority = info.authority;
+
+ mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH,
+ MATCH_RES_CODE);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH,
+ MATCH_RAW_CODE);
+
+ // Sanity check our setup
+ if (!info.exported) {
+ throw new SecurityException("Provider must be exported");
+ }
+ if (!info.grantUriPermissions) {
+ throw new SecurityException("Provider must grantUriPermissions");
+ }
+ if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) {
+ throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES");
+ }
+
+ super.attachInfo(context, info);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ switch (mMatcher.match(uri)) {
+ case MATCH_RES_CODE:
+ return queryXmlResources(null);
+ case MATCH_RAW_CODE:
+ return queryRawData(null);
+ default:
+ throw new UnsupportedOperationException("Unknown Uri " + uri);
+ }
+ }
+
+ /**
+ * Returns all {@link android.provider.SearchIndexablesContract.XmlResource}.
+ *
+ * Those are usually xml resource ID to some {@link android.preference.PreferenceScreen}.
+ *
+ * @param projection list of {@link android.provider.SearchIndexablesContract.XmlResource}
+ * columns to put into the cursor. If {@code null} all supported columns
+ * should be included.
+ */
+ public abstract Cursor queryXmlResources(String[] projection);
+
+ /**
+ * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
+ *
+ * Those are raw indexable data.
+ *
+ * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
+ * to put into the cursor. If {@code null} all supported columns should be
+ * included.
+ */
+ public abstract Cursor queryRawData(String[] projection);
+
+ @Override
+ public String getType(Uri uri) {
+ switch (mMatcher.match(uri)) {
+ case MATCH_RES_CODE:
+ return SearchIndexablesContract.XmlResource.MIME_TYPE;
+ case MATCH_RAW_CODE:
+ return SearchIndexablesContract.RawData.MIME_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
+ @Override
+ public final Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("Insert not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
+ @Override
+ public final int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Delete not supported");
+ }
+
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
+ @Override
+ public final int update(
+ Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update not supported");
+ }
+}
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 @@
}
@Override
- @SuppressWarnings("unchecked")
public LongArray clone() {
LongArray clone = null;
try {
@@ -154,7 +153,8 @@
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 @@
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 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 @@
*/
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
}
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 @@
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 @@
}
}
}
+ 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 @@
info = child.createAccessibilityNodeInfo();
} else {
info = provider.createAccessibilityNodeInfo(
- AccessibilityNodeInfo.UNDEFINED);
+ AccessibilityNodeProvider.HOST_VIEW_ID);
}
if (info != null) {
outInfos.add(info);
@@ -814,7 +910,7 @@
}
} 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 @@
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 @@
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.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 @@
void thawRotation();
/**
- * Gets whether the rotation is frozen.
+ * Gets whether the rotation is frozen.
*
* @return Whether the rotation is frozen.
*/
@@ -231,55 +230,7 @@
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 ecd73af..a8ccd49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5101,10 +5101,6 @@
* @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 {
@@ -6901,7 +6897,7 @@
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
*/
public void setFilterTouchesWhenObscured(boolean enabled) {
- setFlags(enabled ? 0 : FILTER_TOUCHES_WHEN_OBSCURED,
+ setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED : 0,
FILTER_TOUCHES_WHEN_OBSCURED);
}
@@ -9386,6 +9382,8 @@
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 @@
childHasTransientStateChanged(child, true);
}
- if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
+ if (child.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -3954,7 +3954,7 @@
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 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 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 @@
* @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 @@
* @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 @@
* 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 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 @@
* @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 @@
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 @@
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 @@
}
/**
+ * 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 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 @@
}
}
} 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 @@
}
}
} 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 @@
}
}
} 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 @@
}
}
} 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 @@
}
}
} 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 @@
}
}
} 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 @@
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 @@
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 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 @@
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 @@
* @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 @@
* @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 @@
* 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 @@
// 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 @@
* 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 @@
* @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 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 boolean addAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mAccessibilityStateChangeListeners.add(listener);
}
@@ -424,6 +440,7 @@
*/
public boolean removeAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mAccessibilityStateChangeListeners.remove(listener);
}
@@ -436,6 +453,7 @@
*/
public boolean addTouchExplorationStateChangeListener(
TouchExplorationStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mTouchExplorationStateChangeListeners.add(listener);
}
@@ -447,6 +465,7 @@
*/
public boolean removeTouchExplorationStateChangeListener(
TouchExplorationStateChangeListener listener) {
+ // Final CopyOnArrayList - no lock needed.
return mTouchExplorationStateChangeListeners.remove(listener);
}
@@ -455,50 +474,24 @@
*
* @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 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 @@
* @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 @@
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 @@
* @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 @@
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 @@
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 @@
* @param source The info source.
*/
public void setSource(View source) {
- setSource(source, UNDEFINED);
+ setSource(source, UNDEFINED_ITEM_ID);
}
/**
@@ -591,9 +604,9 @@
*/
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 @@
* @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 @@
* @hide
*/
public void addChildUnchecked(View child) {
- addChildInternal(child, UNDEFINED, false);
+ addChildInternal(child, UNDEFINED_ITEM_ID, false);
}
/**
@@ -794,7 +807,7 @@
* @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 @@
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 @@
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 @@
}
/**
+ * 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 @@
}
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 @@
* @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 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 @@
* @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 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 @@
}
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 @@
* @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 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 @@
}
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 @@
if (mChildNodeIds == null) {
mChildNodeIds = otherChildNodeIds.clone();
} else {
+ mChildNodeIds.clear();
mChildNodeIds.addAll(otherChildNodeIds);
}
}
@@ -2474,8 +2507,8 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
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 @@
*/
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/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 16c41f3..cd75010 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -36,4 +36,6 @@
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
void setMode(int code, int uid, String packageName, int mode);
void resetAllModes();
+ int checkAudioOperation(int code, int stream, int uid, String packageName);
+ void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 26d7f5f..082f1a5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -208,7 +208,8 @@
int mNumHistoryTagChars = 0;
int mHistoryBufferLastPos = -1;
boolean mHistoryOverflow = false;
- long mLastHistoryTime = 0;
+ long mLastHistoryElapsedRealtime = 0;
+ long mLastHistoryUptime = 0;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -1957,12 +1958,12 @@
}
}
- void addHistoryBufferLocked(long curTime) {
+ void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
- final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
+ final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
@@ -1981,14 +1982,14 @@
&& mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
&& mHistoryLastWritten.batteryVoltage == mHistoryCur.batteryVoltage) {
// We can merge this new change in with the last one. Merging is
- // allows as long as only the states have changed, and within those states
+ // allowed as long as only the states have changed, and within those states
// as long as no bit has changed both between now and the last entry, as
// well as the last entry and the one before it (so we capture any toggles).
if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
mHistoryBufferLastPos = -1;
- curTime = mHistoryLastWritten.time - mHistoryBaseTime;
+ elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTime;
// If the last written history had a wakelock tag, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
@@ -2018,8 +2019,8 @@
if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
- addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW);
return;
}
@@ -2034,22 +2035,23 @@
return;
}
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
return;
}
- addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
}
- private void addHistoryBufferLocked(long curTime, byte cmd) {
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
if (mIteratingHistory) {
throw new IllegalStateException("Can't do this while iterating history!");
}
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
mHistoryLastLastWritten.setTo(mHistoryLastWritten);
- mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+ mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
- mLastHistoryTime = curTime;
+ mLastHistoryElapsedRealtime = elapsedRealtimeMs;
+ mLastHistoryUptime = uptimeMs;
mHistoryCur.wakelockTag = null;
mHistoryCur.wakeReasonTag = null;
mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
@@ -2061,8 +2063,8 @@
int mChangedStates = 0;
- void addHistoryRecordLocked(long curTime) {
- addHistoryBufferLocked(curTime);
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
if (!USE_OLD_HISTORY) {
return;
@@ -2077,12 +2079,12 @@
// are now resetting back to their original value, then just collapse
// into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+2000)
+ && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
&& ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
+ && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
&& mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
mHistoryLastEnd.next = null;
mHistoryEnd.next = mHistoryCache;
@@ -2100,7 +2102,7 @@
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -2117,25 +2119,26 @@
}
}
- addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
+ addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
}
- void addHistoryEventLocked(long curTime, int code, String name, int uid) {
+ void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+ String name, int uid) {
mHistoryCur.eventCode = code;
mHistoryCur.eventTag = mHistoryCur.localEventTag;
mHistoryCur.eventTag.string = name;
mHistoryCur.eventTag.uid = uid;
- addHistoryBufferLocked(curTime);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
}
- void addHistoryRecordLocked(long curTime, byte cmd) {
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
HistoryItem rec = mHistoryCache;
if (rec != null) {
mHistoryCache = rec.next;
} else {
rec = new HistoryItem();
}
- rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+ rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
addHistoryRecordLocked(rec);
}
@@ -2164,7 +2167,8 @@
}
mHistoryBaseTime = 0;
- mLastHistoryTime = 0;
+ mLastHistoryElapsedRealtime = 0;
+ mLastHistoryUptime = 0;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -2262,7 +2266,9 @@
active.remove(name);
}
}
- addHistoryEventLocked(SystemClock.elapsedRealtime(), code, name, uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
}
private void requestWakelockCpuUpdate() {
@@ -2273,7 +2279,7 @@
}
public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
- boolean unimportantForLogging, long elapsedRealtime) {
+ boolean unimportantForLogging, long elapsedRealtime, long uptime) {
uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
@@ -2286,7 +2292,7 @@
mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
mHistoryCur.wakelockTag.uid = uid;
mWakeLockImportant = !unimportantForLogging;
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
} else if (!mWakeLockImportant && !unimportantForLogging) {
if (mHistoryLastWritten.wakelockTag != null) {
// We'll try to update the last tag.
@@ -2294,19 +2300,23 @@
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
mHistoryCur.wakelockTag.uid = uid;
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWakeLockImportant = true;
}
mWakeLockNesting++;
}
if (uid >= 0) {
+ //if (uid == 0) {
+ // Slog.wtf(TAG, "Acquiring wake lock from root: " + name);
+ //}
requestWakelockCpuUpdate();
getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
}
}
- public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime) {
+ public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime,
+ long uptime) {
uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
@@ -2314,7 +2324,7 @@
mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
if (uid >= 0) {
@@ -2326,10 +2336,11 @@
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
final int N = ws.size();
for (int i=0; i<N; i++) {
noteStartWakeLocked(ws.get(i), pid, name, historyName, type, unimportantForLogging,
- elapsedRealtime);
+ elapsedRealtime, uptime);
}
}
@@ -2337,36 +2348,39 @@
WorkSource newWs, int newPid, String newName,
String newHistoryName, int newType, boolean newUnimportantForLogging) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
// For correct semantics, we start the need worksources first, so that we won't
// make inappropriate history items as if all wake locks went away and new ones
// appeared. This is okay because tracking of wake locks allows nesting.
- final int NN = ws.size();
+ final int NN = newWs.size();
for (int i=0; i<NN; i++) {
noteStartWakeLocked(newWs.get(i), newPid, newName, newHistoryName, newType,
- newUnimportantForLogging, elapsedRealtime);
+ newUnimportantForLogging, elapsedRealtime, uptime);
}
final int NO = ws.size();
for (int i=0; i<NO; i++) {
- noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
+ noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime);
}
}
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
final int N = ws.size();
for (int i=0; i<N; i++) {
- noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
+ noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime);
}
}
public void noteWakeupReasonLocked(int irq, String reason) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
mHistoryCur.wakeReasonTag.string = reason;
mHistoryCur.wakeReasonTag.uid = irq;
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
public int startAddingCpuLocked() {
@@ -2511,11 +2525,12 @@
public void noteStartSensorLocked(int uid, int sensor) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mSensorNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mSensorNesting++;
getUidStatsLocked(uid).noteStartSensor(sensor, elapsedRealtime);
@@ -2524,12 +2539,13 @@
public void noteStopSensorLocked(int uid, int sensor) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mSensorNesting--;
if (mSensorNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteStopSensor(sensor, elapsedRealtime);
}
@@ -2539,11 +2555,12 @@
public void noteStartGpsLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mGpsNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mGpsNesting++;
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -2552,12 +2569,13 @@
public void noteStopGpsLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mGpsNesting--;
if (mGpsNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
}
@@ -2565,10 +2583,11 @@
public void noteScreenOnLocked() {
if (!mScreenOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOn = true;
mScreenOnTimer.startRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
@@ -2580,7 +2599,8 @@
// Fake a wake lock, so we consider the device waked as long
// as the screen is on.
- noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false, elapsedRealtime);
+ noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+ elapsedRealtime, uptime);
// Update discharge amounts.
if (mOnBatteryInternal) {
@@ -2592,17 +2612,19 @@
public void noteScreenOffLocked() {
if (mScreenOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOn = false;
mScreenOnTimer.stopRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
- noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL, elapsedRealtime);
+ noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL,
+ elapsedRealtime, uptime);
updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
SystemClock.uptimeMillis() * 1000, elapsedRealtime * 1000);
@@ -2621,11 +2643,12 @@
else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
if (mScreenBrightnessBin != bin) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
| (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
if (mScreenOn) {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
@@ -2650,12 +2673,13 @@
public void noteDataConnectionActive(int type, boolean active) {
if (ConnectivityManager.isNetworkTypeMobile(type)) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mMobileRadioActive != active) {
if (active) mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
else mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mMobileRadioActive = active;
if (active) {
mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
@@ -2672,10 +2696,11 @@
public void notePhoneOnLocked() {
if (!mPhoneOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2684,10 +2709,11 @@
public void notePhoneOffLocked() {
if (mPhoneOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2727,6 +2753,7 @@
mPhoneSignalStrengthBinRaw = strengthBin;
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// In this case we will always be STATE_OUT_OF_SERVICE, so need
@@ -2800,7 +2827,7 @@
}
if (newHistory) {
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
@@ -2875,11 +2902,12 @@
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
| (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
elapsedRealtime);
@@ -2892,10 +2920,11 @@
public void noteWifiOnLocked() {
if (!mWifiOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2903,11 +2932,12 @@
public void noteWifiOffLocked() {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiOn) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2916,11 +2946,12 @@
public void noteAudioOnLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (!mAudioOn) {
mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mAudioOn = true;
mAudioOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2930,11 +2961,12 @@
public void noteAudioOffLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mAudioOn) {
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mAudioOn = false;
mAudioOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2944,11 +2976,12 @@
public void noteVideoOnLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (!mVideoOn) {
mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mVideoOn = true;
mVideoOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -2958,11 +2991,12 @@
public void noteVideoOffLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mVideoOn) {
mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mVideoOn = false;
mVideoOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -2992,10 +3026,11 @@
public void noteWifiRunningLocked(WorkSource ws) {
if (!mGlobalWifiRunning) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mGlobalWifiRunning = true;
mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtime);
int N = ws.size();
@@ -3029,10 +3064,11 @@
public void noteWifiStoppedLocked(WorkSource ws) {
if (mGlobalWifiRunning) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtime);
int N = ws.size();
@@ -3060,10 +3096,11 @@
public void noteBluetoothOnLocked() {
if (!mBluetoothOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = true;
mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
}
@@ -3072,10 +3109,11 @@
public void noteBluetoothOffLocked() {
if (mBluetoothOn) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
mBluetoothOn = false;
mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
}
@@ -3098,11 +3136,12 @@
public void noteFullWifiLockAcquiredLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiFullLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiFullLockNesting++;
getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3111,12 +3150,13 @@
public void noteFullWifiLockReleasedLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
}
@@ -3126,11 +3166,12 @@
public void noteWifiScanStartedLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiScanNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiScanNesting++;
getUidStatsLocked(uid).noteWifiScanStartedLocked(elapsedRealtime);
@@ -3139,12 +3180,13 @@
public void noteWifiScanStoppedLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteWifiScanStoppedLocked(elapsedRealtime);
}
@@ -3166,11 +3208,12 @@
public void noteWifiMulticastEnabledLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (mWifiMulticastNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWifiMulticastNesting++;
getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -3179,12 +3222,13 @@
public void noteWifiMulticastDisabledLocked(int uid) {
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
mWifiMulticastNesting--;
if (mWifiMulticastNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime);
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
}
@@ -5627,12 +5671,13 @@
public void resetAllStatsCmdLocked() {
resetAllStatsLocked();
- long uptime = SystemClock.uptimeMillis() * 1000;
+ final long mSecUptime = SystemClock.uptimeMillis();
+ long uptime = mSecUptime * 1000;
long mSecRealtime = SystemClock.elapsedRealtime();
long realtime = mSecRealtime * 1000;
mDischargeStartLevel = mHistoryCur.batteryLevel;
pullPendingStateUpdatesLocked();
- addHistoryRecordLocked(mSecRealtime);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = mHistoryCur.batteryLevel;
mOnBatteryTimeBase.reset(uptime, realtime);
mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
@@ -5647,7 +5692,7 @@
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
}
- initActiveHistoryEventsLocked(mSecRealtime);
+ initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
private void resetAllStatsLocked() {
@@ -5705,7 +5750,7 @@
clearHistoryLocked();
}
- private void initActiveHistoryEventsLocked(long nowRealtime) {
+ private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
if (active == null) {
@@ -5715,7 +5760,8 @@
SparseBooleanArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
if (uids.valueAt(j)) {
- addHistoryEventLocked(nowRealtime, i, ent.getKey(), uids.keyAt(j));
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+ uids.keyAt(j));
}
}
}
@@ -5753,16 +5799,16 @@
}
}
- void setOnBatteryLocked(boolean onBattery, int oldStatus, int level) {
+ void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
+ final int oldStatus, final int level) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
mHandler.sendMessage(m);
mOnBattery = mOnBatteryInternal = onBattery;
- long uptime = SystemClock.uptimeMillis() * 1000;
- long mSecRealtime = SystemClock.elapsedRealtime();
- long realtime = mSecRealtime * 1000;
+ final long uptime = mSecUptime * 1000;
+ final long realtime = mSecRealtime * 1000;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
@@ -5783,9 +5829,9 @@
if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(mSecRealtime, HistoryItem.CMD_CURRENT_TIME);
+ addHistoryBufferLocked(mSecRealtime, mSecUptime, HistoryItem.CMD_CURRENT_TIME);
mHistoryCur.currentTime = 0;
- addHistoryRecordLocked(mSecRealtime);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (mScreenOn) {
mDischargeScreenOnUnplugLevel = level;
@@ -5798,7 +5844,7 @@
mDischargeAmountScreenOff = 0;
updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
if (reset) {
- initActiveHistoryEventsLocked(mSecRealtime);
+ initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
} else {
pullPendingStateUpdatesLocked();
@@ -5806,7 +5852,7 @@
mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(mSecRealtime);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = level;
if (level < mDischargeUnplugLevel) {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
@@ -5828,7 +5874,9 @@
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
synchronized(this) {
- boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final long uptime = SystemClock.uptimeMillis();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
int oldStatus = mHistoryCur.batteryStatus;
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
@@ -5856,7 +5904,7 @@
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
mHistoryCur.batteryVoltage = (char)volt;
- setOnBatteryLocked(onBattery, oldStatus, level);
+ setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
} else {
boolean changed = false;
if (mHistoryCur.batteryLevel != level) {
@@ -5886,7 +5934,7 @@
changed = true;
}
if (changed) {
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
@@ -6161,7 +6209,18 @@
return val;
}
}
-
+
+ @Override
+ public int getDischargeAmount(int which) {
+ int dischargeAmount = which == STATS_SINCE_CHARGED
+ ? getHighDischargeAmountSinceCharge()
+ : (getDischargeStartLevel() - getDischargeCurrentLevel());
+ if (dischargeAmount < 0) {
+ dischargeAmount = 0;
+ }
+ return dischargeAmount;
+ }
+
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
@@ -6438,13 +6497,14 @@
}
if (mHistoryBuffer.dataPosition() > 0) {
- long now = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
if (USE_OLD_HISTORY) {
- addHistoryRecordLocked(now, HistoryItem.CMD_START);
+ addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
}
- addHistoryBufferLocked(now, HistoryItem.CMD_START);
+ addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(now, HistoryItem.CMD_CURRENT_TIME);
+ addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_CURRENT_TIME);
mHistoryCur.currentTime = 0;
}
}
@@ -6540,11 +6600,11 @@
StringBuilder sb = new StringBuilder(128);
sb.append("****************** WRITING mHistoryBaseTime: ");
TimeUtils.formatDuration(mHistoryBaseTime, sb);
- sb.append(" mLastHistoryTime: ");
- TimeUtils.formatDuration(mLastHistoryTime, sb);
+ sb.append(" mLastHistoryElapsedRealtime: ");
+ TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
Slog.i(TAG, sb.toString());
}
- out.writeLong(mHistoryBaseTime + mLastHistoryTime);
+ out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
out.writeInt(mHistoryTagPool.size());
for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
HistoryTag tag = ent.getKey();
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
index 246e3bd..201ffe8 100644
--- a/core/jni/android_content_res_Configuration.cpp
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -70,15 +70,6 @@
gConfigurationClassInfo.smallestScreenWidthDp);
}
-/*
- * JNI registration.
- */
-static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
- // (void*) android_content_res_ObbScanner_getObbInfo },
-};
-
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className);
@@ -123,8 +114,7 @@
GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp, clazz,
"smallestScreenWidthDp", "I");
- return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
- NELEM(gMethods));
+ return 0;
}
}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index aa6a035..e72aff9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -42,13 +42,13 @@
#include <SkTemplates.h>
#include <SkXfermode.h>
-#include <DisplayList.h>
#include <DisplayListRenderer.h>
#include <LayerRenderer.h>
#include <OpenGLRenderer.h>
#include <SkiaShader.h>
#include <Stencil.h>
#include <Rect.h>
+#include <RenderNode.h>
#include <TextLayout.h>
#include <TextLayoutCache.h>
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
index 228a92e..00d92fc 100644
--- a/core/jni/android_view_GLRenderer.cpp
+++ b/core/jni/android_view_GLRenderer.cpp
@@ -27,9 +27,9 @@
#include <utils/Timers.h>
#include <Caches.h>
-#include <DisplayList.h>
#include <Extensions.h>
#include <LayerRenderer.h>
+#include <RenderNode.h>
#ifdef USE_OPENGL_RENDERER
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index ad2e9ff..4bf5f78 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -32,10 +32,10 @@
#include <SkXfermode.h>
#include <DeferredLayerUpdater.h>
-#include <DisplayList.h>
#include <LayerRenderer.h>
#include <SkiaShader.h>
#include <Rect.h>
+#include <RenderNode.h>
namespace android {
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 823f8d7..fa08a78 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -23,8 +23,8 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
-#include <DisplayList.h>
#include <DisplayListRenderer.h>
+#include <RenderNode.h>
namespace android {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2159fb..37ef539b 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).
@@ -2400,6 +2388,12 @@
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature" />
+ <!-- Internal permission to allows an application to read indexable data.
+ @hide -->
+ <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature|system" />
+
<!-- Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
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/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index 54ef20c..7c957b8 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -263,256 +263,153 @@
<p>The following table describes the support level of various operations across API levels:</p>
<style type="text/css">
- .tblGenFixed, .tblGeneric{font-size:15px}.tblGenFixed td {padding:0 3px;letter-spacing:0;word-spacing:0;background-color:#fff;z-index:1;border-top:0px none;border-left:0px none;border-bottom:1px solid #CCC;border-right:1px solid #CCC;} .dn {display:none} .tblGenFixed td.s0 {background-color:white;border-top:1px solid #CCC;border-left:1px solid #CCC;} .tblGenFixed td.s1 {background-color:#434343;color:#ffffff;text-align:center;border-top:1px solid #CCC;} .tblGenFixed td.s2 {background-color:#d9d9d9;color:#000000;text-align:center;} .tblGenFixed td.s3 {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.s5 {background-color:#434343;color:#ffffff;text-align:left;border-left:1px solid #CCC;} .tblGenFixed td.s10 {background-color:white;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.g_pos {background-color:#d9d9d9;color:#6aa84f;text-align:center;} .tblGenFixed td.g_neg {background-color:#d9d9d9;color:#980000;text-align:center;} .tblGenFixed td.w_pos {background-color:white;color:#6aa84f;text-align:center;} .tblGenFixed td.w_neg {background-color:white;color:#980000;text-align:center;}
+ .tblGenFixed, .tblGeneric{font-size:15px}.tblGenFixed td {padding:0 3px;letter-spacing:0;word-spacing:0;background-color:#fff;z-index:1;border-top:0px none;border-left:0px none;border-bottom:1px solid #CCC;border-right:1px solid #CCC;} .dn {display:none} .tblGenFixed td.s0 {background-color:white;border-top:1px solid #CCC;border-left:1px solid #CCC;} .tblGenFixed td.s1 {background-color:#434343;color:#ffffff;text-align:center;border-top:1px solid #CCC;} .tblGenFixed td.s3 {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.s5 {background-color:#434343;color:#ffffff;text-align:left;border-left:1px solid #CCC;} .tblGenFixed td.label_pos {background-color:white;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.label_neg {background-color:#ececec;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.value_pos {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.value_neg {background-color:#ececec;color:#980000;text-align:center;}
</style>
<table border="0" cellpadding="0" cellspacing="0" class="tblGenFixed" id="tblMain">
<tbody>
<tr class="rShim">
<td class="rShim" style="width:380px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
+ <td class="rShim" style="width:240px;"></td>
</tr>
<tr>
- <td rowspan="2" class="s0"></td>
- <td colspan="4" class="s1">API level</td>
- </tr>
- <tr>
- <td style="display:none;"></td>
- <td class="s2">< 16</td>
- <td class="s3">16</td>
- <td class="s2">17</td>
- <td class="s3">18</td>
+ <td class="s0"></td>
+ <td class="s1">First supported API level</td>
</tr>
<tr>
<td colspan="5" class="s5">Canvas</td>
</tr>
<tr>
- <td class="s10">drawBitmapMesh() (colors array)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">drawBitmapMesh() (colors array)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">drawPicture()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">drawPicture()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">drawPosText()</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
- <td class="g_pos">✓</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">drawPosText()</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">drawTextOnPath()</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
- <td class="g_pos">✓</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">drawTextOnPath()</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">drawVertices()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">drawVertices()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">setDrawFilter()</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
- <td class="g_pos">✓</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">setDrawFilter()</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">clipPath()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">clipPath()</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRegion()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">clipRegion()</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect(Region.Op.XOR)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">clipRect(Region.Op.XOR)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect(Region.Op.Difference)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">clipRect(Region.Op.Difference)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect(Region.Op.ReverseDifference)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">clipRect(Region.Op.ReverseDifference)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">clipRect() with rotation/perspective</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">clipRect() with rotation/perspective</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
<td colspan="5" class="s5">Paint</td>
</tr>
<tr>
- <td class="s10">setAntiAlias() (for text)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">setAntiAlias() (for text)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">setAntiAlias() (for lines)</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
- <td class="g_pos">✓</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">setAntiAlias() (for lines)</td>
+ <td class="value_pos">16</td>
</tr>
<tr>
- <td class="s10">setFilterBitmap()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_pos">✓</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">setFilterBitmap()</td>
+ <td class="value_pos">17</td>
</tr>
<tr>
- <td class="s10">setLinearText()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">setLinearText()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">setMaskFilter()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">setMaskFilter()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">setPathEffect() (for lines)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">setPathEffect() (for lines)</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">setRasterizer()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">setRasterizer()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">setShadowLayer() (other than text)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">setShadowLayer() (other than text)</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">setStrokeCap() (for lines)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">setStrokeCap() (for lines)</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td class="s10">setStrokeCap() (for points)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_pos">setStrokeCap() (for points)</td>
+ <td class="value_pos">19</td>
</tr>
<tr>
- <td class="s10">setSubpixelText()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">setSubpixelText()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
<td colspan="5" class="s5">Xfermode</td>
</tr>
<tr>
- <td class="s10">AvoidXfermode</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">AvoidXfermode</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">PixelXorXfermode</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">PixelXorXfermode</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">PorterDuff.Mode.DARKEN (framebuffer)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">PorterDuff.Mode.DARKEN (framebuffer)</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">PorterDuff.Mode.LIGHTEN (framebuffer)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">PorterDuff.Mode.LIGHTEN (framebuffer)</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">PorterDuff.Mode.OVERLAY (framebuffer)</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">PorterDuff.Mode.OVERLAY (framebuffer)</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
<td colspan="5" class="s5">Shader</td>
</tr>
<tr>
- <td class="s10">ComposeShader inside ComposeShader</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">ComposeShader inside ComposeShader</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">Same type shaders inside ComposeShader</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
+ <td class="label_neg">Same type shaders inside ComposeShader</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">Local matrix on ComposeShader</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
+ <td class="label_pos">Local matrix on ComposeShader</td>
+ <td class="value_pos">18</td>
</tr>
</tbody>
</table>
@@ -530,64 +427,39 @@
<tbody>
<tr class="rShim">
<td class="rShim" style="width:380px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
- <td class="rShim" style="width:120px;"></td>
+ <td class="rShim" style="width:240px;"></td>
</tr>
<tr>
- <td rowspan="2" class="s0"></td>
- <td colspan="4" class="s1">API level</td>
+ <td class="s5">Drawing operation to be scaled</td>
+ <td class="s1">First supported API level</td>
</tr>
<tr>
- <td style="display:none;"></td>
- <td class="s2">< 17</td>
- <td class="s3">17</td>
- <td class="s2">18</td>
+ <td class="label_pos">drawText()</td>
+ <td class="value_pos">18</td>
</tr>
<tr>
- <td colspan="5" class="s5">Support for large scale factors</td>
+ <td class="label_neg">drawPosText()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">drawText()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_pos">✓</td>
+ <td class="label_neg">drawTextOnPath()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">drawPosText()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
+ <td class="label_pos">Simple Shapes*</td>
+ <td class="value_pos">17</td>
</tr>
<tr>
- <td class="s10">drawTextOnPath()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
+ <td class="label_neg">Complex Shapes*</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">Simple Shapes*</td>
- <td class="g_neg">✗</td>
- <td class="w_pos">✓</td>
- <td class="g_pos">✓</td>
+ <td class="label_neg">drawPath()</td>
+ <td class="value_neg">✗</td>
</tr>
<tr>
- <td class="s10">Complex Shapes*</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- </tr>
- <tr>
- <td class="s10">drawPath()</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
- </tr>
- <tr>
- <td class="s10">Shadow layer</td>
- <td class="g_neg">✗</td>
- <td class="w_neg">✗</td>
- <td class="g_neg">✗</td>
+ <td class="label_neg">Shadow layer</td>
+ <td class="value_neg">✗</td>
</tr>
</tbody>
</table>
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index ce6cc38..589211f 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -72,7 +72,7 @@
}
cur = cur->mNext;
}
-
+
return res;
}
@@ -84,18 +84,18 @@
mNext = mPrev = NULL;
if (gTail == NULL) {
gHead = gTail = this;
- } else {
- mPrev = gTail;
- gTail->mNext = this;
- gTail = this;
- }
+ } else {
+ mPrev = gTail;
+ gTail->mNext = this;
+ gTail = this;
+ }
//ALOGI("Creating Asset %p #%d\n", this, gCount);
}
Asset::~Asset(void)
{
AutoMutex _l(gAssetLock);
- gCount--;
+ gCount--;
if (gHead == this) {
gHead = mNext;
}
@@ -409,7 +409,7 @@
}
mFileName = fileName != NULL ? strdup(fileName) : NULL;
-
+
return NO_ERROR;
}
@@ -538,7 +538,7 @@
free(mFileName);
mFileName = NULL;
}
-
+
if (mFp != NULL) {
// can only be NULL when called from destructor
// (otherwise we would never return this object)
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index 1a5c55c..a5b9416 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -291,7 +291,7 @@
(int)(m_pos - sizeof(m_header)), (int)m_header.type);
m_status = EINVAL;
}
-
+
return m_status;
}
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 302fbf6..ab837ad 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -568,8 +568,8 @@
// [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time
// [ 116 : 8 ] gid -- ignored in Android format
- snprintf(buf + 108, 8, "0%lo", s.st_uid);
- snprintf(buf + 116, 8, "0%lo", s.st_gid);
+ snprintf(buf + 108, 8, "0%lo", (unsigned long)s.st_uid);
+ snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
// [ 124 : 12 ] file size in bytes
if (s.st_size > 077777777777LL) {
@@ -778,7 +778,7 @@
ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
return errno;
}
-
+
while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
err = write(fd, buf, amt);
if (err != amt) {
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 0f54edb..2b74a33 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2006-2007 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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
+ * 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.
*/
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 98849e3..652cd4a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1266,7 +1266,7 @@
const ResXMLTree_node* next = (const ResXMLTree_node*)
(((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
//ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next);
-
+
if (((const uint8_t*)next) >= mTree.mDataEnd) {
mCurNode = NULL;
return (mEventCode=END_DOCUMENT);
@@ -1303,7 +1303,7 @@
(int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)));
continue;
}
-
+
if ((totalSize-headerSize) < minExtSize) {
ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
(int)dtohs(next->header.type),
@@ -1311,10 +1311,10 @@
(int)(totalSize-headerSize), (int)minExtSize);
return (mEventCode=BAD_DOCUMENT);
}
-
+
//printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n",
// mCurNode, mCurExt, headerSize, minExtSize);
-
+
return eventCode;
} while (true);
}
@@ -2717,7 +2717,7 @@
delete types[i];
}
}
-
+
ResTable* const owner;
const Header* const header;
const ResTable_package* const package;
@@ -2725,7 +2725,7 @@
ResStringPool typeStrings;
ResStringPool keyStrings;
-
+
const Type* getType(size_t idx) const {
return idx < types.size() ? types[idx] : NULL;
}
@@ -2775,18 +2775,18 @@
bags = NULL;
}
}
-
+
ResTable* const owner;
String16 const name;
uint32_t const id;
Vector<Package*> packages;
-
+
// This is for finding typeStrings and other common package stuff.
Package* basePackage;
// For quick access.
size_t typeCount;
-
+
// Computed attribute bags, first indexed by the type and second
// by the entry in that type.
bag_set*** bags;
@@ -2935,7 +2935,7 @@
//ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
//dumpToLog();
-
+
return NO_ERROR;
}
@@ -2944,7 +2944,7 @@
//ALOGI("Setting theme %p from theme %p...\n", this, &other);
//dumpToLog();
//other.dumpToLog();
-
+
if (&mTable == &other.mTable) {
for (size_t i=0; i<Res_MAXPACKAGE; i++) {
if (mPackages[i] != NULL) {
@@ -2974,7 +2974,7 @@
//ALOGI("Final theme:");
//dumpToLog();
-
+
return NO_ERROR;
}
@@ -2984,7 +2984,7 @@
int cnt = 20;
if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
-
+
do {
const ssize_t p = mTable.getResourcePackageIndex(resID);
const uint32_t t = Res_GETTYPE(resID);
@@ -3058,12 +3058,12 @@
for (size_t i=0; i<Res_MAXPACKAGE; i++) {
package_info* pi = mPackages[i];
if (pi == NULL) continue;
-
+
ALOGI(" Package #0x%02x:\n", (int)(i+1));
for (size_t j=0; j<pi->numTypes; j++) {
type_info& ti = pi->types[j];
if (ti.numEntries == 0) continue;
-
+
ALOGI(" Type #0x%02x:\n", (int)(j+1));
for (size_t k=0; k<ti.numEntries; k++) {
theme_entry& te = ti.entries[k];
@@ -3125,11 +3125,11 @@
status_t ResTable::add(ResTable* src)
{
mError = src->mError;
-
+
for (size_t i=0; i<src->mHeaders.size(); i++) {
mHeaders.add(src->mHeaders[i]);
}
-
+
for (size_t i=0; i<src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
@@ -3140,14 +3140,14 @@
pg->typeCount = srcPg->typeCount;
mPackageGroups.add(pg);
}
-
+
memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
-
+
return mError;
}
status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
- Asset* asset, bool copyData, const Asset* idmap)
+ Asset* /*asset*/, bool copyData, const Asset* idmap)
{
if (!data) return NO_ERROR;
Header* header = new Header(this);
@@ -3171,7 +3171,7 @@
LOAD_TABLE_NOISY(
ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
"idmap=%p\n", data, size, cookie, asset, copyData, idmap));
-
+
if (copyData || notDeviceEndian) {
header->ownedData = malloc(size);
if (header->ownedData == NULL) {
@@ -3503,7 +3503,7 @@
// are identical (diff == 0), or overlay packages will not take effect.
continue;
}
-
+
bestItem = thisConfig;
bestValue = item;
bestPackage = package;
@@ -3573,7 +3573,7 @@
const char16_t* ResTable::valueToString(
const Res_value* value, size_t stringBlock,
- char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen)
+ char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
{
if (!value) {
return NULL;
@@ -3596,7 +3596,7 @@
return err;
}
-void ResTable::unlockBag(const bag_entry* bag) const
+void ResTable::unlockBag(const bag_entry* /*bag*/) const
{
//printf("<<< unlockBag %p\n", this);
mLock.unlock();
@@ -3697,7 +3697,7 @@
bag_set* set = NULL;
TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
-
+
ResTable_config bestConfig;
memset(&bestConfig, 0, sizeof(bestConfig));
@@ -3763,7 +3763,7 @@
? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-
+
size_t N = count;
TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
@@ -3807,7 +3807,7 @@
} else {
set->typeSpecFlags = -1;
}
-
+
// Now merge in the new attributes...
ssize_t curOff = offset;
const ResTable_map* map;
@@ -4070,7 +4070,7 @@
TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
String8(group->name).string(), ti));
}
-
+
size_t NTC = typeConfigs->configs.size();
for (size_t tci=0; tci<NTC; tci++) {
const ResTable_type* const ty = typeConfigs->configs[tci];
@@ -4086,9 +4086,9 @@
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
-
+
offset += typeOffset;
-
+
if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
ALOGW("ResTable_entry at %d is beyond type chunk data %d",
offset, dtohl(ty->header.size));
@@ -4102,7 +4102,7 @@
String8(name, nameLen).string());
return 0;
}
-
+
const ResTable_entry* const entry = (const ResTable_entry*)
(((const uint8_t*)ty) + offset);
if (dtohs(entry->size) < sizeof(*entry)) {
@@ -4259,7 +4259,7 @@
if (*realEnd != 0) {
return false;
}
-
+
const unit_entry* cur = unitNames;
while (cur->name) {
if (len == cur->len && strncmp(cur->name, str, len) == 0) {
@@ -4410,7 +4410,7 @@
if (neg) {
mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
}
- outValue->data |=
+ outValue->data |=
(radix<<Res_value::COMPLEX_RADIX_SHIFT)
| (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
//printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
@@ -4523,7 +4523,7 @@
// Note: we don't check attrType here because the reference can
// be to any other type; we just need to count on the client making
// sure the referenced type is correct.
-
+
//printf("Looking up ref: %s\n", String8(s, len).string());
// It's a reference!
@@ -4610,7 +4610,7 @@
}
}
}
-
+
if (*s == '#') {
// It's a color! Convert to an integer of the form 0xaarrggbb.
uint32_t color = 0;
@@ -4710,7 +4710,7 @@
// String8(package).string(), String8(type).string(),
// String8(name).string());
uint32_t specFlags = 0;
- uint32_t rid =
+ uint32_t rid =
identifierForName(name.string(), name.size(),
type.string(), type.size(),
package.string(), package.size(), &specFlags);
@@ -4875,7 +4875,7 @@
return true;
}
}
-
+
}
bag++;
cnt--;
@@ -5240,43 +5240,43 @@
entryIndex, (int)allTypes->entryCount);
return BAD_TYPE;
}
-
+
const ResTable_type* type = NULL;
uint32_t offset = ResTable_type::NO_ENTRY;
ResTable_config bestConfig;
memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
-
+
const size_t NT = allTypes->configs.size();
for (size_t i=0; i<NT; i++) {
const ResTable_type* const thisType = allTypes->configs[i];
if (thisType == NULL) continue;
-
+
ResTable_config thisConfig;
thisConfig.copyFromDtoH(thisType->config);
TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
entryIndex, typeIndex+1, dtohl(thisType->config.size),
thisConfig.toString().string()));
-
+
// Check to make sure this one is valid for the current parameters.
if (config && !thisConfig.match(*config)) {
TABLE_GETENTRY(ALOGI("Does not match config!\n"));
continue;
}
-
+
// Check if there is the desired entry in this type.
-
+
const uint8_t* const end = ((const uint8_t*)thisType)
+ dtohl(thisType->header.size);
const uint32_t* const eindex = (const uint32_t*)
(((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
-
+
uint32_t thisOffset = dtohl(eindex[entryIndex]);
if (thisOffset == ResTable_type::NO_ENTRY) {
TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
continue;
}
-
+
if (type != NULL) {
// Check if this one is less specific than the last found. If so,
// we will skip it. We check starting with things we most care
@@ -5286,19 +5286,19 @@
continue;
}
}
-
+
type = thisType;
offset = thisOffset;
bestConfig = thisConfig;
TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
if (!config) break;
}
-
+
if (type == NULL) {
TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
return BAD_INDEX;
}
-
+
offset += dtohl(type->entriesStart);
TABLE_NOISY(aout << "Looking in resource table " << package->header->header
<< ", typeOff="
@@ -5363,7 +5363,7 @@
dtohl(pkg->keyStrings));
return (mError=BAD_TYPE);
}
-
+
Package* package = NULL;
PackageGroup* group = NULL;
uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
@@ -5372,12 +5372,12 @@
// always loaded alongside their idmaps, but during idmap creation
// the package is temporarily loaded by itself.
if (id < 256) {
-
+
package = new Package(this, header, pkg);
if (package == NULL) {
return (mError=NO_MEMORY);
}
-
+
size_t idx = mPackageMap[id];
if (idx == 0) {
idx = mPackageGroups.size()+1;
@@ -5411,7 +5411,7 @@
return (mError=err);
}
group->basePackage = package;
-
+
mPackageMap[id] = (uint8_t)idx;
} else {
group = mPackageGroups.itemAt(idx-1);
@@ -5428,10 +5428,10 @@
return NO_ERROR;
}
-
+
// Iterate through all chunks.
size_t curPackage = 0;
-
+
const ResChunk_header* chunk =
(const ResChunk_header*)(((const uint8_t*)pkg)
+ dtohs(pkg->header.headerSize));
@@ -5450,9 +5450,9 @@
if (err != NO_ERROR) {
return (mError=err);
}
-
+
const size_t typeSpecSize = dtohl(typeSpec->header.size);
-
+
LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(typeSpec->header.type),
@@ -5468,12 +5468,12 @@
(void*)typeSpecSize);
return (mError=BAD_TYPE);
}
-
+
if (typeSpec->id == 0) {
ALOGW("ResTable_type has an id of 0.");
return (mError=BAD_TYPE);
}
-
+
while (package->types.size() < typeSpec->id) {
package->types.add(NULL);
}
@@ -5489,7 +5489,7 @@
t->typeSpecFlags = (const uint32_t*)(
((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
t->typeSpec = typeSpec;
-
+
} else if (ctype == RES_TABLE_TYPE_TYPE) {
const ResTable_type* type = (const ResTable_type*)(chunk);
err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
@@ -5497,9 +5497,9 @@
if (err != NO_ERROR) {
return (mError=err);
}
-
+
const uint32_t typeSize = dtohl(type->header.size);
-
+
LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(type->header.type),
@@ -5523,7 +5523,7 @@
ALOGW("ResTable_type has an id of 0.");
return (mError=BAD_TYPE);
}
-
+
while (package->types.size() < type->id) {
package->types.add(NULL);
}
@@ -5536,7 +5536,7 @@
(int)dtohl(type->entryCount), (int)t->entryCount);
return (mError=BAD_TYPE);
}
-
+
TABLE_GETENTRY(
ResTable_config thisConfig;
thisConfig.copyFromDtoH(type->config);
@@ -5557,7 +5557,7 @@
if (group->typeCount == 0) {
group->typeCount = package->types.size();
}
-
+
return NO_ERROR;
}
@@ -5757,7 +5757,7 @@
* RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
& Res_value::COMPLEX_RADIX_MASK];
printf("%f", value);
-
+
if (!isFraction) {
switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
@@ -5832,7 +5832,7 @@
} else {
printf("(string) null\n");
}
- }
+ }
} else if (value.dataType == Res_value::TYPE_FLOAT) {
printf("(float) %g\n", *(const float*)&value.data);
} else if (value.dataType == Res_value::TYPE_DIMENSION) {
@@ -5875,7 +5875,7 @@
printf("Package Group %d id=%d packageCount=%d name=%s\n",
(int)pgIndex, pg->id, (int)pg->packages.size(),
String8(pg->name).string());
-
+
size_t pkgCount = pg->packages.size();
for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
const Package* pkg = pg->packages[pkgIndex];
@@ -5942,17 +5942,17 @@
continue;
}
for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
-
+
const uint8_t* const end = ((const uint8_t*)type)
+ dtohl(type->header.size);
const uint32_t* const eindex = (const uint32_t*)
(((const uint8_t*)type) + dtohs(type->header.headerSize));
-
+
uint32_t thisOffset = dtohl(eindex[entryIndex]);
if (thisOffset == ResTable_type::NO_ENTRY) {
continue;
}
-
+
uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
@@ -5985,7 +5985,7 @@
entriesStart, thisOffset, typeSize);
continue;
}
-
+
const ResTable_entry* ent = (const ResTable_entry*)
(((const uint8_t*)type) + entriesStart + thisOffset);
if (((entriesStart + thisOffset)&0x3) != 0) {
@@ -5993,7 +5993,7 @@
(entriesStart + thisOffset));
continue;
}
-
+
uintptr_t esize = dtohs(ent->size);
if ((esize&0x3) != 0) {
printf("NON-INTEGER ResTable_entry SIZE: 0x%x\n", esize);
@@ -6004,7 +6004,7 @@
entriesStart, thisOffset, esize, typeSize);
continue;
}
-
+
const Res_value* valuePtr = NULL;
const ResTable_map_entry* bagPtr = NULL;
Res_value value;
@@ -6019,12 +6019,12 @@
(int)value.dataType, (int)value.data,
(int)value.size, (int)value.res0);
}
-
+
if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
printf(" (PUBLIC)");
}
printf("\n");
-
+
if (inclValues) {
if (valuePtr != NULL) {
printf(" ");
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index e9ac2fe..6fa0f14 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -127,7 +127,7 @@
goto z_bail;
}
- /* output buffer holds all, so no need to write the output */
+ /* output buffer holds all, so no need to write the output */
} while (zerr == Z_OK);
assert(zerr == Z_STREAM_END); /* other errors should've been caught */
@@ -197,7 +197,7 @@
{
}
- long read(unsigned char** nextBuffer, long readSize) {
+ long read(unsigned char** nextBuffer, long /*readSize*/) {
if (!mBufferReturned) {
mBufferReturned = true;
*nextBuffer = mInput;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index eeff4c0..4de755d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -38,6 +38,7 @@
Program.cpp \
ProgramCache.cpp \
RenderBufferCache.cpp \
+ RenderNode.cpp \
RenderProperties.cpp \
ResourceCache.cpp \
ShadowTessellator.cpp \
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index d124cde..cf745ee 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -22,10 +22,10 @@
#include <SkMatrix.h>
#include <utils/StrongPointer.h>
-#include "DisplayList.h"
#include "Layer.h"
#include "OpenGLRenderer.h"
#include "Rect.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index f4de8ec..9e6a96d 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -29,543 +29,6 @@
namespace android {
namespace uirenderer {
-void RenderNode::outputLogBuffer(int fd) {
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- if (logBuffer.isEmpty()) {
- return;
- }
-
- FILE *file = fdopen(fd, "a");
-
- fprintf(file, "\nRecent DisplayList operations\n");
- logBuffer.outputCommands(file);
-
- String8 cachesLog;
- Caches::getInstance().dumpMemoryUsage(cachesLog);
- fprintf(file, "\nCaches:\n%s", cachesLog.string());
- fprintf(file, "\n");
-
- fflush(file);
-}
-
-RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
-}
-
-RenderNode::~RenderNode() {
- LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
-
- mDestroyed = true;
- delete mDisplayListData;
-}
-
-void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
- if (displayList) {
- DISPLAY_LIST_LOGD("Deferring display list destruction");
- Caches::getInstance().deleteDisplayListDeferred(displayList);
- }
-}
-
-void RenderNode::setData(DisplayListData* data) {
- delete mDisplayListData;
- mDisplayListData = data;
- if (mDisplayListData) {
- Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
- }
-}
-
-/**
- * This function is a simplified version of replay(), where we simply retrieve and log the
- * display list. This function should remain in sync with the replay() function.
- */
-void RenderNode::output(uint32_t level) {
- ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
- mName.string(), isRenderable());
- ALOGD("%*s%s %d", level * 2, "", "Save",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-
- outputViewProperties(level);
- int flags = DisplayListOp::kOpLogFlag_Recurse;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- mDisplayListData->displayListOps[i]->output(level, flags);
- }
-
- ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
-}
-
-void RenderNode::outputViewProperties(const int level) {
- properties().updateMatrix();
- if (properties().mLeft != 0 || properties().mTop != 0) {
- ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
- }
- if (properties().mStaticMatrix) {
- ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
- level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
- }
- if (properties().mAnimationMatrix) {
- ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
- level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
- }
- if (properties().mMatrixFlags != 0) {
- if (properties().mMatrixFlags == TRANSLATION) {
- ALOGD("%*sTranslate %.2f, %.2f, %.2f",
- level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
- } else {
- ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
- level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
- }
- }
-
- bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
- if (properties().mAlpha < 1) {
- if (properties().mCaching) {
- ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
- } else if (!properties().mHasOverlappingRendering) {
- ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
- } else {
- int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (clipToBoundsNeeded) {
- flags |= SkCanvas::kClipToLayer_SaveFlag;
- clipToBoundsNeeded = false; // clipping done by save layer
- }
- ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
- (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
- (int)(properties().mAlpha * 255), flags);
- }
- }
- if (clipToBoundsNeeded) {
- ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
- (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
- }
-}
-
-/*
- * For property operations, we pass a savecount of 0, since the operations aren't part of the
- * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
- * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
- */
-#define PROPERTY_SAVECOUNT 0
-
-template <class T>
-void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
- const int level) {
-#if DEBUG_DISPLAY_LIST
- outputViewProperties(level);
-#endif
- properties().updateMatrix();
- if (properties().mLeft != 0 || properties().mTop != 0) {
- renderer.translate(properties().mLeft, properties().mTop);
- }
- if (properties().mStaticMatrix) {
- renderer.concatMatrix(properties().mStaticMatrix);
- } else if (properties().mAnimationMatrix) {
- renderer.concatMatrix(properties().mAnimationMatrix);
- }
- if (properties().mMatrixFlags != 0) {
- if (properties().mMatrixFlags == TRANSLATION) {
- renderer.translate(properties().mTranslationX, properties().mTranslationY);
- } else {
- renderer.concatMatrix(*properties().mTransformMatrix);
- }
- }
- bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
- if (properties().mAlpha < 1) {
- if (properties().mCaching) {
- renderer.setOverrideLayerAlpha(properties().mAlpha);
- } else if (!properties().mHasOverlappingRendering) {
- renderer.scaleAlpha(properties().mAlpha);
- } else {
- // TODO: should be able to store the size of a DL at record time and not
- // have to pass it into this call. In fact, this information might be in the
- // location/size info that we store with the new native transform data.
- int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (clipToBoundsNeeded) {
- saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
- clipToBoundsNeeded = false; // clipping done by saveLayer
- }
-
- SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
- 0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
- handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
- }
- if (clipToBoundsNeeded) {
- ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
- properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
- handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
- if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
- ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
- handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
-}
-
-/**
- * Apply property-based transformations to input matrix
- *
- * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
- * matrix computation instead of the Skia 3x3 matrix + camera hackery.
- */
-void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
- if (properties().mLeft != 0 || properties().mTop != 0) {
- matrix.translate(properties().mLeft, properties().mTop);
- }
- if (properties().mStaticMatrix) {
- mat4 stat(*properties().mStaticMatrix);
- matrix.multiply(stat);
- } else if (properties().mAnimationMatrix) {
- mat4 anim(*properties().mAnimationMatrix);
- matrix.multiply(anim);
- }
- if (properties().mMatrixFlags != 0) {
- properties().updateMatrix();
- if (properties().mMatrixFlags == TRANSLATION) {
- matrix.translate(properties().mTranslationX, properties().mTranslationY,
- true3dTransform ? properties().mTranslationZ : 0.0f);
- } else {
- if (!true3dTransform) {
- matrix.multiply(*properties().mTransformMatrix);
- } else {
- mat4 true3dMat;
- true3dMat.loadTranslate(
- properties().mPivotX + properties().mTranslationX,
- properties().mPivotY + properties().mTranslationY,
- properties().mTranslationZ);
- true3dMat.rotate(properties().mRotationX, 1, 0, 0);
- true3dMat.rotate(properties().mRotationY, 0, 1, 0);
- true3dMat.rotate(properties().mRotation, 0, 0, 1);
- true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
- true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
-
- matrix.multiply(true3dMat);
- }
- }
- }
-}
-
-/**
- * Organizes the DisplayList hierarchy to prepare for background projection reordering.
- *
- * This should be called before a call to defer() or drawDisplayList()
- *
- * Each DisplayList that serves as a 3d root builds its list of composited children,
- * which are flagged to not draw in the standard draw loop.
- */
-void RenderNode::computeOrdering() {
- ATRACE_CALL();
- mProjectedNodes.clear();
-
- // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
- // transform properties are applied correctly to top level children
- if (mDisplayListData == NULL) return;
- for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children[i];
- childOp->mDisplayList->computeOrderingImpl(childOp,
- &mProjectedNodes, &mat4::identity());
- }
-}
-
-void RenderNode::computeOrderingImpl(
- DrawDisplayListOp* opState,
- Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
- const mat4* transformFromProjectionSurface) {
- mProjectedNodes.clear();
- if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
-
- // TODO: should avoid this calculation in most cases
- // TODO: just calculate single matrix, down to all leaf composited elements
- Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
- localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
-
- if (properties().mProjectBackwards) {
- // composited projectee, flag for out of order draw, save matrix, and store in proj surface
- opState->mSkipInOrderDraw = true;
- opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
- compositedChildrenOfProjectionSurface->add(opState);
- } else {
- // standard in order draw
- opState->mSkipInOrderDraw = false;
- }
-
- if (mDisplayListData->children.size() > 0) {
- const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
- bool haveAppliedPropertiesToProjection = false;
- for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children[i];
- RenderNode* child = childOp->mDisplayList;
-
- Vector<DrawDisplayListOp*>* projectionChildren = NULL;
- const mat4* projectionTransform = NULL;
- if (isProjectionReceiver && !child->properties().mProjectBackwards) {
- // if receiving projections, collect projecting descendent
-
- // Note that if a direct descendent is projecting backwards, we pass it's
- // grandparent projection collection, since it shouldn't project onto it's
- // parent, where it will already be drawing.
- projectionChildren = &mProjectedNodes;
- projectionTransform = &mat4::identity();
- } else {
- if (!haveAppliedPropertiesToProjection) {
- applyViewPropertyTransforms(localTransformFromProjectionSurface);
- haveAppliedPropertiesToProjection = true;
- }
- projectionChildren = compositedChildrenOfProjectionSurface;
- projectionTransform = &localTransformFromProjectionSurface;
- }
- child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
- }
- }
-
-}
-
-class DeferOperationHandler {
-public:
- DeferOperationHandler(DeferStateStruct& deferStruct, int level)
- : mDeferStruct(deferStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
- operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
- }
- inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
-
-private:
- DeferStateStruct& mDeferStruct;
- const int mLevel;
-};
-
-void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
- DeferOperationHandler handler(deferStruct, level);
- iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
-}
-
-class ReplayOperationHandler {
-public:
- ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
- : mReplayStruct(replayStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- properties().mReplayStruct.mRenderer.eventMark(operation->name());
-#endif
- operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
- }
- inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
-
-private:
- ReplayStateStruct& mReplayStruct;
- const int mLevel;
-};
-
-void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
- ReplayOperationHandler handler(replayStruct, level);
-
- replayStruct.mRenderer.startMark(mName.string());
- iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
- replayStruct.mRenderer.endMark();
-
- DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
- replayStruct.mDrawGlStatus);
-}
-
-void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
- if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
-
- for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children[i];
- RenderNode* child = childOp->mDisplayList;
- float childZ = child->properties().mTranslationZ;
-
- if (childZ != 0.0f) {
- zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
- childOp->mSkipInOrderDraw = true;
- } else if (!child->properties().mProjectBackwards) {
- // regular, in order drawing DisplayList
- childOp->mSkipInOrderDraw = false;
- }
- }
-
- // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
- std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
-}
-
-#define SHADOW_DELTA 0.1f
-
-template <class T>
-void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
- ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
- const int size = zTranslatedNodes.size();
- if (size == 0
- || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
- || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
- // no 3d children to draw
- return;
- }
-
- int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- LinearAllocator& alloc = handler.allocator();
- ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
- SkRegion::kIntersect_Op); // clip to 3d root bounds
- handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
- /**
- * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
- * with very similar Z heights to draw together.
- *
- * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
- * underneath both, and neither's shadow is drawn on top of the other.
- */
- const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
- size_t drawIndex, shadowIndex, endIndex;
- if (mode == kNegativeZChildren) {
- drawIndex = 0;
- endIndex = nonNegativeIndex;
- shadowIndex = endIndex; // draw no shadows
- } else {
- drawIndex = nonNegativeIndex;
- endIndex = size;
- shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
- }
- float lastCasterZ = 0.0f;
- while (shadowIndex < endIndex || drawIndex < endIndex) {
- if (shadowIndex < endIndex) {
- DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
- RenderNode* caster = casterOp->mDisplayList;
- const float casterZ = zTranslatedNodes[shadowIndex].key;
- // attempt to render the shadow if the caster about to be drawn is its caster,
- // OR if its caster's Z value is similar to the previous potential caster
- if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
-
- if (caster->properties().mAlpha > 0.0f) {
- mat4 shadowMatrixXY(casterOp->mTransformFromParent);
- caster->applyViewPropertyTransforms(shadowMatrixXY);
-
- // Z matrix needs actual 3d transformation, so mapped z values will be correct
- mat4 shadowMatrixZ(casterOp->mTransformFromParent);
- caster->applyViewPropertyTransforms(shadowMatrixZ, true);
-
- DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
- shadowMatrixXY, shadowMatrixZ,
- caster->properties().mAlpha, &(caster->properties().mOutline),
- caster->properties().mWidth, caster->properties().mHeight);
- handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
- }
-
- lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
- shadowIndex++;
- continue;
- }
- }
-
- // only the actual child DL draw needs to be in save/restore,
- // since it modifies the renderer's matrix
- int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
-
- DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
- RenderNode* child = childOp->mDisplayList;
-
- renderer.concatMatrix(childOp->mTransformFromParent);
- childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
- handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
- childOp->mSkipInOrderDraw = true;
-
- renderer.restoreToCount(restoreTo);
- drawIndex++;
- }
- handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-template <class T>
-void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
- int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- LinearAllocator& alloc = handler.allocator();
- ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
- SkRegion::kReplace_Op); // clip to projection surface root bounds
- handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
- for (size_t i = 0; i < mProjectedNodes.size(); i++) {
- DrawDisplayListOp* childOp = mProjectedNodes[i];
-
- // matrix save, concat, and restore can be done safely without allocating operations
- int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
- renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
- childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
- handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
- childOp->mSkipInOrderDraw = true;
- renderer.restoreToCount(restoreTo);
- }
- handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-/**
- * This function serves both defer and replay modes, and will organize the displayList's component
- * operations for a single frame:
- *
- * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
- * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
- * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
- * defer vs replay logic, per operation
- */
-template <class T>
-void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
- if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
- ALOGW("Error: %s is drawing after destruction", mName.string());
- CRASH();
- }
- if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
- DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
- return;
- }
-
-#if DEBUG_DISPLAY_LIST
- Rect* clipRect = renderer.getClipRect();
- DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
- level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
- clipRect->right, clipRect->bottom);
-#endif
-
- LinearAllocator& alloc = handler.allocator();
- int restoreTo = renderer.getSaveCount();
- handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
- PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
- DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
-
- setViewProperties<T>(renderer, handler, level + 1);
-
- bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
- if (!quickRejected) {
- Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
- buildZSortedChildList(zTranslatedNodes);
-
- // for 3d root, draw children with negative z values
- iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
-
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- DisplayListOp *op = mDisplayListData->displayListOps[i];
-
-#if DEBUG_DISPLAY_LIST
- op->output(level + 1);
-#endif
-
- logBuffer.writeCommand(level, op->name());
- handler(op, saveCountOffset, properties().mClipToBounds);
-
- if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
- iterateProjectedChildren(renderer, handler, level);
- }
- }
-
- // for 3d root, draw children with positive z values
- iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
- }
-
- DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
- handler(new (alloc) RestoreToCountOp(restoreTo),
- PROPERTY_SAVECOUNT, properties().mClipToBounds);
- renderer.setOverrideLayerAlpha(1.0f);
-}
-
void DisplayListData::cleanupResources() {
Caches& caches = Caches::getInstance();
caches.unregisterFunctors(functorCount);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b80c118..df5cba6 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -143,136 +143,6 @@
void cleanupResources();
};
-/**
- * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
- *
- * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
- * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
- * a renderer).
- *
- * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
- * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
- * attached.
- */
-class RenderNode {
-public:
- ANDROID_API RenderNode();
- ANDROID_API ~RenderNode();
-
- // See flags defined in DisplayList.java
- enum ReplayFlag {
- kReplayFlag_ClipChildren = 0x1
- };
-
- ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
- ANDROID_API static void outputLogBuffer(int fd);
-
- ANDROID_API void setData(DisplayListData* newData);
-
- void computeOrdering();
- void defer(DeferStateStruct& deferStruct, const int level);
- void replay(ReplayStateStruct& replayStruct, const int level);
-
- ANDROID_API void output(uint32_t level = 1);
-
- bool isRenderable() const {
- return mDisplayListData && mDisplayListData->hasDrawOps;
- }
-
- void setName(const char* name) {
- if (name) {
- char* lastPeriod = strrchr(name, '.');
- if (lastPeriod) {
- mName.setTo(lastPeriod + 1);
- } else {
- mName.setTo(name);
- }
- }
- }
-
- RenderProperties& properties() {
- return mProperties;
- }
-
- bool isProjectionReceiver() {
- return properties().isProjectionReceiver();
- }
-
- int getWidth() {
- return properties().getWidth();
- }
-
- int getHeight() {
- return properties().getHeight();
- }
-
-private:
- typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
-
- static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
- for (size_t i = 0; i < nodes.size(); i++) {
- if (nodes[i].key >= 0.0f) return i;
- }
- return nodes.size();
- }
-
- enum ChildrenSelectMode {
- kNegativeZChildren,
- kPositiveZChildren
- };
-
- void outputViewProperties(const int level);
-
- void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
-
- void computeOrderingImpl(DrawDisplayListOp* opState,
- Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
- const mat4* transformFromProjectionSurface);
-
- template <class T>
- inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
-
- void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
-
- template <class T>
- inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
- ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
-
- template <class T>
- inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
-
- template <class T>
- inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
-
- class TextContainer {
- public:
- size_t length() const {
- return mByteLength;
- }
-
- const char* text() const {
- return (const char*) mText;
- }
-
- size_t mByteLength;
- const char* mText;
- };
-
- String8 mName;
- bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
-
- RenderProperties mProperties;
- DisplayListData* mDisplayListData;
-
- /**
- * Draw time state - these properties are only set and used during rendering
- */
-
- // for projection surfaces, contains a list of all children items
- Vector<DrawDisplayListOp*> mProjectedNodes;
-}; // class DisplayList
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index e69e08e..78c97e1 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,12 +21,12 @@
#include <private/hwui/DrawGlInfo.h>
-#include "DisplayList.h"
+#include "Caches.h"
#include "DeferredDisplayList.h"
#include "DisplayListLogBuffer.h"
#include "DisplayListOp.h"
#include "DisplayListRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 65498a5..04c5a73 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -22,9 +22,9 @@
#include <SkPath.h>
#include <cutils/compiler.h>
-#include "DisplayList.h"
#include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 52176d4..bd9bfe9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,12 +18,12 @@
#include <utils/Log.h>
-#include "DisplayList.h"
+#include "Caches.h"
#include "DeferredDisplayList.h"
#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
new file mode 100644
index 0000000..e371590
--- /dev/null
+++ b/libs/hwui/RenderNode.cpp
@@ -0,0 +1,571 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include "RenderNode.h"
+
+#include <SkCanvas.h>
+#include <algorithm>
+
+#include <utils/Trace.h>
+
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "DisplayListLogBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+void RenderNode::outputLogBuffer(int fd) {
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ if (logBuffer.isEmpty()) {
+ return;
+ }
+
+ FILE *file = fdopen(fd, "a");
+
+ fprintf(file, "\nRecent DisplayList operations\n");
+ logBuffer.outputCommands(file);
+
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ fprintf(file, "\nCaches:\n%s", cachesLog.string());
+ fprintf(file, "\n");
+
+ fflush(file);
+}
+
+RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
+}
+
+RenderNode::~RenderNode() {
+ LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
+
+ mDestroyed = true;
+ delete mDisplayListData;
+}
+
+void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
+ if (displayList) {
+ DISPLAY_LIST_LOGD("Deferring display list destruction");
+ Caches::getInstance().deleteDisplayListDeferred(displayList);
+ }
+}
+
+void RenderNode::setData(DisplayListData* data) {
+ delete mDisplayListData;
+ mDisplayListData = data;
+ if (mDisplayListData) {
+ Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
+ }
+}
+
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void RenderNode::output(uint32_t level) {
+ ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
+ mName.string(), isRenderable());
+ ALOGD("%*s%s %d", level * 2, "", "Save",
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
+ outputViewProperties(level);
+ int flags = DisplayListOp::kOpLogFlag_Recurse;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ mDisplayListData->displayListOps[i]->output(level, flags);
+ }
+
+ ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
+}
+
+void RenderNode::outputViewProperties(const int level) {
+ properties().updateMatrix();
+ if (properties().mLeft != 0 || properties().mTop != 0) {
+ ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
+ }
+ if (properties().mStaticMatrix) {
+ ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
+ level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
+ }
+ if (properties().mAnimationMatrix) {
+ ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
+ level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
+ }
+ if (properties().mMatrixFlags != 0) {
+ if (properties().mMatrixFlags == TRANSLATION) {
+ ALOGD("%*sTranslate %.2f, %.2f, %.2f",
+ level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
+ } else {
+ ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
+ level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
+ }
+ }
+
+ bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+ if (properties().mAlpha < 1) {
+ if (properties().mCaching) {
+ ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
+ } else if (!properties().mHasOverlappingRendering) {
+ ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
+ } else {
+ int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (clipToBoundsNeeded) {
+ flags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by save layer
+ }
+ ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
+ (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
+ (int)(properties().mAlpha * 255), flags);
+ }
+ }
+ if (clipToBoundsNeeded) {
+ ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
+ (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
+ }
+}
+
+/*
+ * For property operations, we pass a savecount of 0, since the operations aren't part of the
+ * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
+ * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
+ */
+#define PROPERTY_SAVECOUNT 0
+
+template <class T>
+void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
+ const int level) {
+#if DEBUG_DISPLAY_LIST
+ outputViewProperties(level);
+#endif
+ properties().updateMatrix();
+ if (properties().mLeft != 0 || properties().mTop != 0) {
+ renderer.translate(properties().mLeft, properties().mTop);
+ }
+ if (properties().mStaticMatrix) {
+ renderer.concatMatrix(properties().mStaticMatrix);
+ } else if (properties().mAnimationMatrix) {
+ renderer.concatMatrix(properties().mAnimationMatrix);
+ }
+ if (properties().mMatrixFlags != 0) {
+ if (properties().mMatrixFlags == TRANSLATION) {
+ renderer.translate(properties().mTranslationX, properties().mTranslationY);
+ } else {
+ renderer.concatMatrix(*properties().mTransformMatrix);
+ }
+ }
+ bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+ if (properties().mAlpha < 1) {
+ if (properties().mCaching) {
+ renderer.setOverrideLayerAlpha(properties().mAlpha);
+ } else if (!properties().mHasOverlappingRendering) {
+ renderer.scaleAlpha(properties().mAlpha);
+ } else {
+ // TODO: should be able to store the size of a DL at record time and not
+ // have to pass it into this call. In fact, this information might be in the
+ // location/size info that we store with the new native transform data.
+ int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (clipToBoundsNeeded) {
+ saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by saveLayer
+ }
+
+ SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
+ 0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
+ handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+ }
+ if (clipToBoundsNeeded) {
+ ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
+ properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
+ handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+ if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
+ ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
+ handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+}
+
+/**
+ * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
+ */
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
+ if (properties().mLeft != 0 || properties().mTop != 0) {
+ matrix.translate(properties().mLeft, properties().mTop);
+ }
+ if (properties().mStaticMatrix) {
+ mat4 stat(*properties().mStaticMatrix);
+ matrix.multiply(stat);
+ } else if (properties().mAnimationMatrix) {
+ mat4 anim(*properties().mAnimationMatrix);
+ matrix.multiply(anim);
+ }
+ if (properties().mMatrixFlags != 0) {
+ properties().updateMatrix();
+ if (properties().mMatrixFlags == TRANSLATION) {
+ matrix.translate(properties().mTranslationX, properties().mTranslationY,
+ true3dTransform ? properties().mTranslationZ : 0.0f);
+ } else {
+ if (!true3dTransform) {
+ matrix.multiply(*properties().mTransformMatrix);
+ } else {
+ mat4 true3dMat;
+ true3dMat.loadTranslate(
+ properties().mPivotX + properties().mTranslationX,
+ properties().mPivotY + properties().mTranslationY,
+ properties().mTranslationZ);
+ true3dMat.rotate(properties().mRotationX, 1, 0, 0);
+ true3dMat.rotate(properties().mRotationY, 0, 1, 0);
+ true3dMat.rotate(properties().mRotation, 0, 0, 1);
+ true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
+ true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
+
+ matrix.multiply(true3dMat);
+ }
+ }
+ }
+}
+
+/**
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
+ *
+ * This should be called before a call to defer() or drawDisplayList()
+ *
+ * Each DisplayList that serves as a 3d root builds its list of composited children,
+ * which are flagged to not draw in the standard draw loop.
+ */
+void RenderNode::computeOrdering() {
+ ATRACE_CALL();
+ mProjectedNodes.clear();
+
+ // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
+ // transform properties are applied correctly to top level children
+ if (mDisplayListData == NULL) return;
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ childOp->mDisplayList->computeOrderingImpl(childOp,
+ &mProjectedNodes, &mat4::identity());
+ }
+}
+
+void RenderNode::computeOrderingImpl(
+ DrawDisplayListOp* opState,
+ Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+ const mat4* transformFromProjectionSurface) {
+ mProjectedNodes.clear();
+ if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
+
+ // TODO: should avoid this calculation in most cases
+ // TODO: just calculate single matrix, down to all leaf composited elements
+ Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
+ localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
+
+ if (properties().mProjectBackwards) {
+ // composited projectee, flag for out of order draw, save matrix, and store in proj surface
+ opState->mSkipInOrderDraw = true;
+ opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
+ compositedChildrenOfProjectionSurface->add(opState);
+ } else {
+ // standard in order draw
+ opState->mSkipInOrderDraw = false;
+ }
+
+ if (mDisplayListData->children.size() > 0) {
+ const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+ bool haveAppliedPropertiesToProjection = false;
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ RenderNode* child = childOp->mDisplayList;
+
+ Vector<DrawDisplayListOp*>* projectionChildren = NULL;
+ const mat4* projectionTransform = NULL;
+ if (isProjectionReceiver && !child->properties().mProjectBackwards) {
+ // if receiving projections, collect projecting descendent
+
+ // Note that if a direct descendent is projecting backwards, we pass it's
+ // grandparent projection collection, since it shouldn't project onto it's
+ // parent, where it will already be drawing.
+ projectionChildren = &mProjectedNodes;
+ projectionTransform = &mat4::identity();
+ } else {
+ if (!haveAppliedPropertiesToProjection) {
+ applyViewPropertyTransforms(localTransformFromProjectionSurface);
+ haveAppliedPropertiesToProjection = true;
+ }
+ projectionChildren = compositedChildrenOfProjectionSurface;
+ projectionTransform = &localTransformFromProjectionSurface;
+ }
+ child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
+ }
+ }
+
+}
+
+class DeferOperationHandler {
+public:
+ DeferOperationHandler(DeferStateStruct& deferStruct, int level)
+ : mDeferStruct(deferStruct), mLevel(level) {}
+ inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+ operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
+ }
+ inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
+
+private:
+ DeferStateStruct& mDeferStruct;
+ const int mLevel;
+};
+
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
+ DeferOperationHandler handler(deferStruct, level);
+ iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
+}
+
+class ReplayOperationHandler {
+public:
+ ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
+ : mReplayStruct(replayStruct), mLevel(level) {}
+ inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+ properties().mReplayStruct.mRenderer.eventMark(operation->name());
+#endif
+ operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
+ }
+ inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
+
+private:
+ ReplayStateStruct& mReplayStruct;
+ const int mLevel;
+};
+
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
+ ReplayOperationHandler handler(replayStruct, level);
+
+ replayStruct.mRenderer.startMark(mName.string());
+ iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
+ replayStruct.mRenderer.endMark();
+
+ DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
+ replayStruct.mDrawGlStatus);
+}
+
+void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+ if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ RenderNode* child = childOp->mDisplayList;
+ float childZ = child->properties().mTranslationZ;
+
+ if (childZ != 0.0f) {
+ zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+ childOp->mSkipInOrderDraw = true;
+ } else if (!child->properties().mProjectBackwards) {
+ // regular, in order drawing DisplayList
+ childOp->mSkipInOrderDraw = false;
+ }
+ }
+
+ // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
+#define SHADOW_DELTA 0.1f
+
+template <class T>
+void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+ || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
+ // no 3d children to draw
+ return;
+ }
+
+ int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ LinearAllocator& alloc = handler.allocator();
+ ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+ SkRegion::kIntersect_Op); // clip to 3d root bounds
+ handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+ size_t drawIndex, shadowIndex, endIndex;
+ if (mode == kNegativeZChildren) {
+ drawIndex = 0;
+ endIndex = nonNegativeIndex;
+ shadowIndex = endIndex; // draw no shadows
+ } else {
+ drawIndex = nonNegativeIndex;
+ endIndex = size;
+ shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+ }
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
+ RenderNode* caster = casterOp->mDisplayList;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
+ // attempt to render the shadow if the caster about to be drawn is its caster,
+ // OR if its caster's Z value is similar to the previous potential caster
+ if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+
+ if (caster->properties().mAlpha > 0.0f) {
+ mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+ caster->applyViewPropertyTransforms(shadowMatrixXY);
+
+ // Z matrix needs actual 3d transformation, so mapped z values will be correct
+ mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+ caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+ DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
+ shadowMatrixXY, shadowMatrixZ,
+ caster->properties().mAlpha, &(caster->properties().mOutline),
+ caster->properties().mWidth, caster->properties().mHeight);
+ handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ }
+
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ // only the actual child DL draw needs to be in save/restore,
+ // since it modifies the renderer's matrix
+ int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+
+ DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
+ RenderNode* child = childOp->mDisplayList;
+
+ renderer.concatMatrix(childOp->mTransformFromParent);
+ childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+ handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+ childOp->mSkipInOrderDraw = true;
+
+ renderer.restoreToCount(restoreTo);
+ drawIndex++;
+ }
+ handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+template <class T>
+void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
+ int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ LinearAllocator& alloc = handler.allocator();
+ ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+ SkRegion::kReplace_Op); // clip to projection surface root bounds
+ handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+ for (size_t i = 0; i < mProjectedNodes.size(); i++) {
+ DrawDisplayListOp* childOp = mProjectedNodes[i];
+
+ // matrix save, concat, and restore can be done safely without allocating operations
+ int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+ childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+ handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+ childOp->mSkipInOrderDraw = true;
+ renderer.restoreToCount(restoreTo);
+ }
+ handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+/**
+ * This function serves both defer and replay modes, and will organize the displayList's component
+ * operations for a single frame:
+ *
+ * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
+ * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
+ * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
+ * defer vs replay logic, per operation
+ */
+template <class T>
+void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
+ if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
+ ALOGW("Error: %s is drawing after destruction", mName.string());
+ CRASH();
+ }
+ if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
+ DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
+ return;
+ }
+
+#if DEBUG_DISPLAY_LIST
+ Rect* clipRect = renderer.getClipRect();
+ DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
+ level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
+ clipRect->right, clipRect->bottom);
+#endif
+
+ LinearAllocator& alloc = handler.allocator();
+ int restoreTo = renderer.getSaveCount();
+ handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+ DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
+ SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+ setViewProperties<T>(renderer, handler, level + 1);
+
+ bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
+ if (!quickRejected) {
+ Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+ buildZSortedChildList(zTranslatedNodes);
+
+ // for 3d root, draw children with negative z values
+ iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ const int saveCountOffset = renderer.getSaveCount() - 1;
+ const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ DisplayListOp *op = mDisplayListData->displayListOps[i];
+
+#if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+#endif
+
+ logBuffer.writeCommand(level, op->name());
+ handler(op, saveCountOffset, properties().mClipToBounds);
+
+ if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+ iterateProjectedChildren(renderer, handler, level);
+ }
+ }
+
+ // for 3d root, draw children with positive z values
+ iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+ }
+
+ DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
+ handler(new (alloc) RestoreToCountOp(restoreTo),
+ PROPERTY_SAVECOUNT, properties().mClipToBounds);
+ renderer.setOverrideLayerAlpha(1.0f);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
new file mode 100644
index 0000000..177f33e
--- /dev/null
+++ b/libs/hwui/RenderNode.h
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+#ifndef RENDERNODE_H
+#define RENDERNODE_H
+
+#ifndef LOG_TAG
+ #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/LinearAllocator.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "Debug.h"
+#include "Matrix.h"
+#include "DeferredDisplayList.h"
+#include "DisplayList.h"
+#include "RenderProperties.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+namespace android {
+namespace uirenderer {
+
+class DeferredDisplayList;
+class DisplayListOp;
+class DisplayListRenderer;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+class SkiaShader;
+
+class ClipRectOp;
+class SaveLayerOp;
+class SaveOp;
+class RestoreToCountOp;
+class DrawDisplayListOp;
+
+/**
+ * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
+ *
+ * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
+ * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
+ * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
+ * a renderer).
+ *
+ * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
+ * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * attached.
+ */
+class RenderNode {
+public:
+ ANDROID_API RenderNode();
+ ANDROID_API ~RenderNode();
+
+ // See flags defined in DisplayList.java
+ enum ReplayFlag {
+ kReplayFlag_ClipChildren = 0x1
+ };
+
+ ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
+ ANDROID_API static void outputLogBuffer(int fd);
+
+ ANDROID_API void setData(DisplayListData* newData);
+
+ void computeOrdering();
+ void defer(DeferStateStruct& deferStruct, const int level);
+ void replay(ReplayStateStruct& replayStruct, const int level);
+
+ ANDROID_API void output(uint32_t level = 1);
+
+ bool isRenderable() const {
+ return mDisplayListData && mDisplayListData->hasDrawOps;
+ }
+
+ void setName(const char* name) {
+ if (name) {
+ char* lastPeriod = strrchr(name, '.');
+ if (lastPeriod) {
+ mName.setTo(lastPeriod + 1);
+ } else {
+ mName.setTo(name);
+ }
+ }
+ }
+
+ RenderProperties& properties() {
+ return mProperties;
+ }
+
+ bool isProjectionReceiver() {
+ return properties().isProjectionReceiver();
+ }
+
+ int getWidth() {
+ return properties().getWidth();
+ }
+
+ int getHeight() {
+ return properties().getHeight();
+ }
+
+private:
+ typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
+
+ static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+ for (size_t i = 0; i < nodes.size(); i++) {
+ if (nodes[i].key >= 0.0f) return i;
+ }
+ return nodes.size();
+ }
+
+ enum ChildrenSelectMode {
+ kNegativeZChildren,
+ kPositiveZChildren
+ };
+
+ void outputViewProperties(const int level);
+
+ void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
+
+ void computeOrderingImpl(DrawDisplayListOp* opState,
+ Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+ const mat4* transformFromProjectionSurface);
+
+ template <class T>
+ inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
+
+ void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
+ template <class T>
+ inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
+
+ template <class T>
+ inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
+
+ template <class T>
+ inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
+
+ class TextContainer {
+ public:
+ size_t length() const {
+ return mByteLength;
+ }
+
+ const char* text() const {
+ return (const char*) mText;
+ }
+
+ size_t mByteLength;
+ const char* mText;
+ };
+
+ String8 mName;
+ bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
+
+ RenderProperties mProperties;
+ DisplayListData* mDisplayListData;
+
+ /**
+ * Draw time state - these properties are only set and used during rendering
+ */
+
+ // for projection surfaces, contains a list of all children items
+ Vector<DrawDisplayListOp*> mProjectedNodes;
+}; // class RenderNode
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODE_H */
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index f81cd12..5fa0ba5 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -573,10 +573,8 @@
for (int j = 0; j < lightPolyLength; j++) {
int m = 0;
for (int i = 0; i < polyLength; i++) {
+ // After validating the input, deltaZ is guaranteed to be positive.
float deltaZ = lightPoly[j].z - poly[i].z;
- if (deltaZ == 0) {
- return;
- }
float ratioZ = lightPoly[j].z / deltaZ;
float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x);
float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y);
@@ -615,9 +613,6 @@
// If there is no real umbra, make a fake one.
for (int i = 0; i < polyLength; i++) {
float deltaZ = lightCenter.z - poly[i].z;
- if (deltaZ == 0) {
- return;
- }
float ratioZ = lightCenter.z / deltaZ;
float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x);
float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 46b74da..fe510f6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -258,7 +258,7 @@
private final boolean mUseFixedVolume;
// stream names used by dumpStreamStates()
- private final String[] STREAM_NAMES = new String[] {
+ private static final String[] STREAM_NAMES = new String[] {
"STREAM_VOICE_CALL",
"STREAM_SYSTEM",
"STREAM_RING",
@@ -614,6 +614,12 @@
pw.println(Integer.toHexString(mMuteAffectedStreams));
}
+ /** @hide */
+ public static String streamToString(int stream) {
+ if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
+ if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
+ return "UNKNOWN_STREAM_" + stream;
+ }
private void updateStreamVolumeAlias(boolean updateVolumes) {
int dtmfStreamAlias;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5611efb..dee8705 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,11 +23,20 @@
import java.nio.NioUtils;
import android.annotation.IntDef;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
+import com.android.internal.app.IAppOpsService;
+
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
@@ -239,7 +248,10 @@
* Audio session ID
*/
private int mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
-
+ /**
+ * Reference to the app-ops service.
+ */
+ private final IAppOpsService mAppOps;
//--------------------------------
// Used exclusively by native code
@@ -343,6 +355,9 @@
audioBuffSizeCheck(bufferSizeInBytes);
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
+
if (sessionId < 0) {
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
}
@@ -841,6 +856,9 @@
* {@link #ERROR_INVALID_OPERATION}
*/
public int setStereoVolume(float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return SUCCESS;
+ }
if (mState == STATE_UNINITIALIZED) {
return ERROR_INVALID_OPERATION;
}
@@ -1014,13 +1032,25 @@
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("play() called on uninitialized AudioTrack.");
}
-
+ if (isRestricted()) {
+ setVolume(0);
+ }
synchronized(mPlayStateLock) {
native_start();
mPlayState = PLAYSTATE_PLAYING;
}
}
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, mStreamType,
+ Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* Stops playing the audio data.
* When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
@@ -1296,6 +1326,9 @@
* {@link #ERROR_INVALID_OPERATION}
*/
public int setAuxEffectSendLevel(float level) {
+ if (isRestricted()) {
+ return SUCCESS;
+ }
if (mState == STATE_UNINITIALIZED) {
return ERROR_INVALID_OPERATION;
}
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index 67680a8..eb91668 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -269,6 +269,6 @@
native_init();
}
- private int mNativeContext;
+ private long mNativeContext;
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e20a4af..1b92410 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,6 +16,8 @@
package android.media;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,6 +34,8 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -42,6 +46,8 @@
import android.media.SubtitleController;
import android.media.SubtitleData;
+import com.android.internal.app.IAppOpsService;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -576,6 +582,8 @@
private PowerManager.WakeLock mWakeLock = null;
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
+ private final IAppOpsService mAppOps;
+ private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
/**
* Default constructor. Consider using one of the create() methods for
@@ -599,6 +607,8 @@
mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>();
mOpenSubtitleSources = new Vector<InputStream>();
mInbandSubtitleTracks = new SubtitleTrack[0];
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -1055,13 +1065,35 @@
*
* @throws IllegalStateException if it is called in an invalid state
*/
- public void start() throws IllegalStateException {
+ public void start() throws IllegalStateException {
+ if (isRestricted()) {
+ _setVolume(0, 0);
+ }
stayAwake(true);
_start();
}
private native void _start() throws IllegalStateException;
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ getAudioStreamType(), Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ private int getAudioStreamType() {
+ if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ mStreamType = _getAudioStreamType();
+ }
+ return mStreamType;
+ }
+
+ private native int _getAudioStreamType() throws IllegalStateException;
+
/**
* Stops playback after playback has been stopped or paused.
*
@@ -1402,7 +1434,12 @@
* @param streamtype the audio stream type
* @see android.media.AudioManager
*/
- public native void setAudioStreamType(int streamtype);
+ public void setAudioStreamType(int streamtype) {
+ _setAudioStreamType(streamtype);
+ mStreamType = streamtype;
+ }
+
+ private native void _setAudioStreamType(int streamtype);
/**
* Sets the player to be looping or non-looping.
@@ -1435,7 +1472,14 @@
* The single parameter form below is preferred if the channel volumes don't need
* to be set independently.
*/
- public native void setVolume(float leftVolume, float rightVolume);
+ public void setVolume(float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(leftVolume, rightVolume);
+ }
+
+ private native void _setVolume(float leftVolume, float rightVolume);
/**
* Similar, excepts sets volume of all channels to same value.
@@ -1500,7 +1544,14 @@
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
* @param level send level scalar
*/
- public native void setAuxEffectSendLevel(float level);
+ public void setAuxEffectSendLevel(float level) {
+ if (isRestricted()) {
+ return;
+ }
+ _setAuxEffectSendLevel(level);
+ }
+
+ private native void _setAuxEffectSendLevel(float level);
/*
* @param request Parcel destinated to the media player. The
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index f1b256e..14f0c69 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -20,16 +20,24 @@
import java.io.FileDescriptor;
import java.lang.ref.WeakReference;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import com.android.internal.app.IAppOpsService;
+
/**
* The SoundPool class manages and plays audio resources for applications.
@@ -449,6 +457,8 @@
private SoundPool mProxy;
private final Object mLock;
+ private final int mStreamType;
+ private final IAppOpsService mAppOps;
// SoundPool messages
//
@@ -463,6 +473,9 @@
}
mLock = new Object();
mProxy = proxy;
+ mStreamType = streamType;
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
}
public int load(String path, int priority)
@@ -522,9 +535,27 @@
public native final boolean unload(int soundID);
- public native final int play(int soundID, float leftVolume, float rightVolume,
+ public final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate) {
+ if (isRestricted()) {
+ leftVolume = rightVolume = 0;
+ }
+ return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+ }
+
+ public native final int _play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate);
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ mStreamType, Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
public native final void pause(int streamID);
public native final void resume(int streamID);
@@ -535,8 +566,14 @@
public native final void stop(int streamID);
- public native final void setVolume(int streamID,
- float leftVolume, float rightVolume);
+ public final void setVolume(int streamID, float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(streamID, leftVolume, rightVolume);
+ }
+
+ private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
public void setVolume(int streamID, float volume) {
setVolume(streamID, volume, volume);
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index c48af11..0e7d83e 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -84,7 +84,7 @@
static sp<JMediaHTTPConnection> setObject(
JNIEnv *env, jobject thiz, const sp<JMediaHTTPConnection> &conn) {
sp<JMediaHTTPConnection> old =
- (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context);
+ (JMediaHTTPConnection *)env->GetLongField(thiz, gFields.context);
if (conn != NULL) {
conn->incStrong(thiz);
@@ -92,13 +92,13 @@
if (old != NULL) {
old->decStrong(thiz);
}
- env->SetIntField(thiz, gFields.context, (int)conn.get());
+ env->SetLongField(thiz, gFields.context, (jlong)conn.get());
return old;
}
static sp<JMediaHTTPConnection> getObject(JNIEnv *env, jobject thiz) {
- return (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context);
+ return (JMediaHTTPConnection *)env->GetLongField(thiz, gFields.context);
}
static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) {
@@ -106,7 +106,7 @@
env, env->FindClass("android/media/MediaHTTPConnection"));
CHECK(clazz.get() != NULL);
- gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I");
+ gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
CHECK(gFields.context != NULL);
gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I");
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index dc3ae5b..abebd48 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -500,6 +500,20 @@
process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
}
+static jint
+android_media_MediaPlayer_getAudioStreamType(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return 0;
+ }
+ audio_stream_type_t streamtype;
+ process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
+ ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
+ return (jint) streamtype;
+}
+
static void
android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
{
@@ -841,10 +855,11 @@
{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer_release},
{"_reset", "()V", (void *)android_media_MediaPlayer_reset},
- {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
+ {"_setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
+ {"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer_getAudioStreamType},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
- {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
+ {"_setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
@@ -853,7 +868,7 @@
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
- {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
+ {"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index 76e8346..312c366 100644
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -47,7 +47,6 @@
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
- libaudioflinger \
libaudioutils \
libbinder \
libcutils \
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index 9cc55ab..bda3b6b 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -229,7 +229,7 @@
"(I)Z",
(void *)android_media_SoundPool_SoundPoolImpl_unload
},
- { "play",
+ { "_play",
"(IFFIIF)I",
(void *)android_media_SoundPool_SoundPoolImpl_play
},
@@ -253,7 +253,7 @@
"(I)V",
(void *)android_media_SoundPool_SoundPoolImpl_stop
},
- { "setVolume",
+ { "_setVolume",
"(IFF)V",
(void *)android_media_SoundPool_SoundPoolImpl_setVolume
},
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 3b448d9..e43a1e2 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -47,7 +47,7 @@
<string name="pref_advanced_devices" msgid="903257239609301276">"Erweiterte Geräte anzeigen"</string>
<string name="pref_file_size" msgid="2826879315743961459">"Dateigröße anzeigen"</string>
<string name="pref_device_size" msgid="3542106883278997222">"Geräteabmessungen anzeigen"</string>
- <string name="empty" msgid="7858882803708117596">"Keine Elemente"</string>
+ <string name="empty" msgid="7858882803708117596">"Keine Dokumente"</string>
<string name="toast_no_application" msgid="1339885974067891667">"Datei kann nicht geöffnet werden."</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"Einige Dokumente konnten nicht gelöscht werden."</string>
<string name="share_via" msgid="8966594246261344259">"Teilen über"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index aec3318..5a91484 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -27,7 +27,7 @@
<string name="menu_settings" msgid="6008033148948428823">"Ρυθμίσεις"</string>
<string name="menu_open" msgid="432922957274920903">"Άνοιγμα"</string>
<string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string>
- <string name="menu_share" msgid="3075149983979628146">"Κοινή χρήση"</string>
+ <string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string>
<string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Επιλέχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
@@ -50,5 +50,5 @@
<string name="empty" msgid="7858882803708117596">"Δεν υπάρχουν στοιχεία"</string>
<string name="toast_no_application" msgid="1339885974067891667">"Δεν είναι δυνατό το άνοιγμα του αρχείου"</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"Δεν είναι δυνατή η διαγραφή ορισμένων εγγράφων"</string>
- <string name="share_via" msgid="8966594246261344259">"Κοινή χρήση μέσω"</string>
+ <string name="share_via" msgid="8966594246261344259">"Κοινοποίηση μέσω"</string>
</resources>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 519b936..d9f4475 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -22,7 +22,7 @@
<string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string>
<string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string>
- <string name="menu_sort" msgid="7677740407158414452">"Sortir menurut"</string>
+ <string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string>
<string name="menu_search" msgid="3816712084502856974">"Telusuri"</string>
<string name="menu_settings" msgid="6008033148948428823">"Setelan"</string>
<string name="menu_open" msgid="432922957274920903">"Buka"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 2a96b1a..4b5ebcd 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,7 +44,7 @@
<string name="root_type_shortcut" msgid="3318760609471618093">"Skratky"</string>
<string name="root_type_device" msgid="7121342474653483538">"Zariadenia"</string>
<string name="root_type_apps" msgid="8838065367985945189">"Ďalšie aplikácie"</string>
- <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť rozšírené zariadenia"</string>
+ <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť pokročilé zariadenia"</string>
<string name="pref_file_size" msgid="2826879315743961459">"Zobraziť veľkosť súboru"</string>
<string name="pref_device_size" msgid="3542106883278997222">"Zobraziť veľkosť zariadenia"</string>
<string name="empty" msgid="7858882803708117596">"Žiadne položky"</string>
diff --git a/packages/ExternalStorageProvider/res/values-sk/strings.xml b/packages/ExternalStorageProvider/res/values-sk/strings.xml
index fd424c8..9be7b79 100644
--- a/packages/ExternalStorageProvider/res/values-sk/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sk/strings.xml
@@ -17,6 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Externý ukladací priestor"</string>
- <string name="root_internal_storage" msgid="827844243068584127">"Interný ukladací priestor"</string>
+ <string name="root_internal_storage" msgid="827844243068584127">"Interné úložisko"</string>
<string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 1e79ee4..2e76f19 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -1120,6 +1120,7 @@
KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
frame.removeAllViews();
}
+ getSecurityContainer().onPause(); // clean up any actions in progress
}
public void goToWidget(int appWidgetId) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index 8738288..5b35ba88 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -400,6 +400,7 @@
} else {
mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
}
+ mWindowLayoutParams.format = show ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 55d7def..58086c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -69,7 +69,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 100;
+ private static final int DATABASE_VERSION = 101;
private Context mContext;
private int mUserHandle;
@@ -1591,6 +1591,28 @@
}
upgradeVersion = 100;
}
+ if (upgradeVersion == 100) {
+ // Catch devices that were initialized to version 100 and missed these in onCreate()
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
+ + " VALUES(?,?);");
+ loadIntegerSetting(stmt, Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ R.integer.def_lock_screen_show_notifications);
+
+ loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ R.integer.def_heads_up_enabled);
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ }
+ upgradeVersion = 101;
+ }
// *** Remember to update DATABASE_VERSION above!
@@ -2314,6 +2336,12 @@
loadIntegerSetting(stmt, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
R.integer.def_wifi_scan_always_available);
+ loadIntegerSetting(stmt, Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ R.integer.def_lock_screen_show_notifications);
+
+ loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ R.integer.def_heads_up_enabled);
+
// --- New global settings start here
} finally {
if (stmt != null) stmt.close();
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 71a644d..7671ef1 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -206,7 +206,7 @@
<string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modalità inversione colori"</string>
<string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modalità di contrasto avanzata"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Modalità di correzione del colore"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ELEMENTI RECENTI"</string>
+ <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
<string name="description_target_search" msgid="3091587249776033139">"Ricerca"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Su per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6e09131..032fa34 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -204,7 +204,7 @@
<string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modus voor kleurinversie"</string>
<string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modus voor verbeterd contrast"</string>
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Modus voor kleurcorrectie"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTEN"</string>
+ <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
<string name="description_target_search" msgid="3091587249776033139">"Zoeken"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0d9be4c..0188563 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -151,7 +151,7 @@
<string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"มือถือ <xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="NETWORK">%3$s</xliff:g>"</string>
<string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"แบตเตอรี่ <xliff:g id="STATE">%s</xliff:g>"</string>
- <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้งานบนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
+ <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้บนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -174,7 +174,7 @@
<string name="dessert_case" msgid="1295161776223959221">"ชั้นแสดงของหวาน"</string>
<string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</string>
- <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้งานบนเครื่องบิน"</string>
+ <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้บนเครื่องบิน"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"ชาร์จแล้ว"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"บลูทูธ"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 55399c2..163ef2a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -199,7 +199,7 @@
<string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
- <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi:關閉"</string>
+ <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"投放螢幕"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
diff --git a/packages/services/PacProcessor/jni/Android.mk b/packages/services/PacProcessor/jni/Android.mk
index f16c90b..8a60927 100644
--- a/packages/services/PacProcessor/jni/Android.mk
+++ b/packages/services/PacProcessor/jni/Android.mk
@@ -35,6 +35,7 @@
LOCAL_MODULE := libjni_pacprocessor
LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
include external/stlport/libstlport.mk
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1cca164..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.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 @@
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 @@
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 @@
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 @@
* navigation bar and touch exploration is not enabled
*/
private boolean canHideNavigationBar() {
- return mHasNavigationBar && !mTouchExplorationEnabled;
+ return mHasNavigationBar
+ && !mAccessibilityManager.isTouchExplorationEnabled();
}
@Override
@@ -5083,10 +5088,10 @@
}
if (pattern.length == 1) {
// One-shot vibration
- mVibrator.vibrate(owningUid, owningPackage, pattern[0]);
+ mVibrator.vibrate(owningUid, owningPackage, pattern[0], AudioManager.STREAM_SYSTEM);
} else {
// Pattern vibration
- mVibrator.vibrate(owningUid, owningPackage, pattern, -1);
+ mVibrator.vibrate(owningUid, owningPackage, pattern, -1, AudioManager.STREAM_SYSTEM);
}
return true;
}
@@ -5246,7 +5251,8 @@
* 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 @@
}
@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/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index d4fa5a7..dce4f581 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -235,8 +235,8 @@
rsnContextSendMessage(mContext, id, data);
}
- native void rsnContextBindRootScript(long con, int script);
- synchronized void nContextBindRootScript(int script) {
+ native void rsnContextBindRootScript(long con, long script);
+ synchronized void nContextBindRootScript(long script) {
validate();
rsnContextBindRootScript(mContext, script);
}
@@ -245,23 +245,23 @@
validate();
rsnContextBindSampler(mContext, sampler, slot);
}
- native void rsnContextBindProgramStore(long con, int pfs);
- synchronized void nContextBindProgramStore(int pfs) {
+ native void rsnContextBindProgramStore(long con, long pfs);
+ synchronized void nContextBindProgramStore(long pfs) {
validate();
rsnContextBindProgramStore(mContext, pfs);
}
- native void rsnContextBindProgramFragment(long con, int pf);
- synchronized void nContextBindProgramFragment(int pf) {
+ native void rsnContextBindProgramFragment(long con, long pf);
+ synchronized void nContextBindProgramFragment(long pf) {
validate();
rsnContextBindProgramFragment(mContext, pf);
}
- native void rsnContextBindProgramVertex(long con, int pv);
- synchronized void nContextBindProgramVertex(int pv) {
+ native void rsnContextBindProgramVertex(long con, long pv);
+ synchronized void nContextBindProgramVertex(long pv) {
validate();
rsnContextBindProgramVertex(mContext, pv);
}
- native void rsnContextBindProgramRaster(long con, int pr);
- synchronized void nContextBindProgramRaster(int pr) {
+ native void rsnContextBindProgramRaster(long con, long pr);
+ synchronized void nContextBindProgramRaster(long pr) {
validate();
rsnContextBindProgramRaster(mContext, pr);
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b547706..671b43de 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -452,7 +452,7 @@
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
assert(dataSize == 5);
- uint32_t elementData[5];
+ uintptr_t elementData[5];
rsaElementGetNativeData((RsContext)con, (RsElement)id, elementData, dataSize);
for(jint i = 0; i < dataSize; i ++) {
@@ -473,7 +473,7 @@
uintptr_t *ids = (uintptr_t*)malloc(dataSize * sizeof(uintptr_t));
const char **names = (const char **)malloc(dataSize * sizeof(const char *));
- size_t *arraySizes = (size_t *)malloc(dataSize * sizeof(size_t));
+ uint32_t *arraySizes = (uint32_t *)malloc(dataSize * sizeof(uint32_t));
rsaElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes, (uint32_t)dataSize);
@@ -1404,35 +1404,35 @@
// ---------------------------------------------------------------------------
static void
-nContextBindRootScript(JNIEnv *_env, jobject _this, jlong con, jint script)
+nContextBindRootScript(JNIEnv *_env, jobject _this, jlong con, jlong script)
{
LOG_API("nContextBindRootScript, con(%p), script(%p)", (RsContext)con, (RsScript)script);
rsContextBindRootScript((RsContext)con, (RsScript)script);
}
static void
-nContextBindProgramStore(JNIEnv *_env, jobject _this, jlong con, jint pfs)
+nContextBindProgramStore(JNIEnv *_env, jobject _this, jlong con, jlong pfs)
{
LOG_API("nContextBindProgramStore, con(%p), pfs(%p)", (RsContext)con, (RsProgramStore)pfs);
rsContextBindProgramStore((RsContext)con, (RsProgramStore)pfs);
}
static void
-nContextBindProgramFragment(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, jlong con, jlong pf)
{
LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", (RsContext)con, (RsProgramFragment)pf);
rsContextBindProgramFragment((RsContext)con, (RsProgramFragment)pf);
}
static void
-nContextBindProgramVertex(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, jlong con, jlong pf)
{
LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", (RsContext)con, (RsProgramVertex)pf);
rsContextBindProgramVertex((RsContext)con, (RsProgramVertex)pf);
}
static void
-nContextBindProgramRaster(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramRaster(JNIEnv *_env, jobject _this, jlong con, jlong pf)
{
LOG_API("nContextBindProgramRaster, con(%p), pf(%p)", (RsContext)con, (RsProgramRaster)pf);
rsContextBindProgramRaster((RsContext)con, (RsProgramRaster)pf);
@@ -1676,11 +1676,11 @@
{"rsnProgramRasterCreate", "(JZI)J", (void*)nProgramRasterCreate },
{"rsnProgramVertexCreate", "(JLjava/lang/String;[Ljava/lang/String;[J)J", (void*)nProgramVertexCreate },
-{"rsnContextBindRootScript", "(JI)V", (void*)nContextBindRootScript },
-{"rsnContextBindProgramStore", "(JI)V", (void*)nContextBindProgramStore },
-{"rsnContextBindProgramFragment", "(JI)V", (void*)nContextBindProgramFragment },
-{"rsnContextBindProgramVertex", "(JI)V", (void*)nContextBindProgramVertex },
-{"rsnContextBindProgramRaster", "(JI)V", (void*)nContextBindProgramRaster },
+{"rsnContextBindRootScript", "(JJ)V", (void*)nContextBindRootScript },
+{"rsnContextBindProgramStore", "(JJ)V", (void*)nContextBindProgramStore },
+{"rsnContextBindProgramFragment", "(JJ)V", (void*)nContextBindProgramFragment },
+{"rsnContextBindProgramVertex", "(JJ)V", (void*)nContextBindProgramVertex },
+{"rsnContextBindProgramRaster", "(JJ)V", (void*)nContextBindProgramRaster },
{"rsnSamplerCreate", "(JIIIIIF)J", (void*)nSamplerCreate },
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.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.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.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 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 @@
private final PackageManager mPackageManager;
- private final IWindowManager mWindowManagerService;
+ private final WindowManagerInternal mWindowManagerService;
private final SecurityPolicy mSecurityPolicy;
@@ -197,9 +199,13 @@
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 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 @@
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 @@
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 @@
* @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 @@
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 @@
}
void onMagnificationStateChanged() {
- notifyClearAccessibilityNodeInfoCacheLocked();
+ notifyClearAccessibilityCacheLocked();
}
private void switchUser(int userId) {
@@ -879,7 +879,7 @@
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 @@
}
}
+ 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 @@
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 @@
* @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 @@
}
}
if (setInputFilter) {
- try {
- mWindowManagerService.setInputFilter(inputFilter);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mWindowManagerService.setInputFilter(inputFilter);
}
}
@@ -1296,6 +1303,7 @@
mInitialized = true;
updateLegacyCapabilities(userState);
updateServicesLocked(userState);
+ updateWindowsForAccessibilityCallback(userState);
updateFilterKeyEventsLocked(userState);
updateTouchExplorationLocked(userState);
updateEnhancedWebAccessibilityLocked(userState);
@@ -1304,6 +1312,43 @@
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 @@
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 @@
}
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 @@
}
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 @@
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.getText().add(message);
- event.setWindowId(mSecurityPolicy.getRetrievalAllowingWindowLocked());
sendAccessibilityEvent(event, mCurrentUserId);
}
}
@@ -1703,6 +1749,19 @@
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 @@
boolean mRequestFilterKeyEvents;
+ boolean mRetrieveInteractiveWindows;
+
int mFetchFlags;
long mNotificationTimeout;
@@ -1748,7 +1809,9 @@
boolean mIsAutomation;
- final Rect mTempBounds = new Rect();
+ final Rect mTempBounds1 = new Rect();
+
+ final Rect mTempBounds2 = new Rect();
final ResolveInfo mResolveInfo;
@@ -1758,6 +1821,9 @@
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 @@
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 @@
}
@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 @@
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 @@
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2034,7 +2158,6 @@
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2079,7 +2202,6 @@
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2123,7 +2245,6 @@
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2167,7 +2288,6 @@
if (resolvedUserId != mCurrentUserId) {
return false;
}
- mSecurityPolicy.enforceCanRetrieveWindowContent(this);
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
resolvedWindowId, action, arguments);
@@ -2365,7 +2485,7 @@
}
mPendingEvents.remove(eventType);
- if (mSecurityPolicy.canRetrieveWindowContent(this)) {
+ if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
event.setConnectionId(mId);
} else {
event.setSource(null);
@@ -2396,12 +2516,69 @@
}
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 @@
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 @@
}
}
+ 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 @@
}
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 @@
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 @@
}
}
+ 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 @@
| 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;
- private int mActiveWindowId;
+ public final List<AccessibilityWindowInfo> mWindows =
+ new ArrayList<AccessibilityWindowInfo>();
+
+ public int mActiveWindowId;
+ public int mFocusedWindowId;
+ public AccessibilityEvent mShowingFocusedWindowEvent;
+
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 @@
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 @@
// 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 @@
}
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 @@
}
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.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.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.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 @@
*
* 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 @@
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 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 @@
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 @@
@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 @@
}
return mTempPointerProperties;
}
-
+
private void transitionToState(int state) {
if (DEBUG_STATE_TRANSITIONS) {
switch (state) {
@@ -1120,15 +1109,10 @@
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/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index e5615c0..e26747c 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -33,6 +33,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.AudioService;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
@@ -42,6 +43,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
@@ -123,6 +125,8 @@
= new ArrayMap<String, ArrayList<Callback>>();
final ArrayMap<IBinder, Callback> mModeWatchers
= new ArrayMap<IBinder, Callback>();
+ final SparseArray<SparseArray<Restriction>> mAudioRestrictions
+ = new SparseArray<SparseArray<Restriction>>();
public final class Callback implements DeathRecipient {
final IAppOpsCallback mCallback;
@@ -553,6 +557,58 @@
}
@Override
+ public int checkAudioOperation(int code, int stream, int uid, String packageName) {
+ synchronized (this) {
+ final int mode = checkRestrictionLocked(code, stream, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ return mode;
+ }
+ }
+ return checkOperation(code, uid, packageName);
+ }
+
+ private int checkRestrictionLocked(int code, int stream, int uid, String packageName) {
+ final SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
+ if (streamRestrictions != null) {
+ final Restriction r = streamRestrictions.get(stream);
+ if (r != null && !r.exceptionPackages.contains(packageName)) {
+ return r.mode;
+ }
+ }
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ @Override
+ public void setAudioRestriction(int code, int stream, int uid, int mode,
+ String[] exceptionPackages) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ synchronized (this) {
+ SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
+ if (streamRestrictions == null) {
+ streamRestrictions = new SparseArray<Restriction>();
+ mAudioRestrictions.put(code, streamRestrictions);
+ }
+ streamRestrictions.remove(stream);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ final Restriction r = new Restriction();
+ r.mode = mode;
+ if (exceptionPackages != null) {
+ final int N = exceptionPackages.length;
+ r.exceptionPackages = new ArraySet<String>(N);
+ for (int i = 0; i < N; i++) {
+ final String pkg = exceptionPackages[i];
+ if (pkg != null) {
+ r.exceptionPackages.add(pkg.trim());
+ }
+ }
+ }
+ streamRestrictions.put(stream, r);
+ }
+ }
+ }
+
+ @Override
public int checkPackage(int uid, String packageName) {
synchronized (this) {
if (getOpsLocked(uid, packageName, true) != null) {
@@ -1048,6 +1104,31 @@
}
}
}
+ if (mAudioRestrictions.size() > 0) {
+ boolean printedHeader = false;
+ for (int o=0; o<mAudioRestrictions.size(); o++) {
+ final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
+ final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o);
+ for (int i=0; i<restrictions.size(); i++) {
+ if (!printedHeader){
+ pw.println(" Audio Restrictions:");
+ printedHeader = true;
+ needSep = true;
+ }
+ final int stream = restrictions.keyAt(i);
+ pw.print(" "); pw.print(op);
+ pw.print(" stream="); pw.print(AudioService.streamToString(stream));
+ Restriction r = restrictions.valueAt(i);
+ pw.print(": mode="); pw.println(r.mode);
+ if (!r.exceptionPackages.isEmpty()) {
+ pw.println(" Exceptions:");
+ for (int j=0; j<r.exceptionPackages.size(); j++) {
+ pw.print(" "); pw.println(r.exceptionPackages.valueAt(j));
+ }
+ }
+ }
+ }
+ }
if (needSep) {
pw.println();
}
@@ -1080,4 +1161,10 @@
}
}
}
+
+ private static final class Restriction {
+ private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
+ int mode;
+ ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
+ }
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 68b779c..d2513d3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2434,7 +2434,9 @@
if (timeout > 0 && iface != null) {
try {
mNetd.addIdleTimer(iface, timeout, type);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ // You shall not crash!
+ loge("Exception in setupDataActivityTracking " + e);
}
}
}
@@ -2451,7 +2453,8 @@
try {
// the call fails silently if no idletimer setup for this interface
mNetd.removeIdleTimer(iface);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ loge("Exception in removeDataActivityTracking " + e);
}
}
}
@@ -4041,6 +4044,14 @@
*/
private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
+ /**
+ * The mobile network is provisioning
+ */
+ private static final int CMP_RESULT_CODE_IS_PROVISIONING = 6;
+
+ private AtomicBoolean mIsProvisioningNetwork = new AtomicBoolean(false);
+ private AtomicBoolean mIsStartingProvisioning = new AtomicBoolean(false);
+
private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
@Override
@@ -4111,11 +4122,25 @@
setProvNotificationVisible(true,
ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(),
url);
+ // Mark that we've got a provisioning network and
+ // Disable Mobile Data until user actually starts provisioning.
+ mIsProvisioningNetwork.set(true);
+ MobileDataStateTracker mdst = (MobileDataStateTracker)
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ mdst.setInternalDataEnable(false);
} else {
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
}
break;
}
+ case CMP_RESULT_CODE_IS_PROVISIONING: {
+ // FIXME: Need to know when provisioning is done. Probably we can
+ // check the completion status if successful we're done if we
+ // "timedout" or still connected to provisioning APN turn off data?
+ if (DBG) log("CheckMp.onComplete: provisioning started");
+ mIsStartingProvisioning.set(false);
+ break;
+ }
default: {
loge("CheckMp.onComplete: ignore unexpected result=" + result);
break;
@@ -4265,6 +4290,12 @@
return result;
}
+ if (mCs.mIsStartingProvisioning.get()) {
+ result = CMP_RESULT_CODE_IS_PROVISIONING;
+ log("isMobileOk: X is provisioning result=" + result);
+ return result;
+ }
+
// See if we've already determined we've got a provisioning connection,
// if so we don't need to do anything active.
MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
@@ -4602,19 +4633,20 @@
};
private void handleMobileProvisioningAction(String url) {
- // Notication mark notification as not visible
+ // Mark notification as not visible
setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
// If provisioning network handle as a special case,
// otherwise launch browser with the intent directly.
- NetworkInfo ni = getProvisioningNetworkInfo();
- if ((ni != null) && ni.isConnectedToProvisioningNetwork()) {
- if (DBG) log("handleMobileProvisioningAction: on provisioning network");
+ if (mIsProvisioningNetwork.get()) {
+ if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch");
+ mIsStartingProvisioning.set(true);
MobileDataStateTracker mdst = (MobileDataStateTracker)
mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
mdst.enableMobileProvisioning(url);
} else {
- if (DBG) log("handleMobileProvisioningAction: on default network");
+ if (DBG) log("handleMobileProvisioningAction: not prov network");
// Check for apps that can handle provisioning first
Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 28eb948..52f9aa9 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -84,24 +84,27 @@
private final long mStartTime;
private final long[] mPattern;
private final int mRepeat;
+ private final int mStreamHint;
private final int mUid;
private final String mPackageName;
- Vibration(IBinder token, long millis, int uid, String packageName) {
- this(token, millis, null, 0, uid, packageName);
+ Vibration(IBinder token, long millis, int streamHint, int uid, String packageName) {
+ this(token, millis, null, 0, streamHint, uid, packageName);
}
- Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
- this(token, 0, pattern, repeat, uid, packageName);
+ Vibration(IBinder token, long[] pattern, int repeat, int streamHint, int uid,
+ String packageName) {
+ this(token, 0, pattern, repeat, streamHint, uid, packageName);
}
private Vibration(IBinder token, long millis, long[] pattern,
- int repeat, int uid, String packageName) {
+ int repeat, int streamHint, int uid, String packageName) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
+ mStreamHint = streamHint;
mUid = uid;
mPackageName = packageName;
}
@@ -191,7 +194,8 @@
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
- public void vibrate(int uid, String packageName, long milliseconds, IBinder token) {
+ public void vibrate(int uid, String packageName, long milliseconds, int streamHint,
+ IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
@@ -207,7 +211,7 @@
return;
}
- Vibration vib = new Vibration(token, milliseconds, uid, packageName);
+ Vibration vib = new Vibration(token, milliseconds, streamHint, uid, packageName);
final long ident = Binder.clearCallingIdentity();
try {
@@ -233,7 +237,7 @@
}
public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
- IBinder token) {
+ int streamHint, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
@@ -258,7 +262,7 @@
return;
}
- Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
+ Vibration vib = new Vibration(token, pattern, repeat, streamHint, uid, packageName);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
@@ -342,8 +346,12 @@
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
try {
- int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+ int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
+ vib.mStreamHint, vib.mUid, vib.mPackageName);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+ }
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1345cfd..033b967 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2087,10 +2087,12 @@
// Sanity check: if the service listed for the app is not one
// we actually are maintaining, just let it drop.
- if (smap.mServicesByName.get(sr.name) != sr) {
- ServiceRecord cur = smap.mServicesByName.get(sr.name);
- Slog.wtf(TAG, "Service " + sr + " in process " + app
- + " not same as in map: " + cur);
+ final ServiceRecord curRec = smap.mServicesByName.get(sr.name);
+ if (curRec != sr) {
+ if (curRec != null) {
+ Slog.wtf(TAG, "Service " + sr + " in process " + app
+ + " not same as in map: " + curRec);
+ }
continue;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 09ec4f6..9783a66 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1776,6 +1776,7 @@
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = sourceTask.stack;
+ targetStack.moveToFront();
mWindowManager.moveTaskToTop(sourceTask.taskId);
if (!addingToTask &&
(launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
@@ -1827,6 +1828,7 @@
// of a new task... just put it in the top task, though these days
// this case should never happen.
targetStack = adjustStackFocus(r);
+ targetStack.moveToFront();
ActivityRecord prev = targetStack.topActivity();
r.setTask(prev != null ? prev.task
: targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3414daf..fa4a9d1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -135,14 +135,15 @@
enforceCallingPermission();
synchronized (mStats) {
mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging,
- SystemClock.elapsedRealtime());
+ SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
}
}
public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime());
+ mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime(),
+ SystemClock.uptimeMillis());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e2226aa..7c2de8b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -206,10 +206,6 @@
final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
private int mZenMode;
- private int mPreZenAlarmVolume = -1;
- private int mPreZenRingerMode = -1;
- private boolean mZenMutingAlarm;
- private boolean mZenMutingRinger;
// temporary, until we update apps to provide metadata
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.dialer",
@@ -1122,9 +1118,6 @@
private final Uri ZEN_MODE
= Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
- private final Uri MODE_RINGER
- = Settings.Global.getUriFor(Settings.Global.MODE_RINGER);
-
SettingsObserver(Handler handler) {
super(handler);
}
@@ -1137,8 +1130,6 @@
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ZEN_MODE,
false, this);
- resolver.registerContentObserver(MODE_RINGER,
- false, this);
update(null);
}
@@ -1162,9 +1153,6 @@
if (ZEN_MODE.equals(uri)) {
updateZenMode();
}
- if (MODE_RINGER.equals(uri)) {
- updateRingerMode();
- }
}
}
@@ -1230,7 +1218,6 @@
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisableNotificationAlerts = true;
}
- updateRingerMode();
updateZenMode();
// register for various Intents
@@ -1694,10 +1681,6 @@
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
- pw.println(" mPreZenAlarmVolume=" + mPreZenAlarmVolume);
- pw.println(" mPreZenRingerMode=" + mPreZenRingerMode);
- pw.println(" mZenMutingAlarm=" + mZenMutingAlarm);
- pw.println(" mZenMutingRinger=" + mZenMutingRinger);
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -2023,7 +2006,7 @@
useDefaultVibrate ? mDefaultVibrationPattern
: mFallbackVibrationPattern,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1);
+ ? 0: -1, notification.audioStreamType);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2033,7 +2016,7 @@
mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
notification.vibrate,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1);
+ ? 0: -1, notification.audioStreamType);
}
}
}
@@ -2552,17 +2535,6 @@
}
}
- private void updateRingerMode() {
- final int ringerMode = Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.MODE_RINGER, -1);
- final boolean nonSilentRingerMode = ringerMode == AudioManager.RINGER_MODE_NORMAL
- || ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- if (mZenMode != Settings.Global.ZEN_MODE_OFF && nonSilentRingerMode) {
- Settings.Global.putInt(getContext().getContentResolver(),
- Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- }
- }
-
private void updateZenMode() {
final int mode = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -2572,62 +2544,31 @@
Settings.Global.zenModeToString(mode)));
}
mZenMode = mode;
- if (mAudioManager != null) {
- // call audio
- final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
- if (muteCalls) {
- if (!mZenMutingRinger) {
- if (DBG) Slog.d(TAG, "Muting STREAM_RING");
- mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
- mZenMutingRinger = true;
- }
- // calls vibrate if ringer mode = vibrate, so set the ringer mode as well
- final int ringerMode = mAudioManager.getRingerMode();
- if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
- if (DBG) Slog.d(TAG, "Saving ringer mode of " + ringerMode);
- mPreZenRingerMode = ringerMode;
- mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
- }
- } else {
- if (mZenMutingRinger) {
- if (DBG) Slog.d(TAG, "Unmuting STREAM_RING");
- mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
- mZenMutingRinger = false;
- }
- if (mPreZenRingerMode != -1) {
- if (DBG) Slog.d(TAG, "Restoring ringer mode to " + mPreZenRingerMode);
- mAudioManager.setRingerMode(mPreZenRingerMode);
- mPreZenRingerMode = -1;
- }
- }
- // alarm audio
- final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
- if (muteAlarms) {
- if (!mZenMutingAlarm) {
- if (DBG) Slog.d(TAG, "Muting STREAM_ALARM");
- mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
- mZenMutingAlarm = true;
- }
- // alarms don't simply respect mute, so set the volume as well
- final int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
- if (volume != 0) {
- if (DBG) Slog.d(TAG, "Saving STREAM_ALARM volume of " + volume);
- mPreZenAlarmVolume = volume;
- mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0);
- }
- } else {
- if (mZenMutingAlarm) {
- if (DBG) Slog.d(TAG, "Unmuting STREAM_ALARM");
- mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
- mZenMutingAlarm = false;
- }
- if (mPreZenAlarmVolume != -1) {
- if (DBG) Slog.d(TAG, "Restoring STREAM_ALARM volume to " + mPreZenAlarmVolume);
- mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mPreZenAlarmVolume, 0);
- mPreZenAlarmVolume = -1;
- }
- }
- }
+
+ final String[] exceptionPackages = null; // none (for now)
+
+ // call restrictions
+ final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // alarm restrictions
+ final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
+ muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
+ muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // restrict vibrations with no hints
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
+ (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
}
private void updateRelatedUserCache(Context context) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b7e367b..ae1cfab 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -406,4 +406,8 @@
return execute(builder.toString());
}
+
+ public boolean restoreconData() {
+ return (execute("restorecondata") == 0);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cbcf408..cb4cf98 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -302,12 +302,15 @@
// This is the object monitoring the privileged system app dir.
final FileObserver mPrivilegedInstallObserver;
- // This is the object monitoring the system app dir.
+ // This is the object monitoring the vendor app dir.
final FileObserver mVendorInstallObserver;
// This is the object monitoring the vendor overlay package dir.
final FileObserver mVendorOverlayInstallObserver;
+ // This is the object monitoring the OEM app dir.
+ final FileObserver mOemInstallObserver;
+
// This is the object monitoring mAppInstallDir.
final FileObserver mAppInstallObserver;
@@ -1157,7 +1160,12 @@
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
- readPermissions();
+ // Read permissions and features from system
+ readPermissions(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "permissions"), false);
+ // Only read features from OEM
+ readPermissions(Environment.buildPath(
+ Environment.getOemDirectory(), "etc", "permissions"), true);
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
@@ -1343,6 +1351,14 @@
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+ // Collect all OEM packages.
+ File oemAppDir = new File(Environment.getOemDirectory(), "app");
+ mOemInstallObserver = new AppDirObserver(
+ oemAppDir.getPath(), OBSERVER_EVENTS, true, false);
+ mOemInstallObserver.startWatching();
+ scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
@@ -1493,6 +1509,13 @@
// can downgrade to reader
mSettings.writeLPr();
+ if (SELinuxMMAC.shouldRestorecon()) {
+ Slog.i(TAG, "Relabeling of /data/data and /data/user issued.");
+ if (mInstaller.restoreconData()) {
+ SELinuxMMAC.setRestoreconDone();
+ }
+ }
+
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
@@ -1581,9 +1604,8 @@
mSettings.removePackageLPw(ps.name);
}
- void readPermissions() {
+ void readPermissions(File libraryDir, boolean onlyFeatures) {
// Read permissions from .../etc/permission directory.
- File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
return;
@@ -1609,16 +1631,16 @@
continue;
}
- readPermissionsFromXml(f);
+ readPermissionsFromXml(f, onlyFeatures);
}
// Read permissions from .../etc/permissions/platform.xml last so it will take precedence
final File permFile = new File(Environment.getRootDirectory(),
"etc/permissions/platform.xml");
- readPermissionsFromXml(permFile);
+ readPermissionsFromXml(permFile, onlyFeatures);
}
- private void readPermissionsFromXml(File permFile) {
+ private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
@@ -1640,7 +1662,7 @@
}
String name = parser.getName();
- if ("group".equals(name)) {
+ if ("group".equals(name) && !onlyFeatures) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr);
@@ -1652,7 +1674,7 @@
XmlUtils.skipCurrentTag(parser);
continue;
- } else if ("permission".equals(name)) {
+ } else if ("permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name at "
@@ -1663,7 +1685,7 @@
perm = perm.intern();
readPermission(parser, perm);
- } else if ("assign-permission".equals(name)) {
+ } else if ("assign-permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name at "
@@ -1695,7 +1717,7 @@
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
- } else if ("library".equals(name)) {
+ } else if ("library".equals(name) && !onlyFeatures) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 6e12e41..d70c725 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -25,10 +25,15 @@
import com.android.internal.util.XmlUtils;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
@@ -59,6 +64,13 @@
new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
null};
+ // Location of seapp_contexts policy file.
+ private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts";
+
+ // Stores the hash of the last used seapp_contexts file.
+ private static final String SEAPP_HASH_FILE =
+ Environment.getDataDirectory().toString() + "/system/seapp_hash";
+
// Signature policy stanzas
static class Policy {
private String seinfo;
@@ -102,7 +114,6 @@
/**
* Parses an MMAC install policy from a predefined list of locations.
- * @param none
* @return boolean indicating whether an install policy was correctly parsed.
*/
public static boolean readInstallPolicy() {
@@ -112,7 +123,7 @@
/**
* Parses an MMAC install policy given as an argument.
- * @param File object representing the path of the policy.
+ * @param policyFile object representing the path of the policy.
* @return boolean indicating whether the install policy was correctly parsed.
*/
public static boolean readInstallPolicy(File policyFile) {
@@ -344,8 +355,7 @@
/**
* Labels a package based on an seinfo tag from install policy.
* The label is attached to the ApplicationInfo instance of the package.
- * @param PackageParser.Package object representing the package
- * to labeled.
+ * @param pkg object representing the package to be labeled.
* @return boolean which determines whether a non null seinfo label
* was assigned to the package. A null value simply meaning that
* no policy matched.
@@ -390,4 +400,89 @@
return (sDefaultSeinfo != null);
}
+
+ /**
+ * Determines if a recursive restorecon on /data/data and /data/user is needed.
+ * It does this by comparing the SHA-1 of the seapp_contexts file against the
+ * stored hash at /data/system/seapp_hash.
+ *
+ * @return Returns true if the restorecon should occur or false otherwise.
+ */
+ public static boolean shouldRestorecon() {
+ // Any error with the seapp_contexts file should be fatal
+ byte[] currentHash = null;
+ try {
+ currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
+ return false;
+ }
+
+ // Push past any error with the stored hash file
+ byte[] storedHash = null;
+ try {
+ storedHash = IoUtils.readFileAsByteArray(SEAPP_HASH_FILE);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Error opening " + SEAPP_HASH_FILE + ". Assuming first boot.");
+ }
+
+ return (storedHash == null || !MessageDigest.isEqual(storedHash, currentHash));
+ }
+
+ /**
+ * Stores the SHA-1 of the seapp_contexts to /data/system/seapp_hash.
+ */
+ public static void setRestoreconDone() {
+ try {
+ final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ dumpHash(new File(SEAPP_HASH_FILE), currentHash);
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
+ }
+ }
+
+ /**
+ * Dump the contents of a byte array to a specified file.
+ *
+ * @param file The file that receives the byte array content.
+ * @param content A byte array that will be written to the specified file.
+ * @throws IOException if any failed I/O operation occured.
+ * Included is the failure to atomically rename the tmp
+ * file used in the process.
+ */
+ private static void dumpHash(File file, byte[] content) throws IOException {
+ FileOutputStream fos = null;
+ File tmp = null;
+ try {
+ tmp = File.createTempFile("seapp_hash", ".journal", file.getParentFile());
+ tmp.setReadable(true);
+ fos = new FileOutputStream(tmp);
+ fos.write(content);
+ fos.getFD().sync();
+ if (!tmp.renameTo(file)) {
+ throw new IOException("Failure renaming " + file.getCanonicalPath());
+ }
+ } finally {
+ if (tmp != null) {
+ tmp.delete();
+ }
+ IoUtils.closeQuietly(fos);
+ }
+ }
+
+ /**
+ * Return the SHA-1 of a file.
+ *
+ * @param file The path to the file given as a string.
+ * @return Returns the SHA-1 of the file as a byte array.
+ * @throws IOException if any failed I/O operations occured.
+ */
+ private static byte[] returnHash(String file) throws IOException {
+ try {
+ final byte[] contents = IoUtils.readFileAsByteArray(file);
+ return MessageDigest.getInstance("SHA-1").digest(contents);
+ } catch (NoSuchAlgorithmException nsae) {
+ throw new RuntimeException(nsae); // impossible
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/AutomaticBrightnessController.java b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
index 3ca628a..807262a 100644
--- a/services/core/java/com/android/server/power/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
@@ -200,8 +200,7 @@
public void updatePowerState(DisplayPowerRequest request) {
if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
- || setLightSensorEnabled(request.useAutoBrightness
- && request.wantScreenOnNormal())) {
+ || setLightSensorEnabled(request.wantLightSensorEnabled())) {
updateAutoBrightness(false /*sendUpdate*/);
}
}
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 @@
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 a4f960e7..adfb2bd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -103,7 +103,6 @@
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;
@@ -418,7 +417,7 @@
IInputMethodManager mInputMethodManager;
- DisplayMagnifier mDisplayMagnifier;
+ AccessibilityController mAccessibilityController;
final SurfaceSession mFxSession;
Watermark mWatermark;
@@ -2439,9 +2438,9 @@
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 +2758,12 @@
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 +2998,9 @@
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 +3143,6 @@
}
}
- @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 +3322,8 @@
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 +4179,9 @@
}
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 +4201,9 @@
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 +4218,9 @@
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 +5198,6 @@
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 +6013,9 @@
}
//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 +6922,6 @@
}
}
- @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 +8101,9 @@
}
//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 +9736,11 @@
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 +10854,100 @@
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 @@
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 @@
&& 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 @@
}
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);
}
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index 90b6abc..b12ed94 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -18,6 +18,7 @@
import junit.framework.TestCase;
+import android.media.AudioManager;
import android.os.Binder;
import android.os.IVibratorService;
import android.os.Process;
@@ -47,7 +48,8 @@
*/
public void testVibrate() throws RemoteException {
try {
- mVibratorService.vibrate(Process.myUid(), null, 2000, new Binder());
+ mVibratorService.vibrate(Process.myUid(), null, 2000, AudioManager.STREAM_ALARM,
+ new Binder());
fail("vibrate did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
@@ -63,7 +65,8 @@
*/
public void testVibratePattern() throws RemoteException {
try {
- mVibratorService.vibratePattern(Process.myUid(), null, new long[] {0}, 0, new Binder());
+ mVibratorService.vibratePattern(Process.myUid(), null, new long[] {0}, 0,
+ AudioManager.STREAM_ALARM, new Binder());
fail("vibratePattern did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected