diff options
121 files changed, 6605 insertions, 3176 deletions
diff --git a/Android.mk b/Android.mk index a69198729394..d279704f52f8 100644 --- a/Android.mk +++ b/Android.mk @@ -196,7 +196,6 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IAssetAtlas.aidl \ - core/java/android/view/IMagnificationCallbacks.aidl \ core/java/android/view/IInputFilter.aidl \ core/java/android/view/IInputFilterHost.aidl \ core/java/android/view/IOnKeyguardExitResult.aidl \ @@ -399,6 +398,7 @@ aidl_files := \ frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.aidl \ frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl \ frameworks/base/core/java/android/view/accessibility/AccessibilityRecord.aidl \ + frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl \ frameworks/base/core/java/android/view/KeyEvent.aidl \ frameworks/base/core/java/android/view/MotionEvent.aidl \ frameworks/base/core/java/android/view/Surface.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index 448b03dc273e..4f0e603331f4 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -187,6 +187,8 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_in $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrintClient.*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/IMedia*) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/view/IMagnificationCallbacks*) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/api/current.txt b/api/current.txt index e72354b1852b..e41b3feb2eff 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2328,6 +2328,7 @@ package android.accessibilityservice { ctor public AccessibilityService(); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); + method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public final android.os.IBinder onBind(android.content.Intent); method protected boolean onGesture(int); @@ -2393,6 +2394,7 @@ package android.accessibilityservice { field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8 field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20 field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4 + field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40 field public int eventTypes; field public int feedbackType; field public int flags; @@ -4600,6 +4602,7 @@ package android.app { method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException; method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); + method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method public boolean injectInputEvent(android.view.InputEvent, boolean); method public final boolean performGlobalAction(int); method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener); @@ -19546,8 +19549,10 @@ package android.os { 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 @@ package android.view.accessibility { field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10 field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000 field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000 + field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000 field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800 field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20 } @@ -30461,6 +30467,7 @@ package android.view.accessibility { method public int getTextSelectionEnd(); method public int getTextSelectionStart(); method public java.lang.String getViewIdResourceName(); + method public android.view.accessibility.AccessibilityWindowInfo getWindow(); method public int getWindowId(); method public boolean isAccessibilityFocused(); method public boolean isCheckable(); @@ -30607,6 +30614,7 @@ package android.view.accessibility { method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); method public boolean performAction(int, int, android.os.Bundle); + field public static final int HOST_VIEW_ID = -1; // 0xffffffff } public class AccessibilityRecord { @@ -30658,6 +30666,28 @@ package android.view.accessibility { method public void setToIndex(int); } + public final class AccessibilityWindowInfo implements android.os.Parcelable { + method public int describeContents(); + method public void getBoundsInScreen(android.graphics.Rect); + method public android.view.accessibility.AccessibilityWindowInfo getChild(int); + method public int getChildCount(); + method public int getId(); + method public int getLayer(); + method public android.view.accessibility.AccessibilityWindowInfo getParent(); + method public android.view.accessibility.AccessibilityNodeInfo getRoot(); + method public int getType(); + method public boolean isActive(); + method public boolean isFocused(); + method public static android.view.accessibility.AccessibilityWindowInfo obtain(); + method public static android.view.accessibility.AccessibilityWindowInfo obtain(android.view.accessibility.AccessibilityWindowInfo); + method public void recycle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TYPE_APPLICATION = 1; // 0x1 + field public static final int TYPE_INPUT_METHOD = 2; // 0x2 + field public static final int TYPE_SYSTEM = 3; // 0x3 + } + public class CaptioningManager { method public void addCaptioningChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener); method public final float getFontScale(); diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk index b9afe4024f63..cb7e1a0f37ed 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 @@ LOCAL_SHARED_LIBRARIES := \ 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 1e3d5bea9571..b01d92cd5e4f 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -23,14 +23,18 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.HandlerCaller; +import java.util.List; + /** * An accessibility service runs in the background and receives callbacks by the system * when {@link AccessibilityEvent}s are fired. Such events denote some state transition @@ -180,28 +184,37 @@ import com.android.internal.os.HandlerCaller; * event generation has settled down.</p> * <h3>Event types</h3> * <ul> - * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED} - * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} - * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED} - * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED} - * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} - * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} - * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} - * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START} - * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} - * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} - * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} - * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED} - * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} - * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} + * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li> + * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li> + * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li> + * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li> + * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li> + * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li> + * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li> + * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li> + * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li> + * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li> + * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li> + * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li> + * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li> * </ul> * <h3>Feedback types</h3> * <ul> - * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} - * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC} - * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} - * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL} - * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC} + * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> + * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li> + * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> + * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li> + * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li> + * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li> * </ul> * @see AccessibilityEvent * @see AccessibilityServiceInfo @@ -443,8 +456,41 @@ public abstract class AccessibilityService extends Service { } /** + * Gets the windows on the screen. This method returns only the windows + * that a sighted user can interact with, as opposed to all windows. + * For example, if there is a modal dialog shown and the user cannot touch + * anything behind it, then only the modal window will be reported + * (assuming it is the top one). For convenience the returned windows + * are ordered in a descending layer order, which is the windows that + * are higher in the Z-order are reported first. + * <p> + * <strong>Note:</strong> In order to access the windows your service has + * to declare the capability to retrieve window content by setting the + * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} + * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. + * Also the service has to opt-in to retrieve the interactive windows by + * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} + * flag. + * </p> + * + * @return The windows if there are windows and the service is can retrieve + * them, otherwise an empty list. + */ + public List<AccessibilityWindowInfo> getWindows() { + return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); + } + + /** * Gets the root node in the currently active window if this service - * can retrieve window content. + * can retrieve window content. The active window is the one that the user + * is currently touching or the window with input focus, if the user is not + * touching any window. + * <p> + * <strong>Note:</strong> In order to access the root node your service has + * to declare the capability to retrieve window content by setting the + * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} + * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. + * </p> * * @return The root node if this service can retrieve window content. */ @@ -584,12 +630,13 @@ public abstract class AccessibilityService extends Service { static final int NO_ID = -1; - private static final int DO_SET_SET_CONNECTION = 10; - private static final int DO_ON_INTERRUPT = 20; - private static final int DO_ON_ACCESSIBILITY_EVENT = 30; - private static final int DO_ON_GESTURE = 40; - private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50; - private static final int DO_ON_KEY_EVENT = 60; + private static final int DO_SET_SET_CONNECTION = 1; + private static final int DO_ON_INTERRUPT = 2; + private static final int DO_ON_ACCESSIBILITY_EVENT = 3; + private static final int DO_ON_GESTURE = 4; + private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; + private static final int DO_ON_KEY_EVENT = 6; + private static final int DO_ON_WINDOWS_CHANGED = 7; private final HandlerCaller mCaller; @@ -624,8 +671,8 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } - public void clearAccessibilityNodeInfoCache() { - Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); + public void clearAccessibilityCache() { + Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); mCaller.sendMessage(message); } @@ -635,6 +682,13 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } + @Override + public void onWindowsChanged(int[] windowIds) { + Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds); + mCaller.sendMessage(message); + } + + @Override public void executeMessage(Message message) { switch (message.what) { case DO_ON_ACCESSIBILITY_EVENT: { @@ -642,12 +696,19 @@ public abstract class AccessibilityService extends Service { if (event != null) { AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); mCallback.onAccessibilityEvent(event); - event.recycle(); + // Make sure the event is recycled. + try { + event.recycle(); + } catch (IllegalStateException ise) { + /* ignore - best effort */ + } } } return; + case DO_ON_INTERRUPT: { mCallback.onInterrupt(); } return; + case DO_SET_SET_CONNECTION: { mConnectionId = message.arg1; IAccessibilityServiceConnection connection = @@ -658,18 +719,22 @@ public abstract class AccessibilityService extends Service { mCallback.onSetConnectionId(mConnectionId); mCallback.onServiceConnected(); } else { - AccessibilityInteractionClient.getInstance().removeConnection(mConnectionId); + AccessibilityInteractionClient.getInstance().removeConnection( + mConnectionId); AccessibilityInteractionClient.getInstance().clearCache(); mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID); } } return; + case DO_ON_GESTURE: { final int gestureId = message.arg1; mCallback.onGesture(gestureId); } return; - case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: { + + case DO_CLEAR_ACCESSIBILITY_CACHE: { AccessibilityInteractionClient.getInstance().clearCache(); } return; + case DO_ON_KEY_EVENT: { KeyEvent event = (KeyEvent) message.obj; try { @@ -685,9 +750,40 @@ public abstract class AccessibilityService extends Service { } } } finally { - event.recycle(); + // Make sure the event is recycled. + try { + event.recycle(); + } catch (IllegalStateException ise) { + /* ignore - best effort */ + } } } return; + + case DO_ON_WINDOWS_CHANGED: { + final int[] windowIds = (int[]) message.obj; + + // Update the cached windows first. + // NOTE: The cache will hold on to the windows so do not recycle. + if (windowIds != null) { + AccessibilityInteractionClient.getInstance().removeWindows(windowIds); + } + + // Let the client know the windows changed. + AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_WINDOWS_CHANGED); + event.setEventTime(SystemClock.uptimeMillis()); + event.setSealed(true); + + mCallback.onAccessibilityEvent(event); + + // Make sure the event is recycled. + try { + event.recycle(); + } catch (IllegalStateException ise) { + /* ignore - best effort */ + } + } break; + default : Log.w(LOG_TAG, "Unknown message type " + message.what); } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index bdc4fdde80f4..4f9ba59c6d9d 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -284,6 +284,27 @@ public class AccessibilityServiceInfo implements Parcelable { public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020; /** + * This flag indicates to the system that the accessibility service wants + * to access content of all interactive windows. An interactive window is a + * window that can be touched by a sighted user when explore by touch is not + * enabled. If this flag is not set your service will not receive + * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED} + * events, calling AccessibilityService{@link AccessibilityService#getWindows() + * AccessibilityService.getWindows()} will return an empty list, and {@link + * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will + * return null. + * <p> + * Services that want to set this flag have to declare the capability + * to retrieve window content in their meta-data by setting the attribute + * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to + * true, otherwise this flag will be ignored. For how to declare the meta-data + * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}. + * </p> + * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent + */ + public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040; + + /** * The event types an {@link AccessibilityService} is interested in. * <p> * <strong>Can be dynamically set at runtime.</strong> @@ -302,6 +323,15 @@ public class AccessibilityServiceInfo implements Parcelable { * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START + * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END + * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT + * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START + * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY + * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED */ public int eventTypes; @@ -354,6 +384,7 @@ public class AccessibilityServiceInfo implements Parcelable { * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY * @see #FLAG_REQUEST_FILTER_KEY_EVENTS * @see #FLAG_REPORT_VIEW_IDS + * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS */ public int flags; @@ -449,7 +480,7 @@ public class AccessibilityServiceInfo implements Parcelable { com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType, 0); notificationTimeout = asAttributes.getInt( - com.android.internal.R.styleable.AccessibilityService_notificationTimeout, + com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 0); flags = asAttributes.getInt( com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); @@ -861,6 +892,8 @@ public class AccessibilityServiceInfo implements Parcelable { return "FLAG_REPORT_VIEW_IDS"; case FLAG_REQUEST_FILTER_KEY_EVENTS: return "FLAG_REQUEST_FILTER_KEY_EVENTS"; + case FLAG_RETRIEVE_INTERACTIVE_WINDOWS: + return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS"; default: return null; } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index c5e3d43a527a..edd872712eb3 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.accessibilityservice.IAccessibilityServiceConnection; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityWindowInfo; import android.view.KeyEvent; /** @@ -35,7 +36,9 @@ import android.view.KeyEvent; void onGesture(int gesture); - void clearAccessibilityNodeInfoCache(); + void clearAccessibilityCache(); void onKeyEvent(in KeyEvent event, int sequence); + + void onWindowsChanged(in int[] changedWindowIds); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 3df06b505675..5f7a17d33bd1 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -21,6 +21,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import android.view.accessibility.AccessibilityWindowInfo; /** * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService. @@ -53,6 +54,10 @@ interface IAccessibilityServiceConnection { int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); + AccessibilityWindowInfo getWindow(int windowId); + + List<AccessibilityWindowInfo> getWindows(); + AccessibilityServiceInfo getServiceInfo(); boolean performGlobalAction(int action); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 079cf7a210c9..b616c1e59827 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -780,6 +780,25 @@ public class AppOpsManager { } } + /** + * 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 @@ public class AppOpsManager { } /** + * 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 498fa4264fe5..354a19f52fcd 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -36,9 +36,11 @@ import android.view.Surface; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeoutException; /** @@ -269,10 +271,10 @@ public final class UiAutomation { * @param action The action to perform. * @return Whether the action was successfully performed. * - * @see AccessibilityService#GLOBAL_ACTION_BACK - * @see AccessibilityService#GLOBAL_ACTION_HOME - * @see AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS - * @see AccessibilityService#GLOBAL_ACTION_RECENTS + * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK + * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME + * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS + * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_RECENTS */ public final boolean performGlobalAction(int action) { final IAccessibilityServiceConnection connection; @@ -346,6 +348,33 @@ public final class UiAutomation { } /** + * Gets the windows on the screen. This method returns only the windows + * that a sighted user can interact with, as opposed to all windows. + * For example, if there is a modal dialog shown and the user cannot touch + * anything behind it, then only the modal window will be reported + * (assuming it is the top one). For convenience the returned windows + * are ordered in a descending layer order, which is the windows that + * are higher in the Z-order are reported first. + * <p> + * <strong>Note:</strong> In order to access the windows you have to opt-in + * to retrieve the interactive windows by setting the + * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag. + * </p> + * + * @return The windows if there are windows such, otherwise an empty list. + */ + public List<AccessibilityWindowInfo> getWindows() { + final int connectionId; + synchronized (mLock) { + throwIfNotConnectedLocked(); + connectionId = mConnectionId; + } + // Calling out without a lock held. + return AccessibilityInteractionClient.getInstance() + .getWindows(connectionId); + } + + /** * Gets the root {@link AccessibilityNodeInfo} in the active window. * * @return The root info. @@ -632,7 +661,7 @@ public final class UiAutomation { * potentially undesirable actions such as calling 911 or posting on public forums etc. * * @param enable whether to run in a "monkey" mode or not. Default is not. - * @see {@link ActivityManager#isUserAMonkey()} + * @see {@link android.app.ActivityManager#isUserAMonkey()} */ public void setRunAsMonkey(boolean enable) { synchronized (mLock) { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 38a71aaa25f9..a4374b86f1a3 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -19,7 +19,9 @@ package android.bluetooth; 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 @@ public final class BluetoothAdapter { "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 @@ public final class BluetoothAdapter { "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 @@ public final class BluetoothAdapter { /** 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>(); + mHandler = new Handler(Looper.getMainLooper()); } /** @@ -474,6 +476,7 @@ public final class BluetoothAdapter { /** * 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 @@ public final class BluetoothAdapter { } } + /** + * 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 @@ public final class BluetoothAdapter { // 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 @@ public final class BluetoothAdapter { /** * 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { * @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 @@ public final class BluetoothAdapter { // 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 a97b0a806e38..df2c25629a3b 100644 --- a/core/java/android/bluetooth/BluetoothAdvScanData.java +++ b/core/java/android/bluetooth/BluetoothAdvScanData.java @@ -77,6 +77,7 @@ public final class BluetoothAdvScanData { 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 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.setAdvServiceData(serviceData); } catch (RemoteException e) { + Log.e(TAG, "Unable to set service data.", e); return false; } } @@ -103,6 +105,7 @@ public final class BluetoothAdvScanData { try { return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); } catch (RemoteException e) { + Log.e(TAG, "Unable to get service uuids.", e); return null; } } @@ -115,6 +118,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.getAdvManufacturerData(); } catch (RemoteException e) { + Log.e(TAG, "Unable to get manufacturer data.", e); return null; } } @@ -127,6 +131,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.getAdvServiceData(); } catch (RemoteException e) { + Log.e(TAG, "Unable to get service data.", e); return null; } } @@ -140,7 +145,7 @@ public final class BluetoothAdvScanData { 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 e3820a2e2f2e..39305b05c5fc 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 @@ public final class BluetoothGatt implements BluetoothProfile { 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 e3563fcc3a77..7c69a066b465 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,4 +63,5 @@ interface IBluetoothGattCallback { 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 6e2a099c1000..35c86e7f3501 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -45,6 +45,7 @@ import java.io.IOException; 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 @@ public class Camera { 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 @@ public class Camera { return; } - mMap.put(key, value); + put(key, value); } /** @@ -2242,7 +2253,18 @@ public class Camera { * @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 e3a3830ce8e3..0c0dfe9c2ad9 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -853,13 +853,21 @@ public final class InputManager { 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 @@ public final class InputManager { } } - /** - * @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 6c610462319f..10b5d0ba3329 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -183,8 +183,10 @@ public class EthernetDataTracker extends BaseNetworkStateTracker { 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 3c3d8ec675b4..a470e8885e4f 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -561,6 +561,17 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { 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 346f5de733a5..76b0dc472eb5 100644 --- a/core/java/android/os/BatteryProperty.java +++ b/core/java/android/os/BatteryProperty.java @@ -29,6 +29,7 @@ public class BatteryProperty implements Parcelable { 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 7db4ac20067b..b0d94d59a70e 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -570,13 +570,15 @@ public abstract class BatteryStats implements Parcelable { 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 class BatteryStats implements Parcelable { 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 54e2c0b681cb..e96398a76ef2 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -41,6 +41,7 @@ public class Environment { 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 class Environment { 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 @@ public class Environment { } /** - * 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 456ffb188807..4854bc0409c5 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -20,8 +20,8 @@ package android.os; 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 af90bdbca66c..536da32186ac 100644 --- a/core/java/android/os/NullVibrator.java +++ b/core/java/android/os/NullVibrator.java @@ -36,22 +36,11 @@ public class NullVibrator extends Vibrator { 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 @@ public class NullVibrator extends Vibrator { * @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 700f80d1991c..13bc4f66d6d2 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 @@ import android.util.Log; 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 @@ public class SystemVibrator extends Vibrator { 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 @@ public class SystemVibrator extends Vibrator { * @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 @@ public class SystemVibrator extends Vibrator { // 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 5d55143b97ed..8845ba39af1b 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 @@ import android.content.Context; * {@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 @@ public abstract class Vibrator { * @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 @@ public abstract class Vibrator { * * @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 @@ public abstract class Vibrator { * @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 000000000000..63818842672c --- /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 000000000000..ba3bd4fa7fd3 --- /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 000000000000..b8635b815a36 --- /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 000000000000..2e358e433fd5 --- /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 290a89b46e90..d5f15f0ad368 100644 --- a/core/java/android/util/LongArray.java +++ b/core/java/android/util/LongArray.java @@ -111,7 +111,6 @@ public class LongArray implements Cloneable { } @Override - @SuppressWarnings("unchecked") public LongArray clone() { LongArray clone = null; try { @@ -154,7 +153,8 @@ public class LongArray implements Cloneable { if (index >= mSize) { throw new ArrayIndexOutOfBoundsException(mSize, index); } - System.arraycopy(mValues, index, mValues, index + 1, mSize - index); + System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1); + mSize--; } /** diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 3859ad44b722..abae0687ea56 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -18,12 +18,14 @@ package android.view; import android.graphics.Point; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; +import android.util.LongSparseArray; import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; @@ -35,8 +37,11 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Queue; /** * Class for managing accessibility interactions initiated from the system @@ -47,6 +52,8 @@ import java.util.Map; */ final class AccessibilityInteractionController { + private static final boolean ENFORCE_NODE_TREE_CONSISTENT = Build.IS_DEBUGGABLE; + private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); @@ -137,7 +144,7 @@ final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; - if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) { + if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { root = mViewRootImpl.mView; } else { root = findViewByAccessibilityId(accessibilityViewId); @@ -209,7 +216,7 @@ final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { root = findViewByAccessibilityId(accessibilityViewId); } else { root = mViewRootImpl.mView; @@ -289,7 +296,7 @@ final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { root = findViewByAccessibilityId(accessibilityViewId); } else { root = mViewRootImpl.mView; @@ -297,9 +304,14 @@ final class AccessibilityInteractionController { if (root != null && isShown(root)) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { - infos = provider.findAccessibilityNodeInfosByText(text, - virtualDescendantId); - } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) { + if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + infos = provider.findAccessibilityNodeInfosByText(text, + virtualDescendantId); + } else { + infos = provider.findAccessibilityNodeInfosByText(text, + AccessibilityNodeProvider.HOST_VIEW_ID); + } + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { ArrayList<View> foundViews = mTempArrayList; foundViews.clear(); root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT @@ -316,7 +328,7 @@ final class AccessibilityInteractionController { if (provider != null) { List<AccessibilityNodeInfo> infosFromProvider = provider.findAccessibilityNodeInfosByText(text, - AccessibilityNodeInfo.UNDEFINED); + AccessibilityNodeProvider.HOST_VIEW_ID); if (infosFromProvider != null) { infos.addAll(infosFromProvider); } @@ -391,7 +403,7 @@ final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { root = findViewByAccessibilityId(accessibilityViewId); } else { root = mViewRootImpl.mView; @@ -417,7 +429,7 @@ final class AccessibilityInteractionController { focused = AccessibilityNodeInfo.obtain( mViewRootImpl.mAccessibilityFocusedVirtualView); } - } else if (virtualDescendantId == View.NO_ID) { + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { focused = host.createAccessibilityNodeInfo(); } } break; @@ -500,7 +512,7 @@ final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View root = null; - if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { root = findViewByAccessibilityId(accessibilityViewId); } else { root = mViewRootImpl.mView; @@ -576,7 +588,7 @@ final class AccessibilityInteractionController { } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; View target = null; - if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { target = findViewByAccessibilityId(accessibilityViewId); } else { target = mViewRootImpl.mView; @@ -584,9 +596,14 @@ final class AccessibilityInteractionController { if (target != null && isShown(target)) { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { - succeeded = provider.performAction(virtualDescendantId, action, - arguments); - } else if (virtualDescendantId == View.NO_ID) { + if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + succeeded = provider.performAction(virtualDescendantId, action, + arguments); + } else { + succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, + action, arguments); + } + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { succeeded = target.performAccessibilityAction(action, arguments); } } @@ -734,6 +751,85 @@ final class AccessibilityInteractionController { } } } + if (ENFORCE_NODE_TREE_CONSISTENT) { + enforceNodeTreeConsistent(outInfos); + } + } + + private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) { + LongSparseArray<AccessibilityNodeInfo> nodeMap = + new LongSparseArray<AccessibilityNodeInfo>(); + final int nodeCount = nodes.size(); + for (int i = 0; i < nodeCount; i++) { + AccessibilityNodeInfo node = nodes.get(i); + nodeMap.put(node.getSourceNodeId(), node); + } + + // If the nodes are a tree it does not matter from + // which node we start to search for the root. + AccessibilityNodeInfo root = nodeMap.valueAt(0); + AccessibilityNodeInfo parent = root; + while (parent != null) { + root = parent; + parent = nodeMap.get(parent.getParentNodeId()); + } + + // Traverse the tree and do some checks. + AccessibilityNodeInfo accessFocus = null; + AccessibilityNodeInfo inputFocus = null; + HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>(); + Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>(); + fringe.add(root); + + while (!fringe.isEmpty()) { + AccessibilityNodeInfo current = fringe.poll(); + + // Check for duplicates + if (!seen.add(current)) { + throw new IllegalStateException("Duplicate node: " + + current + " in window:" + + mViewRootImpl.mAttachInfo.mAccessibilityWindowId); + } + + // Check for one accessibility focus. + if (current.isAccessibilityFocused()) { + if (accessFocus != null) { + throw new IllegalStateException("Duplicate accessibility focus:" + + current + + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId); + } else { + accessFocus = current; + } + } + + // Check for one input focus. + if (current.isFocused()) { + if (inputFocus != null) { + throw new IllegalStateException("Duplicate input focus: " + + current + " in window:" + + mViewRootImpl.mAttachInfo.mAccessibilityWindowId); + } else { + inputFocus = current; + } + } + + final int childCount = current.getChildCount(); + for (int j = 0; j < childCount; j++) { + final long childId = current.getChildId(j); + final AccessibilityNodeInfo child = nodeMap.get(childId); + if (child != null) { + fringe.add(child); + } + } + } + + // Check for disconnected nodes. + for (int j = nodeMap.size() - 1; j >= 0; j--) { + AccessibilityNodeInfo info = nodeMap.valueAt(j); + if (!seen.contains(info)) { + throw new IllegalStateException("Disconnected node: " + info); + } + } } private void prefetchPredecessorsOfRealNode(View view, @@ -774,7 +870,7 @@ final class AccessibilityInteractionController { info = child.createAccessibilityNodeInfo(); } else { info = provider.createAccessibilityNodeInfo( - AccessibilityNodeInfo.UNDEFINED); + AccessibilityNodeProvider.HOST_VIEW_ID); } if (info != null) { outInfos.add(info); @@ -814,7 +910,7 @@ final class AccessibilityInteractionController { } } else { AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo( - AccessibilityNodeInfo.UNDEFINED); + AccessibilityNodeProvider.HOST_VIEW_ID); if (info != null) { outInfos.add(info); addedChildren.put(child, info); @@ -845,16 +941,22 @@ final class AccessibilityInteractionController { List<AccessibilityNodeInfo> outInfos) { long parentNodeId = root.getParentNodeId(); int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId); - while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { + while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { return; } final int virtualDescendantId = AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId); - if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED + if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID || accessibilityViewId == providerHost.getAccessibilityViewId()) { - AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo( - virtualDescendantId); + final AccessibilityNodeInfo parent; + if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + parent = provider.createAccessibilityNodeInfo( + virtualDescendantId); + } else { + parent= provider.createAccessibilityNodeInfo( + AccessibilityNodeProvider.HOST_VIEW_ID); + } if (parent != null) { outInfos.add(parent); } @@ -875,10 +977,15 @@ final class AccessibilityInteractionController { AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId); final int parentVirtualDescendantId = AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId); - if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED + if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) { - AccessibilityNodeInfo parent = - provider.createAccessibilityNodeInfo(parentVirtualDescendantId); + final AccessibilityNodeInfo parent; + if (parentAccessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId); + } else { + parent = provider.createAccessibilityNodeInfo( + AccessibilityNodeProvider.HOST_VIEW_ID); + } if (parent != null) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { diff --git a/core/java/android/view/IMagnificationCallbacks.aidl b/core/java/android/view/IMagnificationCallbacks.aidl deleted file mode 100644 index 032d073a3e4d..000000000000 --- 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 c92a104b239d..8f542bbe78dc 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -27,7 +27,6 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IRemoteCallback; import android.view.IApplicationToken; -import android.view.IMagnificationCallbacks; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; @@ -197,7 +196,7 @@ interface IWindowManager void thawRotation(); /** - * Gets whether the rotation is frozen. + * Gets whether the rotation is frozen. * * @return Whether the rotation is frozen. */ @@ -231,55 +230,7 @@ interface IWindowManager void lockNow(in Bundle options); /** - * Gets the token for the focused window. - */ - IBinder getFocusedWindowToken(); - - /** - * Sets an input filter for manipulating the input event stream. - */ - void setInputFilter(in IInputFilter filter); - - /** - * Gets the frame of a window given its token. - */ - void getWindowFrame(IBinder token, out Rect outFrame); - - /** * Device is in safe mode. */ boolean isSafeModeEnabled(); - - /** - * Sets the display magnification callbacks. These callbacks notify - * the client for contextual changes related to display magnification. - * - * @param callbacks The magnification callbacks. - */ - void setMagnificationCallbacks(IMagnificationCallbacks callbacks); - - /** - * Sets the magnification spec to be applied to all windows that can be - * magnified. - * - * @param spec The current magnification spec. - */ - void setMagnificationSpec(in MagnificationSpec spec); - - /** - * Gets the magnification spec for a window given its token. If the - * window has a compatibility scale it is also folded in the returned - * magnification spec. - * - * @param windowToken The unique window token. - * @return The magnification spec if such or null. - */ - MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken); - - /** - * Sets the current touch exploration state. - * - * @param enabled Whether touch exploration is enabled. - */ - void setTouchExplorationEnabled(boolean enabled); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ecd73af2923c..a8ccd49119a5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5101,10 +5101,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see AccessibilityDelegate */ public void sendAccessibilityEvent(int eventType) { - // Excluded views do not send accessibility events. - if (!includeForAccessibility()) { - return; - } if (mAccessibilityDelegate != null) { mAccessibilityDelegate.sendAccessibilityEvent(this, eventType); } else { @@ -6901,7 +6897,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mParent.invalidateChild(this, null); } dispatchVisibilityChanged(this, newVisibility); + + notifySubtreeAccessibilityStateChangedIfNeeded(); } if ((changed & WILL_NOT_CACHE_DRAWING) != 0) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f9b940161a10..22fbbd62a058 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3711,7 +3711,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childHasTransientStateChanged(child, true); } - if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) { + if (child.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -3954,7 +3954,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager onViewRemoved(view); - if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) { + if (view.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 185cb655c678..1d6e998d19f9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6355,7 +6355,7 @@ public final class ViewRootImpl implements ViewParent, public void ensureConnection() { if (mAttachInfo != null) { final boolean registered = - mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED; + mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID; if (!registered) { mAttachInfo.mAccessibilityWindowId = mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, @@ -6366,9 +6366,9 @@ public final class ViewRootImpl implements ViewParent, public void ensureNoConnection() { final boolean registered = - mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED; + mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID; if (registered) { - mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED; + mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); } } diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/WindowInfo.aidl new file mode 100644 index 000000000000..75b8fd214aea --- /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 000000000000..7f890441b5aa --- /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 a1bd4bd1f7ca..14dc35646539 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -16,7 +16,12 @@ package android.view; +import android.graphics.Rect; +import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; +import android.os.IBinder; + +import java.util.List; /** * Window manager local system service interface. @@ -24,10 +29,136 @@ import android.hardware.display.DisplayManagerInternal; * @hide Only for use within the system server. */ public abstract class WindowManagerInternal { + + /** + * Interface to receive a callback when the windows reported for + * accessibility changed. + */ + public interface WindowsForAccessibilityCallback { + + /** + * Called when the windows for accessibility changed. + * + * @param windows The windows for accessibility. + */ + public void onWindowsForAccessibilityChanged(List<WindowInfo> windows); + } + + /** + * Callbacks for contextual changes that affect the screen magnification + * feature. + */ + public interface MagnificationCallbacks { + + /** + * Called when the bounds of the screen content that is magnified changed. + * Note that not the entire screen is magnified. + * + * @param bounds The bounds. + */ + public void onMagnifedBoundsChanged(Region bounds); + + /** + * Called when an application requests a rectangle on the screen to allow + * the client to apply the appropriate pan and scale. + * + * @param left The rectangle left. + * @param top The rectangle top. + * @param right The rectangle right. + * @param bottom The rectangle bottom. + */ + public void onRectangleOnScreenRequested(int left, int top, int right, int bottom); + + /** + * Notifies that the rotation changed. + * + * @param rotation The current rotation. + */ + public void onRotationChanged(int rotation); + + /** + * Notifies that the context of the user changed. For example, an application + * was started. + */ + public void onUserContextChanged(); + } + /** * Request that the window manager call * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager} * within a surface transaction at a later time. */ public abstract void requestTraversalFromDisplayManager(); -}
\ No newline at end of file + + /** + * Set by the accessibility layer to observe changes in the magnified region, + * rotation, and other window transformations related to display magnification + * as the window manager is responsible for doing the actual magnification + * and has access to the raw window data while the accessibility layer serves + * as a controller. + * + * @param callbacks The callbacks to invoke. + */ + public abstract void setMagnificationCallbacks(MagnificationCallbacks callbacks); + + /** + * Set by the accessibility layer to specify the magnification and panning to + * be applied to all windows that should be magnified. + * + * @param callbacks The callbacks to invoke. + * + * @see #setMagnificationCallbacks(MagnificationCallbacks) + */ + public abstract void setMagnificationSpec(MagnificationSpec spec); + + /** + * Gets the magnification and translation applied to a window given its token. + * Not all windows are magnified and the window manager policy determines which + * windows are magnified. The returned result also takes into account the compat + * scale if necessary. + * + * @param windowToken The window's token. + * + * @return The magnification spec for the window. + * + * @see #setMagnificationCallbacks(MagnificationCallbacks) + */ + public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow( + IBinder windowToken); + + /** + * Sets a callback for observing which windows are touchable for the purposes + * of accessibility. + * + * @param callback The callback. + */ + public abstract void setWindowsForAccessibilityCallback( + WindowsForAccessibilityCallback callback); + + /** + * Sets a filter for manipulating the input event stream. + * + * @param filter The filter implementation. + */ + public abstract void setInputFilter(IInputFilter filter); + + /** + * Gets the token of the window that has input focus. + * + * @return The token. + */ + public abstract IBinder getFocusedWindowToken(); + + /** + * @return Whether the keyguard is engaged. + */ + public abstract boolean isKeyguardLocked(); + + /** + * Gets the frame of a window given its token. + * + * @param token The token. + * @param outBounds The frame to populate. + */ + public abstract void getWindowFrame(IBinder token, Rect outBounds); +} diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 756565457dc9..bd203c873763 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1187,11 +1187,4 @@ public interface WindowManagerPolicy { * @return True if the window is a top level one. */ public boolean isTopLevelWindow(int windowType); - - /** - * Sets the current touch exploration state. - * - * @param enabled Whether touch exploration is enabled. - */ - public void setTouchExplorationEnabled(boolean enabled); } diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java new file mode 100644 index 000000000000..77d48e2db378 --- /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 8b9115549c2a..417e22c57456 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -343,6 +343,23 @@ import java.util.List; * view.</br> * </p> * <p> + * <b>Windows changed</b> - represents the event of changes in the windows shown on + * the screen such as a window appeared, a window disappeared, a window size changed, + * a window layer changed, etc.</br> + * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br> + * <em>Properties:</em></br> + * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> + * <li>{@link #getEventTime()} - The event time.</li> + * </ul> + * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window + * source of the event via {@link AccessibilityEvent#getSource()} to get the source + * node on which then call {@link AccessibilityNodeInfo#getWindow() + * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can + * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows() + * android.accessibilityservice.AccessibilityService.getWindows()}. + * </p> + * <p> * <b>NOTIFICATION TYPES</b></br> * </p> * <p> @@ -662,6 +679,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000; /** + * Represents the event change in the windows shown on the screen. + */ + public static final int TYPE_WINDOWS_CHANGED = 0x00400000; + + /** * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: * The type of change is not defined. */ @@ -708,6 +730,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @see #TYPE_GESTURE_DETECTION_END * @see #TYPE_TOUCH_INTERACTION_START * @see #TYPE_TOUCH_INTERACTION_END + * @see #TYPE_WINDOWS_CHANGED */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF; @@ -1366,6 +1389,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("TYPE_TOUCH_INTERACTION_END"); eventTypeCount++; } break; + case TYPE_WINDOWS_CHANGED: { + if (eventTypeCount > 0) { + builder.append(", "); + } + builder.append("TYPE_WINDOWS_CHANGED"); + eventTypeCount++; + } break; } } if (eventTypeCount > 1) { diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 5a55e340297b..4dd8dcba5a2f 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -100,14 +100,11 @@ public final class AccessibilityInteractionClient private Message mSameThreadMessage; - // The connection cache is shared between all interrogating threads. private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = new SparseArray<IAccessibilityServiceConnection>(); - // The connection cache is shared between all interrogating threads since - // at any given time there is only one window allowing querying. - private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache = - new AccessibilityNodeInfoCache(); + private static final AccessibilityCache sAccessibilityCache = + new AccessibilityCache(); /** * @return The client for the current thread. @@ -166,6 +163,86 @@ public final class AccessibilityInteractionClient } /** + * Gets the info for a window. + * + * @param connectionId The id of a connection for interacting with the system. + * @param accessibilityWindowId A unique window id. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @return The {@link AccessibilityWindowInfo}. + */ + public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId) { + try { + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + AccessibilityWindowInfo window = sAccessibilityCache.getWindow( + accessibilityWindowId); + if (window != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Window cache hit"); + } + return window; + } + if (DEBUG) { + Log.i(LOG_TAG, "Window cache miss"); + } + window = connection.getWindow(accessibilityWindowId); + if (window != null) { + sAccessibilityCache.addWindow(window); + return window; + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while calling remote getWindow", re); + } + return null; + } + + /** + * Gets the info for all windows. + * + * @param connectionId The id of a connection for interacting with the system. + * @return The {@link AccessibilityWindowInfo} list. + */ + public List<AccessibilityWindowInfo> getWindows(int connectionId) { + try { + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows(); + if (windows != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Window cache hit"); + } + return windows; + } + if (DEBUG) { + Log.i(LOG_TAG, "Window cache miss"); + } + windows = connection.getWindows(); + if (windows != null) { + final int windowCount = windows.size(); + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo window = windows.get(i); + sAccessibilityCache.addWindow(window); + } + return windows; + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while calling remote getWindows", re); + } + return Collections.emptyList(); + } + + /** * Finds an {@link AccessibilityNodeInfo} by accessibility id. * * @param connectionId The id of a connection for interacting with the system. @@ -183,15 +260,26 @@ public final class AccessibilityInteractionClient public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache, int prefetchFlags) { + if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0 + && (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) { + throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS" + + " requires FLAG_PREFETCH_PREDECESSORS"); + } try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { if (!bypassCache) { - AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get( - accessibilityNodeId); + AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode( + accessibilityWindowId, accessibilityNodeId); if (cachedInfo != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Node cache hit"); + } return cachedInfo; } + if (DEBUG) { + Log.i(LOG_TAG, "Node cache miss"); + } } final int interactionId = mInteractionIdCounter.getAndIncrement(); final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId( @@ -212,10 +300,8 @@ public final class AccessibilityInteractionClient } } } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote" - + " findAccessibilityNodeInfoByAccessibilityId", re); - } + Log.e(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfoByAccessibilityId", re); } return null; } @@ -259,10 +345,8 @@ public final class AccessibilityInteractionClient } } } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote" - + " findAccessibilityNodeInfoByViewIdInActiveWindow", re); - } + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfoByViewIdInActiveWindow", re); } return Collections.emptyList(); } @@ -307,10 +391,8 @@ public final class AccessibilityInteractionClient } } } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote" - + " findAccessibilityNodeInfosByViewText", re); - } + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfosByViewText", re); } return Collections.emptyList(); } @@ -352,9 +434,7 @@ public final class AccessibilityInteractionClient } } } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote findFocus", re); - } + Log.w(LOG_TAG, "Error while calling remote findFocus", re); } return null; } @@ -396,9 +476,7 @@ public final class AccessibilityInteractionClient } } } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re); - } + Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re); } return null; } @@ -436,19 +514,21 @@ public final class AccessibilityInteractionClient } } } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re); - } + Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re); } return false; } public void clearCache() { - sAccessibilityNodeInfoCache.clear(); + sAccessibilityCache.clear(); } public void onAccessibilityEvent(AccessibilityEvent event) { - sAccessibilityNodeInfoCache.onAccessibilityEvent(event); + sAccessibilityCache.onAccessibilityEvent(event); + } + + public void removeWindows(int[] windowIds) { + sAccessibilityCache.removeWindows(windowIds); } /** @@ -613,7 +693,7 @@ public final class AccessibilityInteractionClient if (info != null) { info.setConnectionId(connectionId); info.setSealed(true); - sAccessibilityNodeInfoCache.add(info); + sAccessibilityCache.add(info); } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index a711f48c8c14..116c173b5b4e 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -115,9 +115,9 @@ public final class AccessibilityManager { private static AccessibilityManager sInstance; - private static final int DO_SET_STATE = 10; + private final Object mLock = new Object(); - final IAccessibilityManager mService; + private IAccessibilityManager mService; final int mUserId; @@ -166,29 +166,14 @@ public final class AccessibilityManager { public void onTouchExplorationStateChanged(boolean enabled); } - final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { + private final IAccessibilityManagerClient.Stub mClient = + new IAccessibilityManagerClient.Stub() { public void setState(int state) { - mHandler.obtainMessage(DO_SET_STATE, state, 0).sendToTarget(); - } - }; - - class MyHandler extends Handler { - - MyHandler(Looper mainLooper) { - super(mainLooper); - } - - @Override - public void handleMessage(Message message) { - switch (message.what) { - case DO_SET_STATE : - setState(message.arg1); - return; - default : - Log.w(LOG_TAG, "Unknown message type: " + message.what); + synchronized (mLock) { + setStateLocked(state); } } - } + }; /** * Get an AccessibilityManager instance (create one if necessary). @@ -234,16 +219,8 @@ public final class AccessibilityManager { mHandler = new MyHandler(context.getMainLooper()); mService = service; mUserId = userId; - if (mService == null) { - mIsEnabled = false; - } - try { - if (mService != null) { - final int stateFlags = mService.addClient(mClient, userId); - setState(stateFlags); - } - } catch (RemoteException re) { - Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); + synchronized (mLock) { + tryConnectToServiceLocked(); } } @@ -253,7 +230,11 @@ public final class AccessibilityManager { * @return True if accessibility is enabled, false otherwise. */ public boolean isEnabled() { - synchronized (mHandler) { + synchronized (mLock) { + IAccessibilityManager service = getServiceLocked(); + if (service == null) { + return false; + } return mIsEnabled; } } @@ -264,24 +245,16 @@ public final class AccessibilityManager { * @return True if touch exploration is enabled, false otherwise. */ public boolean isTouchExplorationEnabled() { - synchronized (mHandler) { + synchronized (mLock) { + IAccessibilityManager service = getServiceLocked(); + if (service == null) { + return false; + } return mIsTouchExplorationEnabled; } } /** - * Returns the client interface this instance registers in - * the centralized accessibility manager service. - * - * @return The client. - * - * @hide - */ - public IAccessibilityManagerClient getClient() { - return (IAccessibilityManagerClient) mClient.asBinder(); - } - - /** * Sends an {@link AccessibilityEvent}. * * @param event The event to send. @@ -295,8 +268,17 @@ public final class AccessibilityManager { * their descendants. */ public void sendAccessibilityEvent(AccessibilityEvent event) { - if (!mIsEnabled) { - throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + final IAccessibilityManager service; + final int userId; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + if (!mIsEnabled) { + throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + } + userId = mUserId; } boolean doRecycle = false; try { @@ -305,7 +287,7 @@ public final class AccessibilityManager { // client using it is called through Binder from another process. Example: MMS // app adds a SMS notification and the NotificationManagerService calls this method long identityToken = Binder.clearCallingIdentity(); - doRecycle = mService.sendAccessibilityEvent(event, mUserId); + doRecycle = service.sendAccessibilityEvent(event, userId); Binder.restoreCallingIdentity(identityToken); if (DEBUG) { Log.i(LOG_TAG, event + " sent"); @@ -323,11 +305,20 @@ public final class AccessibilityManager { * Requests feedback interruption from all accessibility services. */ public void interrupt() { - if (!mIsEnabled) { - throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + final IAccessibilityManager service; + final int userId; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + if (!mIsEnabled) { + throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + } + userId = mUserId; } try { - mService.interrupt(mUserId); + service.interrupt(userId); if (DEBUG) { Log.i(LOG_TAG, "Requested interrupt from all services"); } @@ -361,18 +352,30 @@ public final class AccessibilityManager { * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. */ public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { + final IAccessibilityManager service; + final int userId; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return Collections.emptyList(); + } + userId = mUserId; + } + List<AccessibilityServiceInfo> services = null; try { - if (mService != null) { - services = mService.getInstalledAccessibilityServiceList(mUserId); - if (DEBUG) { - Log.i(LOG_TAG, "Installed AccessibilityServices " + services); - } + services = service.getInstalledAccessibilityServiceList(userId); + if (DEBUG) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); } - return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST; + if (services != null) { + return Collections.unmodifiableList(services); + } else { + return Collections.emptyList(); + } } /** @@ -390,18 +393,30 @@ public final class AccessibilityManager { */ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( int feedbackTypeFlags) { + final IAccessibilityManager service; + final int userId; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return Collections.emptyList(); + } + userId = mUserId; + } + List<AccessibilityServiceInfo> services = null; try { - if (mService != null) { - services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId); - if (DEBUG) { - Log.i(LOG_TAG, "Installed AccessibilityServices " + services); - } + services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId); + if (DEBUG) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); } } catch (RemoteException re) { Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); } - return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST; + if (services != null) { + return Collections.unmodifiableList(services); + } else { + return Collections.emptyList(); + } } /** @@ -413,6 +428,7 @@ public final class AccessibilityManager { */ public boolean addAccessibilityStateChangeListener( AccessibilityStateChangeListener listener) { + // Final CopyOnArrayList - no lock needed. return mAccessibilityStateChangeListeners.add(listener); } @@ -424,6 +440,7 @@ public final class AccessibilityManager { */ public boolean removeAccessibilityStateChangeListener( AccessibilityStateChangeListener listener) { + // Final CopyOnArrayList - no lock needed. return mAccessibilityStateChangeListeners.remove(listener); } @@ -436,6 +453,7 @@ public final class AccessibilityManager { */ public boolean addTouchExplorationStateChangeListener( TouchExplorationStateChangeListener listener) { + // Final CopyOnArrayList - no lock needed. return mTouchExplorationStateChangeListeners.add(listener); } @@ -447,6 +465,7 @@ public final class AccessibilityManager { */ public boolean removeTouchExplorationStateChangeListener( TouchExplorationStateChangeListener listener) { + // Final CopyOnArrayList - no lock needed. return mTouchExplorationStateChangeListeners.remove(listener); } @@ -455,50 +474,24 @@ public final class AccessibilityManager { * * @param stateFlags The state flags. */ - private void setState(int stateFlags) { + private void setStateLocked(int stateFlags) { final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0; final boolean touchExplorationEnabled = (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; - synchronized (mHandler) { - final boolean wasEnabled = mIsEnabled; - final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; - // Ensure listeners get current state from isZzzEnabled() calls. - mIsEnabled = enabled; - mIsTouchExplorationEnabled = touchExplorationEnabled; + final boolean wasEnabled = mIsEnabled; + final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; - if (wasEnabled != enabled) { - notifyAccessibilityStateChangedLh(); - } + // Ensure listeners get current state from isZzzEnabled() calls. + mIsEnabled = enabled; + mIsTouchExplorationEnabled = touchExplorationEnabled; - if (wasTouchExplorationEnabled != touchExplorationEnabled) { - notifyTouchExplorationStateChangedLh(); - } + if (wasEnabled != enabled) { + mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED); } - } - - /** - * Notifies the registered {@link AccessibilityStateChangeListener}s. - * <p> - * The caller must be locked on {@link #mHandler}. - */ - private void notifyAccessibilityStateChangedLh() { - final int listenerCount = mAccessibilityStateChangeListeners.size(); - for (int i = 0; i < listenerCount; i++) { - mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled); - } - } - /** - * Notifies the registered {@link TouchExplorationStateChangeListener}s. - * <p> - * The caller must be locked on {@link #mHandler}. - */ - private void notifyTouchExplorationStateChangedLh() { - final int listenerCount = mTouchExplorationStateChangeListeners.size(); - for (int i = 0; i < listenerCount; i++) { - mTouchExplorationStateChangeListeners.get(i) - .onTouchExplorationStateChanged(mIsTouchExplorationEnabled); + if (wasTouchExplorationEnabled != touchExplorationEnabled) { + mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED); } } @@ -511,11 +504,17 @@ public final class AccessibilityManager { */ public int addAccessibilityInteractionConnection(IWindow windowToken, IAccessibilityInteractionConnection connection) { - if (mService == null) { - return View.NO_ID; + final IAccessibilityManager service; + final int userId; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return View.NO_ID; + } + userId = mUserId; } try { - return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId); + return service.addAccessibilityInteractionConnection(windowToken, connection, userId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); } @@ -529,12 +528,90 @@ public final class AccessibilityManager { * @hide */ public void removeAccessibilityInteractionConnection(IWindow windowToken) { - try { - if (mService != null) { - mService.removeAccessibilityInteractionConnection(windowToken); + final IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; } + } + try { + service.removeAccessibilityInteractionConnection(windowToken); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); } } + + private IAccessibilityManager getServiceLocked() { + if (mService == null) { + tryConnectToServiceLocked(); + } + return mService; + } + + private void tryConnectToServiceLocked() { + IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); + if (iBinder == null) { + return; + } + IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder); + try { + final int stateFlags = service.addClient(mClient, mUserId); + setStateLocked(stateFlags); + mService = service; + } catch (RemoteException re) { + Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); + } + } + + /** + * Notifies the registered {@link AccessibilityStateChangeListener}s. + */ + private void handleNotifyAccessibilityStateChanged() { + final boolean isEnabled; + synchronized (mLock) { + isEnabled = mIsEnabled; + } + final int listenerCount = mAccessibilityStateChangeListeners.size(); + for (int i = 0; i < listenerCount; i++) { + mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(isEnabled); + } + } + + /** + * Notifies the registered {@link TouchExplorationStateChangeListener}s. + */ + private void handleNotifyTouchExplorationStateChanged() { + final boolean isTouchExplorationEnabled; + synchronized (mLock) { + isTouchExplorationEnabled = mIsTouchExplorationEnabled; + } + final int listenerCount = mTouchExplorationStateChangeListeners.size(); + for (int i = 0; i < listenerCount; i++) { + mTouchExplorationStateChangeListeners.get(i) + .onTouchExplorationStateChanged(isTouchExplorationEnabled); + } + } + + private final class MyHandler extends Handler { + public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1; + public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2; + + public MyHandler(Looper looper) { + super(looper, null, false); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: { + handleNotifyAccessibilityStateChanged(); + } break; + + case MSG_NOTIFY_EXPLORATION_STATE_CHANGED: { + handleNotifyTouchExplorationStateChanged(); + } + } + } + } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 560d0c9ba965..a6904f717b29 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -62,13 +62,19 @@ public class AccessibilityNodeInfo implements Parcelable { private static final boolean DEBUG = false; /** @hide */ - public static final int UNDEFINED = -1; + public static final int UNDEFINED_CONNECTION_ID = -1; /** @hide */ - public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED); + public static final int UNDEFINED_SELECTION_INDEX = -1; /** @hide */ - public static final int ACTIVE_WINDOW_ID = UNDEFINED; + public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE; + + /** @hide */ + public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID); + + /** @hide */ + public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID; /** @hide */ public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; @@ -504,6 +510,13 @@ public class AccessibilityNodeInfo implements Parcelable { * @hide */ public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) { + // We changed the value for undefined node to positive due to wrong + // global id composition (two 32-bin ints into one 64-bit long) but + // the value used for the host node provider view has id -1 so we + // remap it here. + if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) { + virtualDescendantId = UNDEFINED_ITEM_ID; + } return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId; } @@ -515,7 +528,7 @@ public class AccessibilityNodeInfo implements Parcelable { private boolean mSealed; // Data. - private int mWindowId = UNDEFINED; + private int mWindowId = UNDEFINED_ITEM_ID; private long mSourceNodeId = ROOT_NODE_ID; private long mParentNodeId = ROOT_NODE_ID; private long mLabelForId = ROOT_NODE_ID; @@ -536,14 +549,14 @@ public class AccessibilityNodeInfo implements Parcelable { private int mMovementGranularities; - private int mTextSelectionStart = UNDEFINED; - private int mTextSelectionEnd = UNDEFINED; + private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX; + private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX; private int mInputType = InputType.TYPE_NULL; private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; private Bundle mExtras; - private int mConnectionId = UNDEFINED; + private int mConnectionId = UNDEFINED_CONNECTION_ID; private RangeInfo mRangeInfo; private CollectionInfo mCollectionInfo; @@ -567,7 +580,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @param source The info source. */ public void setSource(View source) { - setSource(source, UNDEFINED); + setSource(source, UNDEFINED_ITEM_ID); } /** @@ -591,9 +604,9 @@ public class AccessibilityNodeInfo implements Parcelable { */ public void setSource(View root, int virtualDescendantId) { enforceNotSealed(); - mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED; + mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID; final int rootAccessibilityViewId = - (root != null) ? root.getAccessibilityViewId() : UNDEFINED; + (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); } @@ -766,7 +779,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void addChild(View child) { - addChildInternal(child, UNDEFINED, true); + addChildInternal(child, UNDEFINED_ITEM_ID, true); } /** @@ -776,7 +789,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @hide */ public void addChildUnchecked(View child) { - addChildInternal(child, UNDEFINED, false); + addChildInternal(child, UNDEFINED_ITEM_ID, false); } /** @@ -794,7 +807,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public boolean removeChild(View child) { - return removeChild(child, UNDEFINED); + return removeChild(child, UNDEFINED_ITEM_ID); } /** @@ -821,7 +834,7 @@ public class AccessibilityNodeInfo implements Parcelable { mChildNodeIds = new LongArray(); } final int rootAccessibilityViewId = - (root != null) ? root.getAccessibilityViewId() : UNDEFINED; + (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); // If we're checking uniqueness and the ID already exists, abort. if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) { @@ -847,7 +860,7 @@ public class AccessibilityNodeInfo implements Parcelable { return false; } final int rootAccessibilityViewId = - (root != null) ? root.getAccessibilityViewId() : UNDEFINED; + (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); final int index = childIds.indexOf(childNodeId); if (index < 0) { @@ -1043,6 +1056,22 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Gets the window to which this node belongs. + * + * @return The window. + * + * @see android.accessibilityservice.AccessibilityService#getWindows() + */ + public AccessibilityWindowInfo getWindow() { + enforceSealed(); + if (!canPerformRequestOverConnection(mSourceNodeId)) { + return null; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.getWindow(mConnectionId, mWindowId); + } + + /** * Gets the parent. * <p> * <strong>Note:</strong> It is a client responsibility to recycle the @@ -1059,7 +1088,8 @@ public class AccessibilityNodeInfo implements Parcelable { } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS + | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); } /** @@ -1084,7 +1114,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @throws IllegalStateException If called from an AccessibilityService. */ public void setParent(View parent) { - setParent(parent, UNDEFINED); + setParent(parent, UNDEFINED_ITEM_ID); } /** @@ -1109,7 +1139,7 @@ public class AccessibilityNodeInfo implements Parcelable { public void setParent(View root, int virtualDescendantId) { enforceNotSealed(); final int rootAccessibilityViewId = - (root != null) ? root.getAccessibilityViewId() : UNDEFINED; + (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); } @@ -1811,7 +1841,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @param labeled The view for which this info serves as a label. */ public void setLabelFor(View labeled) { - setLabelFor(labeled, UNDEFINED); + setLabelFor(labeled, UNDEFINED_ITEM_ID); } /** @@ -1836,7 +1866,7 @@ public class AccessibilityNodeInfo implements Parcelable { public void setLabelFor(View root, int virtualDescendantId) { enforceNotSealed(); final int rootAccessibilityViewId = (root != null) - ? root.getAccessibilityViewId() : UNDEFINED; + ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); } @@ -1858,7 +1888,8 @@ public class AccessibilityNodeInfo implements Parcelable { } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS + | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); } /** @@ -1868,7 +1899,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @param label The view that labels this node's source. */ public void setLabeledBy(View label) { - setLabeledBy(label, UNDEFINED); + setLabeledBy(label, UNDEFINED_ITEM_ID); } /** @@ -1893,7 +1924,7 @@ public class AccessibilityNodeInfo implements Parcelable { public void setLabeledBy(View root, int virtualDescendantId) { enforceNotSealed(); final int rootAccessibilityViewId = (root != null) - ? root.getAccessibilityViewId() : UNDEFINED; + ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId); } @@ -1915,7 +1946,8 @@ public class AccessibilityNodeInfo implements Parcelable { } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS + | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); } /** @@ -2362,6 +2394,7 @@ public class AccessibilityNodeInfo implements Parcelable { if (mChildNodeIds == null) { mChildNodeIds = otherChildNodeIds.clone(); } else { + mChildNodeIds.clear(); mChildNodeIds.addAll(otherChildNodeIds); } } @@ -2474,8 +2507,8 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = ROOT_NODE_ID; mLabelForId = ROOT_NODE_ID; mLabeledById = ROOT_NODE_ID; - mWindowId = UNDEFINED; - mConnectionId = UNDEFINED; + mWindowId = UNDEFINED_ITEM_ID; + mConnectionId = UNDEFINED_CONNECTION_ID; mMovementGranularities = 0; if (mChildNodeIds != null) { mChildNodeIds.clear(); @@ -2489,8 +2522,8 @@ public class AccessibilityNodeInfo implements Parcelable { mContentDescription = null; mViewIdResourceName = null; mActions = 0; - mTextSelectionStart = UNDEFINED; - mTextSelectionEnd = UNDEFINED; + mTextSelectionStart = UNDEFINED_SELECTION_INDEX; + mTextSelectionEnd = UNDEFINED_SELECTION_INDEX; mInputType = InputType.TYPE_NULL; mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE; if (mExtras != null) { @@ -2583,9 +2616,9 @@ public class AccessibilityNodeInfo implements Parcelable { } private boolean canPerformRequestOverConnection(long accessibilityNodeId) { - return (mWindowId != UNDEFINED - && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED - && mConnectionId != UNDEFINED); + return (mWindowId != UNDEFINED_ITEM_ID + && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID + && mConnectionId != UNDEFINED_CONNECTION_ID); } @Override @@ -2625,6 +2658,7 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append(super.toString()); if (DEBUG) { + builder.append("; sourceNodeId: " + mSourceNodeId); builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); builder.append("; mParentNodeId: " + mParentNodeId); diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java deleted file mode 100644 index b4944bee6f77..000000000000 --- 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 718c32fd8d25..abcbb7044de8 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java +++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java @@ -70,9 +70,14 @@ import java.util.List; public abstract class AccessibilityNodeProvider { /** + * The virtual id for the hosting View. + */ + public static final int HOST_VIEW_ID = -1; + + /** * Returns an {@link AccessibilityNodeInfo} representing a virtual view, * i.e. a descendant of the host View, with the given <code>virtualViewId</code> - * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}. + * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}. * <p> * A virtual descendant is an imaginary View that is reported as a part of the view * hierarchy for accessibility purposes. This enables custom views that draw complex @@ -99,7 +104,7 @@ public abstract class AccessibilityNodeProvider { /** * Performs an accessibility action on a virtual view, i.e. a descendant of the * host View, with the given <code>virtualViewId</code> or the host View itself - * if <code>virtualViewId</code> equals to {@link View#NO_ID}. + * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}. * * @param virtualViewId A client defined virtual view id. * @param action The action to perform. @@ -117,8 +122,8 @@ public abstract class AccessibilityNodeProvider { /** * Finds {@link AccessibilityNodeInfo}s by text. The match is case insensitive * containment. The search is relative to the virtual view, i.e. a descendant of the - * host View, with the given <code>virtualViewId</code> or the host View itself - * <code>virtualViewId</code> equals to {@link View#NO_ID}. + * host View, with the given <code>virtualViewId</code> or the host View itself + * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}. * * @param virtualViewId A client defined virtual view id which defined * the root of the tree in which to perform the search. diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index 3fcd2189066f..cc6a71d1250b 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -78,7 +78,7 @@ public class AccessibilityRecord { private boolean mIsInPool; boolean mSealed; - int mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY; + int mBooleanProperties = 0; int mCurrentItemIndex = UNDEFINED; int mItemCount = UNDEFINED; int mFromIndex = UNDEFINED; @@ -791,7 +791,7 @@ public class AccessibilityRecord { */ void clear() { mSealed = false; - mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY; + mBooleanProperties = 0; mCurrentItemIndex = UNDEFINED; mItemCount = UNDEFINED; mFromIndex = UNDEFINED; diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl new file mode 100644 index 000000000000..fdb25fb1ee61 --- /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 000000000000..80b5c5071c3a --- /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 16c41f3efdf4..cd75010e525f 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -36,4 +36,6 @@ interface IAppOpsService { 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 26d7f5fe0680..082f1a583763 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -208,7 +208,8 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } } - 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 @@ public final class BatteryStatsImpl extends BatteryStats { && 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { // 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } } - 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 @@ public final class BatteryStatsImpl extends BatteryStats { } mHistoryBaseTime = 0; - mLastHistoryTime = 0; + mLastHistoryElapsedRealtime = 0; + mLastHistoryUptime = 0; mHistoryBuffer.setDataSize(0); mHistoryBuffer.setDataPosition(0); @@ -2262,7 +2266,9 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { // 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 final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } if (newHistory) { - addHistoryRecordLocked(elapsedRealtime); + addHistoryRecordLocked(elapsedRealtime, uptime); } } @@ -2875,11 +2902,12 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeAmountScreenOn = 0; mDischargeAmountScreenOff = 0; } - initActiveHistoryEventsLocked(mSecRealtime); + initActiveHistoryEventsLocked(mSecRealtime, mSecUptime); } private void resetAllStatsLocked() { @@ -5705,7 +5750,7 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } } - 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeAmountScreenOff = 0; updateTimeBasesLocked(true, !mScreenOn, uptime, realtime); if (reset) { - initActiveHistoryEventsLocked(mSecRealtime); + initActiveHistoryEventsLocked(mSecRealtime, mSecUptime); } } else { pullPendingStateUpdatesLocked(); @@ -5806,7 +5852,7 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { changed = true; } if (changed) { - addHistoryRecordLocked(SystemClock.elapsedRealtime()); + addHistoryRecordLocked(elapsedRealtime, uptime); } } if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) { @@ -6161,7 +6209,18 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 246e3bd229a0..201ffe85b19a 100644 --- a/core/jni/android_content_res_Configuration.cpp +++ b/core/jni/android_content_res_Configuration.cpp @@ -70,15 +70,6 @@ void android_Configuration_getFromJava( 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 @@ int register_android_content_res_Configuration(JNIEnv* env) 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 aa6a03595a6e..e72aff95871a 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 228a92eacb08..00d92fc73d84 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 ad2e9ff757d8..4bf5f78374e8 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 823f8d718aef..fa08a7837b3c 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 c2159fbeae6f..37ef539b5b83 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 8482fdb6e82e..582ed1bba803 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 f1bcf656efa2..0699e8b4a4be 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 54ef20cd0fa7..7c957b8796a7 100644 --- a/docs/html/guide/topics/graphics/hardware-accel.jd +++ b/docs/html/guide/topics/graphics/hardware-accel.jd @@ -263,256 +263,153 @@ changed.</li> <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 @@ changed.</li> <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> - </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">< 17</td> - <td class="s3">17</td> - <td class="s2">18</td> + <td class="rShim" style="width:240px;"></td> </tr> <tr> - <td colspan="5" class="s5">Support for large scale factors</td> + <td class="s5">Drawing operation to be scaled</td> + <td class="s1">First supported API level</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_pos">drawText()</td> + <td class="value_pos">18</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_neg">drawPosText()</td> + <td class="value_neg">✗</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">drawTextOnPath()</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_pos">Simple Shapes*</td> + <td class="value_pos">17</td> </tr> <tr> - <td class="s10">Complex Shapes*</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">drawPath()</td> - <td class="g_neg">✗</td> - <td class="w_neg">✗</td> - <td class="g_neg">✗</td> + <td class="label_neg">drawPath()</td> + <td class="value_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 ce6cc38b8357..589211fa60b8 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -72,7 +72,7 @@ String8 Asset::getAssetAllocations() } cur = cur->mNext; } - + return res; } @@ -84,18 +84,18 @@ Asset::Asset(void) 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 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz } mFileName = fileName != NULL ? strdup(fileName) : NULL; - + return NO_ERROR; } @@ -538,7 +538,7 @@ void _FileAsset::close(void) 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 1a5c55ce8eda..a5b94167fdfc 100644 --- a/libs/androidfw/BackupData.cpp +++ b/libs/androidfw/BackupData.cpp @@ -291,7 +291,7 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) (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 302fbf63af52..ab837ad71cbf 100644 --- a/libs/androidfw/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -568,8 +568,8 @@ int write_tarfile(const String8& packageName, const String8& domain, // [ 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 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) 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 0f54edb6f915..2b74a3308381 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 98849e359825..652cd4a142f7 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1266,7 +1266,7 @@ ResXMLParser::event_code_t ResXMLParser::nextNode() 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 @@ ResXMLParser::event_code_t ResXMLParser::nextNode() (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 @@ ResXMLParser::event_code_t ResXMLParser::nextNode() (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 @@ struct ResTable::Package delete types[i]; } } - + ResTable* const owner; const Header* const header; const ResTable_package* const package; @@ -2725,7 +2725,7 @@ struct ResTable::Package ResStringPool typeStrings; ResStringPool keyStrings; - + const Type* getType(size_t idx) const { return idx < types.size() ? types[idx] : NULL; } @@ -2775,18 +2775,18 @@ struct ResTable::PackageGroup 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 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) //ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); //dumpToLog(); - + return NO_ERROR; } @@ -2944,7 +2944,7 @@ status_t ResTable::Theme::setTo(const Theme& other) //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 @@ status_t ResTable::Theme::setTo(const Theme& other) //ALOGI("Final theme:"); //dumpToLog(); - + return NO_ERROR; } @@ -2984,7 +2984,7 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, 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 @@ void ResTable::Theme::dumpToLog() const 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(Asset* asset, const int32_t cookie, bool copyData, const 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 @@ status_t ResTable::add(ResTable* src) 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 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook 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 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag // are identical (diff == 0), or overlay packages will not take effect. continue; } - + bestItem = thisConfig; bestValue = item; bestPackage = package; @@ -3573,7 +3573,7 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, 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 @@ ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const 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 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, 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 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, ? 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 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, } else { set->typeSpecFlags = -1; } - + // Now merge in the new attributes... ssize_t curOff = offset; const ResTable_map* map; @@ -4070,7 +4070,7 @@ nope: 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 @@ nope: 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 @@ nope: 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 @@ static bool parse_unit(const char* str, Res_value* outValue, 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 @@ bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) 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 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, // 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 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, } } } - + if (*s == '#') { // It's a color! Convert to an integer of the form 0xaarrggbb. uint32_t color = 0; @@ -4710,7 +4710,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, // 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 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, return true; } } - + } bag++; cnt--; @@ -5240,43 +5240,43 @@ ssize_t ResTable::getEntry( 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 @@ ssize_t ResTable::getEntry( 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, // 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=err); } group->basePackage = package; - + mPackageMap[id] = (uint8_t)idx; } else { group = mPackageGroups.itemAt(idx-1); @@ -5428,10 +5428,10 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, 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 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (int)dtohl(type->entryCount), (int)t->entryCount); return (mError=BAD_TYPE); } - + TABLE_GETENTRY( ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); @@ -5557,7 +5557,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (group->typeCount == 0) { group->typeCount = package->types.size(); } - + return NO_ERROR; } @@ -5757,7 +5757,7 @@ static void print_complex(uint32_t complex, bool isFraction) * 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 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const } 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 @@ void ResTable::print(bool inclValues) const 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 @@ void ResTable::print(bool inclValues) const 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 @@ void ResTable::print(bool inclValues) const 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 @@ void ResTable::print(bool inclValues) const (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 @@ void ResTable::print(bool inclValues) const entriesStart, thisOffset, esize, typeSize); continue; } - + const Res_value* valuePtr = NULL; const ResTable_map_entry* bagPtr = NULL; Res_value value; @@ -6019,12 +6019,12 @@ void ResTable::print(bool inclValues) const (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 e9ac2fe25f58..6fa0f14ecb8e 100644 --- a/libs/androidfw/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -127,7 +127,7 @@ static const unsigned long kReadBufSize = 32768; 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 @@ public: { } - 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 eeff4c0949a0..4de755d6084c 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -38,6 +38,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) 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 d124cdea2454..cf745eed1ee6 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 f4de8ec1cc0d..9e6a96d38713 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 b80c1187648c..df5cba61d7cb 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -143,136 +143,6 @@ private: 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 e69e08ed2227..78c97e14d363 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 65498a577c06..04c5a7372aae 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 52176d4ef78b..bd9bfe95c057 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 000000000000..e371590b8120 --- /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 000000000000..177f33e10241 --- /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 f81cd12d82ab..5fa0ba59efac 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -573,10 +573,8 @@ void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly 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 @@ void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly // 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 46b74daebdb5..fe510f639bee 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -258,7 +258,7 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { 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 5611efb0940e..dee8705561bf 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -23,11 +23,20 @@ import java.nio.ByteBuffer; 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 @@ public class AudioTrack * 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 @@ public class AudioTrack 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 @@ public class AudioTrack * {@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 @@ public class AudioTrack 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 @@ public class AudioTrack * {@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 67680a8ae900..eb91668ca8c7 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -269,6 +269,6 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { 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 e20a4af43344..1b924102bd6b 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.Parcel; 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.MediaTimeProvider; 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 @@ public class MediaPlayer implements SubtitleController.Listener 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 @@ public class MediaPlayer implements SubtitleController.Listener 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 @@ public class MediaPlayer implements SubtitleController.Listener * * @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 @@ public class MediaPlayer implements SubtitleController.Listener * @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 @@ public class MediaPlayer implements SubtitleController.Listener * 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 @@ public class MediaPlayer implements SubtitleController.Listener * 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 f1b256e0d197..14f0c6903932 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -20,16 +20,24 @@ import java.io.File; 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 @@ public class SoundPool { private SoundPool mProxy; private final Object mLock; + private final int mStreamType; + private final IAppOpsService mAppOps; // SoundPool messages // @@ -463,6 +473,9 @@ public class SoundPool { } 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 class SoundPool { 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 class SoundPool { 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 c48af1124848..0e7d83ec9a1d 100644 --- a/media/jni/android_media_MediaHTTPConnection.cpp +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -84,7 +84,7 @@ static fields_t gFields; 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 @@ static sp<JMediaHTTPConnection> setObject( 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 @@ static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) { 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 dc3ae5bf7fef..abebd483e240 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -500,6 +500,20 @@ android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint str 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 @@ static JNINativeMethod gMethods[] = { {"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 @@ static JNINativeMethod gMethods[] = { {"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 76e834658291..312c3664a773 100644 --- a/media/jni/mediaeditor/Android.mk +++ b/media/jni/mediaeditor/Android.mk @@ -47,7 +47,6 @@ LOCAL_C_INCLUDES += \ 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 9cc55abe2f1b..bda3b6bd3f8a 100644 --- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp +++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp @@ -229,7 +229,7 @@ static JNINativeMethod gMethods[] = { "(I)Z", (void *)android_media_SoundPool_SoundPoolImpl_unload }, - { "play", + { "_play", "(IFFIIF)I", (void *)android_media_SoundPool_SoundPoolImpl_play }, @@ -253,7 +253,7 @@ static JNINativeMethod gMethods[] = { "(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 3b448d9bd692..e43a1e27f20f 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 aec3318a3f36..5a91484b8c7f 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 519b936dd40f..d9f44755e6f6 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 2a96b1a6de8a..4b5ebcd20a42 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 fd424c8390c6..9be7b79d022b 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 1e79ee416f5c..2e76f191307b 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -1120,6 +1120,7 @@ public class KeyguardHostView extends KeyguardViewBase { 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 873828861c71..5b35ba883803 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java @@ -400,6 +400,7 @@ public class KeyguardViewManager { } 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 55d7def460b1..58086c4f63c2 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { // 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { } 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { 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 71a644da6c0f..7671ef17b9a9 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 6e09131abae3..032fa346b1b1 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 0d9be4c61747..018856315530 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 55399c2d7924..163ef2a17173 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 f16c90b367dd..8a60927c968f 100644 --- a/packages/services/PacProcessor/jni/Android.mk +++ b/packages/services/PacProcessor/jni/Android.mk @@ -35,6 +35,7 @@ LOCAL_SHARED_LIBRARIES := \ 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 1cca16413e0e..3c23c6e56cd0 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -91,6 +91,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -212,6 +213,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Object mServiceAquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes SearchManager mSearchManager; + AccessibilityManager mAccessibilityManager; // Vibrator pattern for haptic feedback of a long press. long[] mLongPressVibePattern; @@ -299,7 +301,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mOrientationSensorEnabled = false; int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mHasSoftInput = false; - boolean mTouchExplorationEnabled = false; boolean mTranslucentDecorEnabled = true; int mPointerLocationMode = 0; // guarded by mLock @@ -905,6 +906,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.bool.config_enableTranslucentDecor); readConfigurationDependentBehaviors(); + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + // register for dock events IntentFilter filter = new IntentFilter(); filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE); @@ -1105,7 +1109,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { * navigation bar and touch exploration is not enabled */ private boolean canHideNavigationBar() { - return mHasNavigationBar && !mTouchExplorationEnabled; + return mHasNavigationBar + && !mAccessibilityManager.isTouchExplorationEnabled(); } @Override @@ -5083,10 +5088,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { * R.boolean.config_enableTranslucentDecor is false. */ private boolean areTranslucentBarsAllowed() { - return mTranslucentDecorEnabled && !mTouchExplorationEnabled; + return mTranslucentDecorEnabled + && !mAccessibilityManager.isTouchExplorationEnabled(); } // Use this instead of checking config_showNavigationBar so that it can be consistently @@ -5297,11 +5303,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void setTouchExplorationEnabled(boolean enabled) { - mTouchExplorationEnabled = enabled; - } - - @Override public boolean isTopLevelWindow(int windowType) { if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index d4fa5a709f25..dce4f581062a 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -235,8 +235,8 @@ public class RenderScript { 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 @@ public class RenderScript { 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 b547706f885e..671b43defdc6 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -452,7 +452,7 @@ nElementGetNativeData(JNIEnv *_env, jobject _this, jlong con, jlong id, jintArra // 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 @@ nElementGetSubElements(JNIEnv *_env, jobject _this, jlong con, jlong id, 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 @@ nProgramRasterCreate(JNIEnv *_env, jobject _this, jlong con, jboolean pointSprit // --------------------------------------------------------------------------- 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 @@ static JNINativeMethod methods[] = { {"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 959d4a94b732..0edce1147e63 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -44,7 +44,6 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.input.InputManager; import android.net.Uri; -import android.opengl.Matrix; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -52,7 +51,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.Parcel; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -63,24 +61,27 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; +import android.util.LongArray; import android.util.Pools.Pool; import android.util.Pools.SimplePool; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.IWindow; -import android.view.IWindowManager; import android.view.InputDevice; import android.view.InputEventConsistencyVerifier; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MagnificationSpec; +import android.view.WindowInfo; import android.view.WindowManager; +import android.view.WindowManagerInternal; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.accessibility.IAccessibilityManager; @@ -89,6 +90,7 @@ import android.view.accessibility.IAccessibilityManagerClient; import com.android.internal.R; import com.android.internal.content.PackageMonitor; import com.android.internal.statusbar.IStatusBarService; +import com.android.server.LocalServices; import org.xmlpull.v1.XmlPullParserException; @@ -166,7 +168,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final PackageManager mPackageManager; - private final IWindowManager mWindowManagerService; + private final WindowManagerInternal mWindowManagerService; private final SecurityPolicy mSecurityPolicy; @@ -197,9 +199,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private int mCurrentUserId = UserHandle.USER_OWNER; + private final LongArray mTempLongArray = new LongArray(); + //TODO: Remove this hack private boolean mInitialized; + private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback; + private UserState getCurrentUserStateLocked() { return getUserStateLocked(mCurrentUserId); } @@ -221,7 +227,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public AccessibilityManagerService(Context context) { mContext = context; mPackageManager = mContext.getPackageManager(); - mWindowManagerService = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE); + mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); mSecurityPolicy = new SecurityPolicy(); mMainHandler = new MainHandler(mContext.getMainLooper()); //TODO: (multi-display) We need to support multiple displays. @@ -390,7 +396,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return true; // yes, recycle the event } - if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) { + if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) { mSecurityPolicy.updateEventSourceLocked(event); mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_ACTIVE_WINDOW, event.getWindowId(), event.getEventType()).sendToTarget(); @@ -632,11 +638,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mSecurityPolicy.enforceCallingPermission( Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY, TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED); - try { - if (!mWindowManagerService.isKeyguardLocked()) { - return; - } - } catch (RemoteException re) { + if (!mWindowManagerService.isKeyguardLocked()) { return; } synchronized (mLock) { @@ -739,6 +741,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { * @param outBounds The output to which to write the bounds. */ boolean getActiveWindowBounds(Rect outBounds) { + // TODO: This should be refactored to work with accessibility + // focus in multiple windows. IBinder token; synchronized (mLock) { final int windowId = mSecurityPolicy.mActiveWindowId; @@ -747,13 +751,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { token = getCurrentUserStateLocked().mWindowTokens.get(windowId); } } - try { - mWindowManagerService.getWindowFrame(token, outBounds); - if (!outBounds.isEmpty()) { - return true; - } - } catch (RemoteException re) { - /* ignore */ + mWindowManagerService.getWindowFrame(token, outBounds); + if (!outBounds.isEmpty()) { + return true; } return false; } @@ -771,7 +771,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } void onMagnificationStateChanged() { - notifyClearAccessibilityNodeInfoCacheLocked(); + notifyClearAccessibilityCacheLocked(); } private void switchUser(int userId) { @@ -879,7 +879,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } - private void notifyClearAccessibilityNodeInfoCacheLocked() { + private void notifyClearAccessibilityCacheLocked() { UserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { Service service = state.mBoundServices.get(i); @@ -887,6 +887,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) { + UserState state = getCurrentUserStateLocked(); + for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { + Service service = state.mBoundServices.get(i); + if (mSecurityPolicy.canRetrieveWindowsLocked(service)) { + service.notifyWindowsChangedLocked(windows); + } + } + } + /** * Removes an AccessibilityInteractionConnection. * @@ -994,7 +1004,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Service service = state.mBoundServices.get(i); if (service.mIsDefault == isDefault) { - if (canDispathEventLocked(service, event, state.mHandledFeedbackTypes)) { + if (canDispatchEventToServiceLocked(service, event, + state.mHandledFeedbackTypes)) { state.mHandledFeedbackTypes |= service.mFeedbackType; service.notifyAccessibilityEvent(event); } @@ -1043,7 +1054,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { * @param handledFeedbackTypes The feedback types for which services have been notified. * @return True if the listener should be notified, false otherwise. */ - private boolean canDispathEventLocked(Service service, AccessibilityEvent event, + private boolean canDispatchEventToServiceLocked(Service service, AccessibilityEvent event, int handledFeedbackTypes) { if (!service.canReceiveEventsLocked()) { @@ -1232,11 +1243,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } if (setInputFilter) { - try { - mWindowManagerService.setInputFilter(inputFilter); - } catch (RemoteException re) { - /* ignore */ - } + mWindowManagerService.setInputFilter(inputFilter); } } @@ -1296,6 +1303,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mInitialized = true; updateLegacyCapabilities(userState); updateServicesLocked(userState); + updateWindowsForAccessibilityCallback(userState); updateFilterKeyEventsLocked(userState); updateTouchExplorationLocked(userState); updateEnhancedWebAccessibilityLocked(userState); @@ -1304,6 +1312,43 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { scheduleUpdateClientsIfNeededLocked(userState); } + private void updateWindowsForAccessibilityCallback(UserState userState) { + if (userState.mIsAccessibilityEnabled) { + // We observe windows for accessibility only if there is at least + // one bound service that can retrieve window content that specified + // it is interested in accessing such windows. For services that are + // binding we do an update pass after each bind event, so we run this + // code and register the callback if needed. + boolean boundServiceCanRetrieveInteractiveWindows = false; + + List<Service> boundServices = userState.mBoundServices; + final int boundServiceCount = boundServices.size(); + for (int i = 0; i < boundServiceCount; i++) { + Service boundService = boundServices.get(i); + if (mSecurityPolicy.canRetrieveWindowContentLocked(boundService) + && boundService.mRetrieveInteractiveWindows) { + boundServiceCanRetrieveInteractiveWindows = true; + break; + } + } + + if (boundServiceCanRetrieveInteractiveWindows) { + if (mWindowsForAccessibilityCallback == null) { + mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback(); + mWindowManagerService.setWindowsForAccessibilityCallback( + mWindowsForAccessibilityCallback); + } + return; + } + } + + if (mWindowsForAccessibilityCallback != null) { + mWindowsForAccessibilityCallback = null; + mWindowManagerService.setWindowsForAccessibilityCallback( + mWindowsForAccessibilityCallback); + } + } + private void updateLegacyCapabilities(UserState userState) { // Up to JB-MR1 we had a white list with services that can enable touch // exploration. When a service is first started we show a dialog to the @@ -1435,11 +1480,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0, userState.mUserId); } - try { - mWindowManagerService.setTouchExplorationEnabled(enabled); - } catch (RemoteException e) { - e.printStackTrace(); - } } private boolean canRequestAndRequestsTouchExplorationLocked(Service service) { @@ -1605,6 +1645,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } event.recycle(); } break; + case MSG_SEND_KEY_EVENT_TO_INPUT_FILTER: { KeyEvent event = (KeyEvent) msg.obj; final int policyFlags = msg.arg1; @@ -1615,28 +1656,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } event.recycle(); } break; + case MSG_SEND_STATE_TO_CLIENTS: { final int clientState = msg.arg1; final int userId = msg.arg2; sendStateToClients(clientState, mGlobalClients); sendStateToClientsForUser(clientState, userId); } break; + case MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER: { final int userId = msg.arg1; sendStateToClientsForUser(0, userId); } break; + case MSG_UPDATE_ACTIVE_WINDOW: { final int windowId = msg.arg1; final int eventType = msg.arg2; mSecurityPolicy.updateActiveWindow(windowId, eventType); } break; + case MSG_ANNOUNCE_NEW_USER_IF_NEEDED: { announceNewUserIfNeeded(); } break; + case MSG_UPDATE_INPUT_FILTER: { UserState userState = (UserState) msg.obj; updateInputFilter(userState); } break; + case MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG: { Service service = (Service) msg.obj; showEnableTouchExplorationDialog(service); @@ -1655,7 +1702,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_ANNOUNCEMENT); event.getText().add(message); - event.setWindowId(mSecurityPolicy.getRetrievalAllowingWindowLocked()); sendAccessibilityEvent(event, mCurrentUserId); } } @@ -1703,6 +1749,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mPendingEventPool.release(pendingEvent); } + private int findWindowIdLocked(IBinder token) { + final int globalIndex = mGlobalWindowTokens.indexOfValue(token); + if (globalIndex >= 0) { + return mGlobalWindowTokens.keyAt(globalIndex); + } + UserState userState = getCurrentUserStateLocked(); + final int userIndex = userState.mWindowTokens.indexOfValue(token); + if (userIndex >= 0) { + return userState.mWindowTokens.keyAt(userIndex); + } + return -1; + } + /** * This class represents an accessibility service. It stores all per service * data required for the service management, provides API for starting/stopping the @@ -1738,6 +1797,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boolean mRequestFilterKeyEvents; + boolean mRetrieveInteractiveWindows; + int mFetchFlags; long mNotificationTimeout; @@ -1748,7 +1809,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boolean mIsAutomation; - final Rect mTempBounds = new Rect(); + final Rect mTempBounds1 = new Rect(); + + final Rect mTempBounds2 = new Rect(); final ResolveInfo mResolveInfo; @@ -1758,6 +1821,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher(); + final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows = + new SparseArray<AccessibilityWindowInfo>(); + boolean mWasConnectedAndDied; // Handler only for dispatching accessibility events since we use event @@ -1822,7 +1888,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mRequestEnhancedWebAccessibility = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0; mRequestFilterKeyEvents = (info.flags - & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; + & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; + mRetrieveInteractiveWindows = (info.flags + & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0; + + if (!mRetrieveInteractiveWindows) { + clearIntrospectedWindows(); + } } /** @@ -1932,6 +2004,59 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override + public List<AccessibilityWindowInfo> getWindows() { + synchronized (mLock) { + final int resolvedUserId = mSecurityPolicy + .resolveCallingUserIdEnforcingPermissionsLocked( + UserHandle.getCallingUserId()); + if (resolvedUserId != mCurrentUserId) { + return null; + } + final boolean permissionGranted = + mSecurityPolicy.canRetrieveWindowsLocked(this); + if (!permissionGranted) { + return null; + } + List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>(); + final int windowCount = mSecurityPolicy.mWindows.size(); + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i); + AccessibilityWindowInfo windowClone = + AccessibilityWindowInfo.obtain(window); + windowClone.setConnectionId(mId); + mIntrospectedWindows.put(window.getId(), windowClone); + windows.add(windowClone); + } + return windows; + } + } + + @Override + public AccessibilityWindowInfo getWindow(int windowId) { + synchronized (mLock) { + final int resolvedUserId = mSecurityPolicy + .resolveCallingUserIdEnforcingPermissionsLocked( + UserHandle.getCallingUserId()); + if (resolvedUserId != mCurrentUserId) { + return null; + } + final boolean permissionGranted = + mSecurityPolicy.canRetrieveWindowsLocked(this); + if (!permissionGranted) { + return null; + } + AccessibilityWindowInfo window = mSecurityPolicy.findWindowById(windowId); + if (window != null) { + AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window); + windowClone.setConnectionId(mId); + mIntrospectedWindows.put(windowId, windowClone); + return windowClone; + } + return null; + } + } + + @Override public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewIdResName, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) @@ -1945,12 +2070,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return false; } - mSecurityPolicy.enforceCanRetrieveWindowContent(this); - final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); + resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); + final boolean permissionGranted = + mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { return false; } else { - resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); connection = getConnectionLocked(resolvedWindowId); if (connection == null) { return false; @@ -1989,7 +2114,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return false; } - mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); @@ -2034,7 +2158,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return false; } - mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); @@ -2079,7 +2202,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return false; } - mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); @@ -2123,7 +2245,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return false; } - mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); @@ -2167,7 +2288,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (resolvedUserId != mCurrentUserId) { return false; } - mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this, resolvedWindowId, action, arguments); @@ -2365,7 +2485,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } mPendingEvents.remove(eventType); - if (mSecurityPolicy.canRetrieveWindowContent(this)) { + if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) { event.setConnectionId(mId); } else { event.setSource(null); @@ -2396,12 +2516,69 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } public void notifyClearAccessibilityNodeInfoCache() { + clearIntrospectedWindows(); mInvocationHandler.sendEmptyMessage( - InvocationHandler.MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); + InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); + } + + private void clearIntrospectedWindows() { + final int windowCount = mIntrospectedWindows.size(); + for (int i = windowCount - 1; i >= 0; i--) { + mIntrospectedWindows.valueAt(i).recycle(); + mIntrospectedWindows.removeAt(i); + } + } + + public void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) { + LongArray changedWindows = mTempLongArray; + changedWindows.clear(); + + // Figure out which windows the service cares about changed. + final int windowCount = windows.size(); + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo newState = windows.get(i); + final int windowId = newState.getId(); + AccessibilityWindowInfo oldState = mIntrospectedWindows.get(windowId); + if (oldState != null && oldState.changed(newState)) { + oldState.recycle(); + mIntrospectedWindows.put(newState.getId(), + AccessibilityWindowInfo.obtain(newState)); + changedWindows.add(windowId); + } + } + + // Figure out which windows the service cares about went away. + final int introspectedWindowCount = mIntrospectedWindows.size(); + for (int i = introspectedWindowCount - 1; i >= 0; i--) { + AccessibilityWindowInfo window = mIntrospectedWindows.valueAt(i); + if (changedWindows.indexOf(window.getId()) < 0 && !windows.contains(window)) { + changedWindows.add(window.getId()); + mIntrospectedWindows.removeAt(i); + window.recycle(); + } + } + + int[] windowIds = null; + + final int changedWindowCount = changedWindows.size(); + if (changedWindowCount > 0) { + windowIds = new int[changedWindowCount]; + for (int i = 0; i < changedWindowCount; i++) { + // Cast is fine as we use long array to cache ints. + windowIds[i] = (int) changedWindows.get(i); + } + changedWindows.clear(); + } + + mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_WINDOWS_CHANGED, + windowIds).sendToTarget(); } private void notifyGestureInternal(int gestureId) { - IAccessibilityServiceClient listener = mServiceInterface; + final IAccessibilityServiceClient listener; + synchronized (mLock) { + listener = mServiceInterface; + } if (listener != null) { try { listener.onGesture(gestureId); @@ -2416,11 +2593,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mKeyEventDispatcher.notifyKeyEvent(event, policyFlags); } - private void notifyClearAccessibilityNodeInfoCacheInternal() { - IAccessibilityServiceClient listener = mServiceInterface; + private void notifyClearAccessibilityCacheInternal() { + final IAccessibilityServiceClient listener; + synchronized (mLock) { + listener = mServiceInterface; + } if (listener != null) { try { - listener.clearAccessibilityNodeInfoCache(); + listener.clearAccessibilityCache(); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error during requesting accessibility info cache" + " to be cleared.", re); @@ -2428,6 +2608,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private void notifyWindowsChangedInternal(int[] windowIds) { + final IAccessibilityServiceClient listener; + synchronized (mLock) { + listener = mServiceInterface; + } + if (listener != null) { + try { + listener.onWindowsChanged(windowIds); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error during sending windows to: " + mService, re); + } + } + } + private void sendDownAndUpKeyEvents(int keyCode) { final long token = Binder.clearCallingIdentity(); @@ -2511,27 +2705,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private MagnificationSpec getCompatibleMagnificationSpec(int windowId) { - try { - IBinder windowToken = mGlobalWindowTokens.get(windowId); - if (windowToken == null) { - windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId); - } - if (windowToken != null) { - return mWindowManagerService.getCompatibleMagnificationSpecForWindow( - windowToken); - } - } catch (RemoteException re) { - /* ignore */ + IBinder windowToken = mGlobalWindowTokens.get(windowId); + if (windowToken == null) { + windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId); + } + if (windowToken != null) { + return mWindowManagerService.getCompatibleMagnificationSpecForWindow( + windowToken); } return null; } private final class InvocationHandler extends Handler { - public static final int MSG_ON_GESTURE = 1; public static final int MSG_ON_KEY_EVENT = 2; - public static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 3; + public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3; public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4; + public static final int MSG_ON_WINDOWS_CHANGED = 5; public InvocationHandler(Looper looper) { super(looper, null, true); @@ -2545,18 +2735,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int gestureId = message.arg1; notifyGestureInternal(gestureId); } break; + case MSG_ON_KEY_EVENT: { KeyEvent event = (KeyEvent) message.obj; final int policyFlags = message.arg1; notifyKeyEventInternal(event, policyFlags); } break; - case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: { - notifyClearAccessibilityNodeInfoCacheInternal(); + + case MSG_CLEAR_ACCESSIBILITY_CACHE: { + notifyClearAccessibilityCacheInternal(); } break; + case MSG_ON_KEY_EVENT_TIMEOUT: { PendingEvent eventState = (PendingEvent) message.obj; setOnKeyEventResult(false, eventState.sequence); } break; + + case MSG_ON_WINDOWS_CHANGED: { + final int[] windowIds = (int[]) message.obj; + notifyWindowsChangedInternal(windowIds); + } break; + default: { throw new IllegalArgumentException("Unknown message: " + type); } @@ -2700,6 +2899,113 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + final class WindowsForAccessibilityCallback implements + WindowManagerInternal.WindowsForAccessibilityCallback { + + @Override + public void onWindowsForAccessibilityChanged(List<WindowInfo> windows) { + synchronized (mLock) { + List<WindowInfo> receivedWindows = (List<WindowInfo>) windows; + + // Populate the windows to report. + List<AccessibilityWindowInfo> reportedWindows = + new ArrayList<AccessibilityWindowInfo>(); + final int receivedWindowCount = receivedWindows.size(); + for (int i = 0; i < receivedWindowCount; i++) { + WindowInfo receivedWindow = receivedWindows.get(i); + AccessibilityWindowInfo reportedWindow = populateReportedWindow( + receivedWindow); + if (reportedWindow != null) { + reportedWindows.add(reportedWindow); + } + } + + if (DEBUG) { + Slog.i(LOG_TAG, "Windows changed: " + reportedWindows); + } + + // Let the policy update the focused and active windows. + mSecurityPolicy.updateWindowsLocked(reportedWindows); + } + } + + private AccessibilityWindowInfo populateReportedWindow(WindowInfo window) { + final int windowId = findWindowIdLocked(window.token); + if (windowId < 0) { + return null; + } + + AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); + + reportedWindow.setId(windowId); + reportedWindow.setType(getTypeForWindowManagerWindowType(window.type)); + reportedWindow.setLayer(window.layer); + reportedWindow.setFocused(window.focused); + reportedWindow.setBoundsInScreen(window.boundsInScreen); + + final int parentId = findWindowIdLocked(window.parentToken); + if (parentId >= 0) { + reportedWindow.setParentId(parentId); + } + + if (window.childTokens != null) { + final int childCount = window.childTokens.size(); + for (int i = 0; i < childCount; i++) { + IBinder childToken = window.childTokens.get(i); + final int childId = findWindowIdLocked(childToken); + if (childId >= 0) { + reportedWindow.addChild(childId); + } + } + } + + return reportedWindow; + } + + private int getTypeForWindowManagerWindowType(int windowType) { + switch (windowType) { + case WindowManager.LayoutParams.TYPE_APPLICATION: + case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: + case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING: + case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_BASE_APPLICATION: + case WindowManager.LayoutParams.TYPE_PHONE: + case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: + case WindowManager.LayoutParams.TYPE_TOAST: + case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: { + return AccessibilityWindowInfo.TYPE_APPLICATION; + } + + case WindowManager.LayoutParams.TYPE_INPUT_METHOD: + case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: { + return AccessibilityWindowInfo.TYPE_INPUT_METHOD; + } + + case WindowManager.LayoutParams.TYPE_KEYGUARD: + case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_SEARCH_BAR: + case WindowManager.LayoutParams.TYPE_STATUS_BAR: + case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: + case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: + case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: + case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: + case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: + case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: { + return AccessibilityWindowInfo.TYPE_SYSTEM; + } + + default: { + return -1; + } + } + } + } + final class SecurityPolicy { private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_CLICK @@ -2738,17 +3044,37 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED | AccessibilityEvent.TYPE_VIEW_SCROLLED | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED - | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED; + | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED + | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; + + public final List<AccessibilityWindowInfo> mWindows = + new ArrayList<AccessibilityWindowInfo>(); + + public int mActiveWindowId; + public int mFocusedWindowId; + public AccessibilityEvent mShowingFocusedWindowEvent; - private int mActiveWindowId; private boolean mTouchInteractionInProgress; - private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) { + private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) { final int eventType = event.getEventType(); switch (eventType) { // All events that are for changes in a global window // state should *always* be dispatched. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: + if (mWindowsForAccessibilityCallback != null) { + // OK, this is fun. Sometimes the focused window is notified + // it has focus before being shown. Historically this event + // means that the window is focused and can be introspected. + // But we still have not gotten the window state from the + // window manager, so delay the notification until then. + AccessibilityWindowInfo window = findWindowById(event.getWindowId()); + if (window == null || !window.isFocused()) { + mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event); + return false; + } + } + // $fall-through$ case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: // All events generated by the user touching the // screen should *always* be dispatched. @@ -2758,15 +3084,81 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START: case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END: - // These will change the active window, so dispatch. case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { return true; } // All events for changes in window content should be - // dispatched *only* if this window is the active one. - default: - return event.getWindowId() == mActiveWindowId; + // dispatched *only* if this window is one of the windows + // the accessibility layer reports which are windows + // that a sighted user can touch. + default: { + return isRetrievalAllowingWindow(event.getWindowId()); + } + } + } + + public void updateWindowsLocked(List<AccessibilityWindowInfo> windows) { + final int oldWindowCount = mWindows.size(); + for (int i = oldWindowCount - 1; i >= 0; i--) { + mWindows.remove(i).recycle(); + } + + mFocusedWindowId = -1; + if (!mTouchInteractionInProgress) { + mActiveWindowId = -1; + } + + // If the active window goes away while the user is touch exploring we + // reset the active window id and wait for the next hover event from + // under the user's finger to determine which one is the new one. It + // is possible that the finger is not moving and the input system + // filters out such events. + boolean activeWindowGone = true; + + final int windowCount = windows.size(); + if (windowCount > 0) { + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo window = windows.get(i); + final int windowId = window.getId(); + if (window.isFocused()) { + mFocusedWindowId = windowId; + if (!mTouchInteractionInProgress) { + mActiveWindowId = windowId; + window.setActive(true); + } else if (windowId == mActiveWindowId) { + activeWindowGone = false; + } + } + mWindows.add(window); + } + + if (mTouchInteractionInProgress && activeWindowGone) { + mActiveWindowId = -1; + } + + // Focused window may change the active one, so set the + // active window once we decided which it is. + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo window = mWindows.get(i); + if (window.getId() == mActiveWindowId) { + window.setActive(true); + } + } + } + + notifyWindowsChangedLocked(mWindows); + + // If we are delaying a window state change event as the window + // source was showing when it was fired, now is the time to send. + if (mShowingFocusedWindowEvent != null) { + final int windowId = mShowingFocusedWindowEvent.getWindowId(); + AccessibilityWindowInfo window = findWindowById(windowId); + if (window != null && window.isFocused()) { + // Sending does the recycle. + sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId); + } + mShowingFocusedWindowEvent = null; } } @@ -2781,67 +3173,96 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // the window that the user is currently touching. If the user is // touching a window that does not have input focus as soon as the // the user stops touching that window the focused window becomes - // the active one. + // the active one. Here we detect the touched window and make it + // active. In updateWindowsLocked() we update the focused window + // and if the user is not touching the screen, we make the focused + // window the active one. switch (eventType) { case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: { - if (getFocusedWindowId() == windowId) { - mActiveWindowId = windowId; + // If no service has the capability to introspect screen, + // we do not register callback in the window manager for + // window changes, so we have to ask the window manager + // what the focused window is to update the active one. + // The active window also determined events from which + // windows are delivered. + boolean focusedWindowActive = false; + synchronized (mLock) { + if (mWindowsForAccessibilityCallback == null) { + focusedWindowActive = true; + } + } + if (focusedWindowActive) { + if (windowId == getFocusedWindowId()) { + synchronized (mLock) { + mActiveWindowId = windowId; + } + } } } break; + case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: { // Do not allow delayed hover events to confuse us // which the active window is. - if (mTouchInteractionInProgress) { - mActiveWindowId = windowId; + synchronized (mLock) { + if (mTouchInteractionInProgress && mActiveWindowId != windowId) { + setActiveWindowLocked(windowId); + } } } break; } } public void onTouchInteractionStart() { - mTouchInteractionInProgress = true; + synchronized (mLock) { + mTouchInteractionInProgress = true; + } } public void onTouchInteractionEnd() { - mTouchInteractionInProgress = false; - // We want to set the active window to be current immediately - // after the user has stopped touching the screen since if the - // user types with the IME he should get a feedback for the - // letter typed in the text view which is in the input focused - // window. Note that we always deliver hover accessibility events - // (they are a result of user touching the screen) so change of - // the active window before all hover accessibility events from - // the touched window are delivered is fine. - mActiveWindowId = getFocusedWindowId(); + synchronized (mLock) { + mTouchInteractionInProgress = false; + // We want to set the active window to be current immediately + // after the user has stopped touching the screen since if the + // user types with the IME he should get a feedback for the + // letter typed in the text view which is in the input focused + // window. Note that we always deliver hover accessibility events + // (they are a result of user touching the screen) so change of + // the active window before all hover accessibility events from + // the touched window are delivered is fine. + setActiveWindowLocked(mFocusedWindowId); + } } - public int getRetrievalAllowingWindowLocked() { - return mActiveWindowId; + private void setActiveWindowLocked(int windowId) { + if (mActiveWindowId != windowId) { + mActiveWindowId = windowId; + final int windowCount = mWindows.size(); + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo window = mWindows.get(i); + window.setActive(window.getId() == windowId); + } + notifyWindowsChangedLocked(mWindows); + } } public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) { - return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId); + return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId); } public boolean canPerformActionLocked(Service service, int windowId, int action, Bundle arguments) { - return canRetrieveWindowContent(service) + return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId) && isActionPermitted(action); } - public boolean canRetrieveWindowContent(Service service) { - return (service.mAccessibilityServiceInfo.getCapabilities() - & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0; + public boolean canRetrieveWindowsLocked(Service service) { + return canRetrieveWindowContentLocked(service) && service.mRetrieveInteractiveWindows; } - public void enforceCanRetrieveWindowContent(Service service) throws RemoteException { - // This happens due to incorrect registration so make it apparent. - if (!canRetrieveWindowContent(service)) { - Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " + - "declare android:canRetrieveWindowContent."); - throw new RemoteException(); - } + public boolean canRetrieveWindowContentLocked(Service service) { + return (service.mAccessibilityServiceInfo.getCapabilities() + & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0; } public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) { @@ -2878,7 +3299,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private boolean isRetrievalAllowingWindow(int windowId) { - return (mActiveWindowId == windowId); + if (windowId == mActiveWindowId) { + return true; + } + return findWindowById(windowId) != null; + } + + private AccessibilityWindowInfo findWindowById(int windowId) { + final int windowCount = mWindows.size(); + for (int i = 0; i < windowCount; i++) { + AccessibilityWindowInfo window = mWindows.get(i); + if (window.getId() == windowId) { + return window; + } + } + return null; } private boolean isActionPermitted(int action) { @@ -2901,35 +3336,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private int getFocusedWindowId() { - try { - // We call this only on window focus change or after touch - // exploration gesture end and the shown windows are not that - // many, so the linear look up is just fine. - IBinder token = mWindowManagerService.getFocusedWindowToken(); - if (token != null) { - synchronized (mLock) { - int windowId = getFocusedWindowIdLocked(token, mGlobalWindowTokens); - if (windowId < 0) { - windowId = getFocusedWindowIdLocked(token, - getCurrentUserStateLocked().mWindowTokens); - } - return windowId; - } - } - } catch (RemoteException re) { - /* ignore */ - } - return -1; - } - - private int getFocusedWindowIdLocked(IBinder token, SparseArray<IBinder> windows) { - final int windowCount = windows.size(); - for (int i = 0; i < windowCount; i++) { - if (windows.valueAt(i) == token) { - return windows.keyAt(i); - } - } - return -1; + IBinder token = mWindowManagerService.getFocusedWindowToken(); + return findWindowIdLocked(token); } } diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java index 5f12cf41c1b5..c8b080e91dc8 100644 --- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java @@ -29,8 +29,6 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Handler; import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; @@ -38,8 +36,6 @@ import android.util.Property; import android.util.Slog; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.IMagnificationCallbacks; -import android.view.IWindowManager; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; @@ -48,10 +44,12 @@ import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.View; import android.view.ViewConfiguration; +import android.view.WindowManagerInternal; import android.view.accessibility.AccessibilityEvent; import android.view.animation.DecelerateInterpolator; import com.android.internal.os.SomeArgs; +import com.android.server.LocalServices; import java.util.Locale; @@ -94,8 +92,8 @@ import java.util.Locale; * * 6. The magnification scale will be persisted in settings and in the cloud. */ -public final class ScreenMagnifier extends IMagnificationCallbacks.Stub - implements EventStreamTransformation { +public final class ScreenMagnifier implements WindowManagerInternal.MagnificationCallbacks, + EventStreamTransformation { private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName(); @@ -127,7 +125,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub private final Rect mTempRect1 = new Rect(); private final Context mContext; - private final IWindowManager mWindowManager; + private final WindowManagerInternal mWindowManager; private final MagnificationController mMagnificationController; private final ScreenStateObserver mScreenStateObserver; @@ -191,8 +189,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) { mContext = context; - mWindowManager = IWindowManager.Stub.asInterface( - ServiceManager.getService("window")); + mWindowManager = LocalServices.getService(WindowManagerInternal.class); mAms = service; mLongAnimationDuration = context.getResources().getInteger( @@ -208,11 +205,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub mMagnificationController = new MagnificationController(mLongAnimationDuration); mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController); - try { - mWindowManager.setMagnificationCallbacks(this); - } catch (RemoteException re) { - /* ignore */ - } + mWindowManager.setMagnificationCallbacks(this); transitionToState(STATE_DETECTING); } @@ -378,11 +371,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub @Override public void onDestroy() { mScreenStateObserver.destroy(); - try { - mWindowManager.setMagnificationCallbacks(null); - } catch (RemoteException re) { - /* ignore */ - } + mWindowManager.setMagnificationCallbacks(null); } private void handleMotionEventStateDelegating(MotionEvent event, @@ -462,7 +451,7 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub } return mTempPointerProperties; } - + private void transitionToState(int state) { if (DEBUG_STATE_TRANSITIONS) { switch (state) { @@ -1120,15 +1109,10 @@ public final class ScreenMagnifier extends IMagnificationCallbacks.Stub if (DEBUG_SET_MAGNIFICATION_SPEC) { Slog.i(LOG_TAG, "Sending: " + spec); } - try { - mSentMagnificationSpec.scale = spec.scale; - mSentMagnificationSpec.offsetX = spec.offsetX; - mSentMagnificationSpec.offsetY = spec.offsetY; - mWindowManager.setMagnificationSpec( - MagnificationSpec.obtain(spec)); - } catch (RemoteException re) { - /* ignore */ - } + mSentMagnificationSpec.scale = spec.scale; + mSentMagnificationSpec.offsetX = spec.offsetX; + mSentMagnificationSpec.offsetY = spec.offsetY; + mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec)); } } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index e5615c0441a2..e26747cfc582 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.app.AppOpsManager; 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.RemoteException; 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 @@ public class AppOpsService extends IAppOpsService.Stub { = 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 @@ public class AppOpsService extends IAppOpsService.Stub { } @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 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + 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 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + + 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 68b779c023e4..d2513d36814f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2434,7 +2434,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { }; 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 28eb948d4169..52f9aa9fca64 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -84,24 +84,27 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub 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 class VibratorService extends IVibratorService.Stub } 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 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub // 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 1345cfd03879..033b9677f307 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 @@ public final class ActiveServices { // 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 09ec4f6c2b43..9783a663b6af 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { // 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 3414dafb2d7d..fa4a9d12feeb 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub { 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 e2226aa5a649..7c2de8b9f82e 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(ZEN_MODE, false, this); - resolver.registerContentObserver(MODE_RINGER, - false, this); update(null); } @@ -1162,9 +1153,6 @@ public class NotificationManagerService extends SystemService { if (ZEN_MODE.equals(uri)) { updateZenMode(); } - if (MODE_RINGER.equals(uri)) { - updateRingerMode(); - } } } @@ -1230,7 +1218,6 @@ public class NotificationManagerService extends SystemService { Settings.Global.DEVICE_PROVISIONED, 0)) { mDisableNotificationAlerts = true; } - updateRingerMode(); updateZenMode(); // register for various Intents @@ -1694,10 +1681,6 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { useDefaultVibrate ? mDefaultVibrationPattern : mFallbackVibrationPattern, ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1); + ? 0: -1, notification.audioStreamType); } finally { Binder.restoreCallingIdentity(identity); } @@ -2033,7 +2016,7 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } } - 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 @@ public class NotificationManagerService extends SystemService { 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 b7e367b5fb6a..ae1cfab4c8c7 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 @@ public final class Installer extends SystemService { 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 cbcf408236a1..cb4cf982ca00 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 6e12e4183c81..d70c725c8990 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 android.util.Xml; 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 @@ public final class SELinuxMMAC { 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 @@ public final class SELinuxMMAC { /** * 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 @@ public final class SELinuxMMAC { /** * 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 @@ public final class SELinuxMMAC { /** * 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 @@ public final class SELinuxMMAC { 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 3ca628a06bd2..807262a2e7ff 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 @@ class AutomaticBrightnessController { 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 000000000000..35b7f99d8813 --- /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 382d7b4a94af..000000000000 --- 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 0c682587f19d..266527d32e97 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -529,8 +529,9 @@ public class WindowAnimator { mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers(); //TODO (multidisplay): Magnification is supported only for the default display. - if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) { - mService.mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); + if (mService.mAccessibilityController != null + && displayId == Display.DEFAULT_DISPLAY) { + mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked(); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a4f960e73019..adfb2bd0de26 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.DisplayInfo; import android.view.Gravity; import android.view.IApplicationToken; import android.view.IInputFilter; -import android.view.IMagnificationCallbacks; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindow; @@ -418,7 +417,7 @@ public class WindowManagerService extends IWindowManager.Stub IInputMethodManager mInputMethodManager; - DisplayMagnifier mDisplayMagnifier; + AccessibilityController mAccessibilityController; final SurfaceSession mFxSession; Watermark mWatermark; @@ -2439,9 +2438,9 @@ public class WindowManagerService extends IWindowManager.Stub win.mExiting = true; } //TODO (multidisplay): Magnification is supported only for the default display. - if (mDisplayMagnifier != null + if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onWindowTransitionLocked(win, transit); + mAccessibilityController.onWindowTransitionLocked(win, transit); } } if (win.mExiting || win.mWinAnimator.isAnimating()) { @@ -2759,11 +2758,12 @@ public class WindowManagerService extends IWindowManager.Stub public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { synchronized (mWindowMap) { - if (mDisplayMagnifier != null) { + if (mAccessibilityController != null) { WindowState window = mWindowMap.get(token); //TODO (multidisplay): Magnification is supported only for the default display. if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate); + mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle, + immediate); } } } @@ -2998,9 +2998,9 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.destroySurfaceLocked(); } //TODO (multidisplay): Magnification is supported only for the default - if (mDisplayMagnifier != null + if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onWindowTransitionLocked(win, transit); + mAccessibilityController.onWindowTransitionLocked(win, transit); } } } @@ -3143,86 +3143,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override - public void getWindowFrame(IBinder token, Rect outBounds) { - if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, - "getWindowInfo()")) { - throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); - } - synchronized (mWindowMap) { - WindowState windowState = mWindowMap.get(token); - if (windowState != null) { - outBounds.set(windowState.mFrame); - } else { - outBounds.setEmpty(); - } - } - } - - @Override - public void setMagnificationSpec(MagnificationSpec spec) { - if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, - "setMagnificationSpec()")) { - throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); - } - synchronized (mWindowMap) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.setMagnificationSpecLocked(spec); - } else { - throw new IllegalStateException("Magnification callbacks not set!"); - } - } - if (Binder.getCallingPid() != android.os.Process.myPid()) { - spec.recycle(); - } - } - - @Override - public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { - if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, - "getCompatibleMagnificationSpecForWindow()")) { - throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); - } - synchronized (mWindowMap) { - WindowState windowState = mWindowMap.get(windowToken); - if (windowState == null) { - return null; - } - MagnificationSpec spec = null; - if (mDisplayMagnifier != null) { - spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState); - } - if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { - return null; - } - spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec); - spec.scale *= windowState.mGlobalScale; - return spec; - } - } - - @Override - public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) { - if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, - "setMagnificationCallbacks()")) { - throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); - } - synchronized (mWindowMap) { - if (mDisplayMagnifier == null) { - mDisplayMagnifier = new DisplayMagnifier(this, callbacks); - } else { - if (callbacks == null) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.destroyLocked(); - mDisplayMagnifier = null; - } - } else { - throw new IllegalStateException("Magnification callbacks already set!"); - } - } - } - } - private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, int transit, boolean enter) { // Only apply an animation if the display isn't frozen. If it is @@ -3402,8 +3322,8 @@ public class WindowManagerService extends IWindowManager.Stub win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false); //TODO (multidisplay): Magnification is supported only for the default - if (mDisplayMagnifier != null && win.isDefaultDisplay()) { - mDisplayMagnifier.onWindowTransitionLocked(win, + if (mAccessibilityController != null && win.isDefaultDisplay()) { + mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } changed = true; @@ -4259,9 +4179,9 @@ public class WindowManagerService extends IWindowManager.Stub } WindowState window = wtoken.findMainWindow(); //TODO (multidisplay): Magnification is supported only for the default display. - if (window != null && mDisplayMagnifier != null + if (window != null && mAccessibilityController != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onAppWindowTransitionLocked(window, transit); + mAccessibilityController.onAppWindowTransitionLocked(window, transit); } changed = true; } @@ -4281,9 +4201,9 @@ public class WindowManagerService extends IWindowManager.Stub win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_ENTER, true); //TODO (multidisplay): Magnification is supported only for the default - if (mDisplayMagnifier != null + if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onWindowTransitionLocked(win, + mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_ENTER); } } @@ -4298,9 +4218,9 @@ public class WindowManagerService extends IWindowManager.Stub win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_EXIT, false); //TODO (multidisplay): Magnification is supported only for the default - if (mDisplayMagnifier != null + if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onWindowTransitionLocked(win, + mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } } @@ -5278,19 +5198,6 @@ public class WindowManagerService extends IWindowManager.Stub ShutdownThread.rebootSafeMode(mContext, confirm); } - @Override - public void setInputFilter(IInputFilter filter) { - if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) { - throw new SecurityException("Requires FILTER_EVENTS permission"); - } - mInputManager.setInputFilter(filter); - } - - @Override - public void setTouchExplorationEnabled(boolean enabled) { - mPolicy.setTouchExplorationEnabled(enabled); - } - public void updateRelatedUserIds(final int[] relatedUserIds) { synchronized (mWindowMap) { mRelatedUserIds = relatedUserIds; @@ -6106,9 +6013,9 @@ public class WindowManagerService extends IWindowManager.Stub } //TODO (multidisplay): Magnification is supported only for the default display. - if (mDisplayMagnifier != null + if (mAccessibilityController != null && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation); + mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation); } return true; @@ -7015,21 +6922,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override - public IBinder getFocusedWindowToken() { - if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, - "getFocusedWindowToken()")) { - throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); - } - synchronized (mWindowMap) { - WindowState windowState = getFocusedWindowLocked(); - if (windowState != null) { - return windowState.mClient.asBinder(); - } - return null; - } - } - private WindowState getFocusedWindow() { synchronized (mWindowMap) { return getFocusedWindowLocked(); @@ -8209,9 +8101,9 @@ public class WindowManagerService extends IWindowManager.Stub } //TODO (multidisplay): Magnification is supported only for the default display. - if (mDisplayMagnifier != null && anyLayerChanged + if (mAccessibilityController != null && anyLayerChanged && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) { - mDisplayMagnifier.onWindowLayersChangedLocked(); + mAccessibilityController.onWindowLayersChangedLocked(); } } @@ -9844,6 +9736,11 @@ public class WindowManagerService extends IWindowManager.Stub final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); + + if (mAccessibilityController != null) { + mAccessibilityController.onWindowFocusChangedLocked(); + } + int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus); if (imWindowChanged && oldFocus != mInputMethodWindow) { @@ -10957,5 +10854,100 @@ public class WindowManagerService extends IWindowManager.Stub public void requestTraversalFromDisplayManager() { requestTraversal(); } + + @Override + public void setMagnificationSpec(MagnificationSpec spec) { + synchronized (mWindowMap) { + if (mAccessibilityController != null) { + mAccessibilityController.setMagnificationSpecLocked(spec); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + if (Binder.getCallingPid() != android.os.Process.myPid()) { + spec.recycle(); + } + } + + @Override + public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { + synchronized (mWindowMap) { + WindowState windowState = mWindowMap.get(windowToken); + if (windowState == null) { + return null; + } + MagnificationSpec spec = null; + if (mAccessibilityController != null) { + spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState); + } + if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { + return null; + } + spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec); + spec.scale *= windowState.mGlobalScale; + return spec; + } + } + + @Override + public void setMagnificationCallbacks(MagnificationCallbacks callbacks) { + synchronized (mWindowMap) { + if (mAccessibilityController == null) { + mAccessibilityController = new AccessibilityController( + WindowManagerService.this); + } + mAccessibilityController.setMagnificationCallbacksLocked(callbacks); + if (!mAccessibilityController.hasCallbacksLocked()) { + mAccessibilityController = null; + } + } + } + + @Override + public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) { + synchronized (mWindowMap) { + if (mAccessibilityController == null) { + mAccessibilityController = new AccessibilityController( + WindowManagerService.this); + } + mAccessibilityController.setWindowsForAccessibilityCallback(callback); + if (!mAccessibilityController.hasCallbacksLocked()) { + mAccessibilityController = null; + } + } + } + + @Override + public void setInputFilter(IInputFilter filter) { + mInputManager.setInputFilter(filter); + } + + @Override + public IBinder getFocusedWindowToken() { + synchronized (mWindowMap) { + WindowState windowState = getFocusedWindowLocked(); + if (windowState != null) { + return windowState.mClient.asBinder(); + } + return null; + } + } + + @Override + public boolean isKeyguardLocked() { + return isKeyguardLocked(); + } + + @Override + public void getWindowFrame(IBinder token, Rect outBounds) { + synchronized (mWindowMap) { + WindowState windowState = mWindowMap.get(token); + if (windowState != null) { + outBounds.set(windowState.mFrame); + } else { + outBounds.setEmpty(); + } + } + } } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 93f6d2292e6b..2cd600094f71 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -952,8 +952,8 @@ class WindowStateAnimator { tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix()); } //TODO (multidisplay): Magnification is supported only for the default display. - if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) { - MagnificationSpec spec = mService.mDisplayMagnifier + if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { + MagnificationSpec spec = mService.mAccessibilityController .getMagnificationSpecForWindowLocked(mWin); if (spec != null && !spec.isNop()) { tmpMatrix.postScale(spec.scale, spec.scale); @@ -1032,8 +1032,8 @@ class WindowStateAnimator { && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer); MagnificationSpec spec = null; //TODO (multidisplay): Magnification is supported only for the default display. - if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) { - spec = mService.mDisplayMagnifier.getMagnificationSpecForWindowLocked(mWin); + if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { + spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin); } if (applyUniverseTransformation || spec != null) { final Rect frame = mWin.mFrame; @@ -1565,9 +1565,9 @@ class WindowStateAnimator { } applyAnimationLocked(transit, true); //TODO (multidisplay): Magnification is supported only for the default display. - if (mService.mDisplayMagnifier != null + if (mService.mAccessibilityController != null && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) { - mService.mDisplayMagnifier.onWindowTransitionLocked(mWin, transit); + mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit); } } 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 90b6abce3a37..b12ed94467b7 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 @@ package com.android.framework.permission.tests; 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 class VibratorServicePermissionTest extends TestCase { */ 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 class VibratorServicePermissionTest extends TestCase { */ 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 |