Merge "Adding render stats APIs to UiAutomation (framework)."

This commit is contained in:
Svetoslav
2014-04-10 19:32:59 +00:00
committed by Android (Google) Code Review
21 changed files with 1004 additions and 18 deletions

View File

@@ -420,6 +420,8 @@ aidl_files := \
frameworks/base/core/java/android/view/MotionEvent.aidl \
frameworks/base/core/java/android/view/Surface.aidl \
frameworks/base/core/java/android/view/WindowManager.aidl \
frameworks/base/core/java/android/view/WindowAnimationFrameStats.aidl \
frameworks/base/core/java/android/view/WindowContentFrameStats.aidl \
frameworks/base/core/java/android/widget/RemoteViews.aidl \
frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
frameworks/base/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \

View File

@@ -4752,9 +4752,13 @@ package android.app {
}
public final class UiAutomation {
method public void clearWindowAnimationFrameStats();
method public boolean clearWindowContentFrameStats(int);
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 android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public boolean injectInputEvent(android.view.InputEvent, boolean);
method public final boolean performGlobalAction(int);
@@ -28445,6 +28449,18 @@ package android.view {
method public static android.view.FocusFinder getInstance();
}
public abstract class FrameStats {
ctor public FrameStats();
method public final long getEndTimeNano();
method public final int getFrameCount();
method public final long getFramePresentedTimeNano(int);
method public final long getRefreshPeriodNano();
method public final long getStartTimeNano();
field public static final long UNDEFINED_TIME_NANO = -1L; // 0xffffffffffffffffL
field protected long[] mFramesPresentedTimeNano;
field protected long mRefreshPeriodNano;
}
public class GestureDetector {
ctor public deprecated GestureDetector(android.view.GestureDetector.OnGestureListener, android.os.Handler);
ctor public deprecated GestureDetector(android.view.GestureDetector.OnGestureListener);
@@ -30769,6 +30785,20 @@ package android.view {
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback);
}
public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public final class WindowContentFrameStats extends android.view.FrameStats implements android.os.Parcelable {
method public int describeContents();
method public long getFramePostedTimeNano(int);
method public long getFrameReadyTimeNano(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public class WindowId implements android.os.Parcelable {
method public int describeContents();
method public boolean isFocused();

View File

@@ -19,6 +19,8 @@ package android.app;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.graphics.Bitmap;
import android.view.InputEvent;
import android.view.WindowContentFrameStats;
import android.view.WindowAnimationFrameStats;
import android.os.ParcelFileDescriptor;
/**
@@ -26,7 +28,7 @@ import android.os.ParcelFileDescriptor;
* on behalf of an instrumentation that it runs. These operations require
* special permissions which the shell user has but the instrumentation does
* not. Running privileged operations by the shell user on behalf of an
* instrumentation is needed for running UiTestCases.
* instrumentation is needed for running UiTestCases.
*
* {@hide}
*/
@@ -37,4 +39,8 @@ interface IUiAutomationConnection {
boolean setRotation(int rotation);
Bitmap takeScreenshot(int width, int height);
void shutdown();
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
WindowAnimationFrameStats getWindowAnimationFrameStats();
}

View File

@@ -33,6 +33,8 @@ import android.view.Display;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.WindowAnimationFrameStats;
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -674,6 +676,148 @@ public final class UiAutomation {
}
}
/**
* Clears the frame statistics for the content of a given window. These
* statistics contain information about the most recently rendered content
* frames.
*
* @param windowId The window id.
* @return Whether the window is present and its frame statistics
* were cleared.
*
* @see android.view.WindowContentFrameStats
* @see #getWindowContentFrameStats(int)
* @see #getWindows()
* @see AccessibilityWindowInfo#getId() AccessibilityWindowInfo.getId()
*/
public boolean clearWindowContentFrameStats(int windowId) {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
try {
if (DEBUG) {
Log.i(LOG_TAG, "Clearing content frame stats for window: " + windowId);
}
// Calling out without a lock held.
return mUiAutomationConnection.clearWindowContentFrameStats(windowId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error clearing window content frame stats!", re);
}
return false;
}
/**
* Gets the frame statistics for a given window. These statistics contain
* information about the most recently rendered content frames.
* <p>
* A typical usage requires clearing the window frame statistics via {@link
* #clearWindowContentFrameStats(int)} followed by an interaction with the UI and
* finally getting the window frame statistics via calling this method.
* </p>
* <pre>
* // Assume we have at least one window.
* final int windowId = getWindows().get(0).getId();
*
* // Start with a clean slate.
* uiAutimation.clearWindowContentFrameStats(windowId);
*
* // Do stuff with the UI.
*
* // Get the frame statistics.
* WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
* </pre>
*
* @param windowId The window id.
* @return The window frame statistics, or null if the window is not present.
*
* @see android.view.WindowContentFrameStats
* @see #clearWindowContentFrameStats(int)
* @see #getWindows()
* @see AccessibilityWindowInfo#getId() AccessibilityWindowInfo.getId()
*/
public WindowContentFrameStats getWindowContentFrameStats(int windowId) {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
try {
if (DEBUG) {
Log.i(LOG_TAG, "Getting content frame stats for window: " + windowId);
}
// Calling out without a lock held.
return mUiAutomationConnection.getWindowContentFrameStats(windowId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error getting window content frame stats!", re);
}
return null;
}
/**
* Clears the window animation rendering statistics. These statistics contain
* information about the most recently rendered window animation frames, i.e.
* for window transition animations.
*
* @see android.view.WindowAnimationFrameStats
* @see #getWindowAnimationFrameStats()
* @see android.R.styleable#WindowAnimation
*/
public void clearWindowAnimationFrameStats() {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
try {
if (DEBUG) {
Log.i(LOG_TAG, "Clearing window animation frame stats");
}
// Calling out without a lock held.
mUiAutomationConnection.clearWindowAnimationFrameStats();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error clearing window animation frame stats!", re);
}
}
/**
* Gets the window animation frame statistics. These statistics contain
* information about the most recently rendered window animation frames, i.e.
* for window transition animations.
*
* <p>
* A typical usage requires clearing the window animation frame statistics via
* {@link #clearWindowAnimationFrameStats()} followed by an interaction that causes
* a window transition which uses a window animation and finally getting the window
* animation frame statistics by calling this method.
* </p>
* <pre>
* // Start with a clean slate.
* uiAutimation.clearWindowAnimationFrameStats();
*
* // Do stuff to trigger a window transition.
*
* // Get the frame statistics.
* WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
* </pre>
*
* @return The window animation frame statistics.
*
* @see android.view.WindowAnimationFrameStats
* @see #clearWindowAnimationFrameStats()
* @see android.R.styleable#WindowAnimation
*/
public WindowAnimationFrameStats getWindowAnimationFrameStats() {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
try {
if (DEBUG) {
Log.i(LOG_TAG, "Getting window animation frame stats");
}
// Calling out without a lock held.
return mUiAutomationConnection.getWindowAnimationFrameStats();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error getting window animation frame stats!", re);
}
return null;
}
private static float getDegreesForRotation(int value) {
switch (value) {
case Surface.ROTATION_90: {

View File

@@ -22,12 +22,15 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.IWindowManager;
import android.view.InputEvent;
import android.view.SurfaceControl;
import android.view.WindowAnimationFrameStats;
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
@@ -47,6 +50,9 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Service.WINDOW_SERVICE));
private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
private final Object mLock = new Object();
private final Binder mToken = new Binder();
@@ -143,6 +149,76 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
}
@Override
public boolean clearWindowContentFrameStats(int windowId) throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
IBinder token = mAccessibilityManager.getWindowToken(windowId);
if (token == null) {
return false;
}
return mWindowManager.clearWindowContentFrameStats(token);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public WindowContentFrameStats getWindowContentFrameStats(int windowId) throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
IBinder token = mAccessibilityManager.getWindowToken(windowId);
if (token == null) {
return null;
}
return mWindowManager.getWindowContentFrameStats(token);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void clearWindowAnimationFrameStats() {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
SurfaceControl.clearAnimationFrameStats();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public WindowAnimationFrameStats getWindowAnimationFrameStats() {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
WindowAnimationFrameStats stats = new WindowAnimationFrameStats();
SurfaceControl.getAnimationFrameStats(stats);
return stats;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void shutdown() {
synchronized (mLock) {

View File

@@ -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 AnimationRenderStats;

View File

@@ -0,0 +1,97 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/**
* This is the base class for frame statistics.
*/
public abstract class FrameStats {
/**
* Undefined time.
*/
public static final long UNDEFINED_TIME_NANO = -1;
protected long mRefreshPeriodNano;
protected long[] mFramesPresentedTimeNano;
/**
* Gets the refresh period of the display hosting the window(s) for
* which these statistics apply.
*
* @return The refresh period in nanoseconds.
*/
public final long getRefreshPeriodNano() {
return mRefreshPeriodNano;
}
/**
* Gets the number of frames for which there is data.
*
* @return The number of frames.
*/
public final int getFrameCount() {
return mFramesPresentedTimeNano != null
? mFramesPresentedTimeNano.length : 0;
}
/**
* Gets the start time of the interval for which these statistics
* apply. The start interval is the time when the first frame was
* presented.
*
* @return The start time in nanoseconds or {@link #UNDEFINED_TIME_NANO}
* if there is no frame data.
*/
public final long getStartTimeNano() {
if (getFrameCount() <= 0) {
return UNDEFINED_TIME_NANO;
}
return mFramesPresentedTimeNano[0];
}
/**
* Gets the end time of the interval for which these statistics
* apply. The end interval is the time when the last frame was
* presented.
*
* @return The end time in nanoseconds or {@link #UNDEFINED_TIME_NANO}
* if there is no frame data.
*/
public final long getEndTimeNano() {
if (getFrameCount() <= 0) {
return UNDEFINED_TIME_NANO;
}
return mFramesPresentedTimeNano[mFramesPresentedTimeNano.length - 1];
}
/**
* Get the time a frame at a given index was presented.
*
* @param index The frame index.
* @return The presented time in nanoseconds or {@link #UNDEFINED_TIME_NANO}
* if the frame is not presented yet.
*/
public final long getFramePresentedTimeNano(int index) {
if (mFramesPresentedTimeNano == null) {
throw new IndexOutOfBoundsException();
}
return mFramesPresentedTimeNano[index];
}
}

View File

@@ -37,6 +37,7 @@ import android.view.MotionEvent;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.IInputFilter;
import android.view.WindowContentFrameStats;
/**
* System private interface to the window manager.
@@ -233,4 +234,20 @@ interface IWindowManager
* Device is in safe mode.
*/
boolean isSafeModeEnabled();
/**
* Clears the frame statistics for a given window.
*
* @param token The window token.
* @return Whether the frame statistics were cleared.
*/
boolean clearWindowContentFrameStats(IBinder token);
/**
* Gets the content frame statistics for a given window.
*
* @param token The window token.
* @return The frame statistics or null if the window does not exist.
*/
WindowContentFrameStats getWindowContentFrameStats(IBinder token);
}

View File

@@ -20,7 +20,6 @@ import dalvik.system.CloseGuard;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Region;
import android.view.Surface;
import android.os.IBinder;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
@@ -59,6 +58,11 @@ public class SurfaceControl {
private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b);
private static native void nativeSetLayerStack(long nativeObject, int layerStack);
private static native boolean nativeClearContentFrameStats(long nativeObject);
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
private static native boolean nativeClearAnimationFrameStats();
private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
@@ -357,6 +361,24 @@ public class SurfaceControl {
nativeSetTransparentRegionHint(mNativeObject, region);
}
public boolean clearContentFrameStats() {
checkNotReleased();
return nativeClearContentFrameStats(mNativeObject);
}
public boolean getContentFrameStats(WindowContentFrameStats outStats) {
checkNotReleased();
return nativeGetContentFrameStats(mNativeObject, outStats);
}
public static boolean clearAnimationFrameStats() {
return nativeClearAnimationFrameStats();
}
public static boolean getAnimationFrameStats(WindowAnimationFrameStats outStats) {
return nativeGetAnimationFrameStats(outStats);
}
/**
* Sets an alpha value for the entire Surface. This value is combined with the
* per-pixel alpha. It may be used with opaque Surfaces.
@@ -542,7 +564,6 @@ public class SurfaceControl {
return nativeGetBuiltInDisplay(builtInDisplayId);
}
/**
* Copy the current screen contents into the provided {@link Surface}
*
@@ -592,7 +613,6 @@ public class SurfaceControl {
screenshot(display, consumer, 0, 0, 0, 0, true, false);
}
/**
* Copy the current screen contents into a bitmap and return it.
*
@@ -626,8 +646,8 @@ public class SurfaceControl {
}
/**
* Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all
* Surfaces in the screenshot.
* Like {@link SurfaceControl#screenshot(int, int, int, int, boolean)} but
* includes all Surfaces in the screenshot.
*
* @param width The desired width of the returned bitmap; the raw
* screen will be scaled down to this size.

View File

@@ -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 WindowAnimationFrameStats;

View File

@@ -0,0 +1,94 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/**
* This class contains window animation frame statistics. For example, a window
* animation is usually performed when the application is transitioning from one
* activity to another. The frame statistics are a snapshot for the time interval
* from {@link #getStartTimeNano()} to {@link #getEndTimeNano()}.
* <p>
* The key idea is that in order to provide a smooth user experience the system should
* run window animations at a specific time interval obtained by calling {@link
* #getRefreshPeriodNano()}. If the system does not render a frame every refresh
* period the user will see irregular window transitions. The time when the frame was
* actually presented on the display by calling {@link #getFramePresentedTimeNano(int)}.
*/
public final class WindowAnimationFrameStats extends FrameStats implements Parcelable {
/**
* @hide
*/
public WindowAnimationFrameStats() {
/* do nothing */
}
/**
* Initializes this isntance.
*
* @param refreshPeriodNano The display refresh period.
* @param framesPresentedTimeNano The presented frame times.
*
* @hide
*/
public void init(long refreshPeriodNano, long[] framesPresentedTimeNano) {
mRefreshPeriodNano = refreshPeriodNano;
mFramesPresentedTimeNano = framesPresentedTimeNano;
}
private WindowAnimationFrameStats(Parcel parcel) {
mRefreshPeriodNano = parcel.readLong();
mFramesPresentedTimeNano = parcel.createLongArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeLong(mRefreshPeriodNano);
parcel.writeLongArray(mFramesPresentedTimeNano);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("WindowAnimationFrameStats[");
builder.append("frameCount:" + getFrameCount());
builder.append(", fromTimeNano:" + getStartTimeNano());
builder.append(", toTimeNano:" + getEndTimeNano());
builder.append(']');
return builder.toString();
}
public static final Creator<WindowAnimationFrameStats> CREATOR =
new Creator<WindowAnimationFrameStats>() {
@Override
public WindowAnimationFrameStats createFromParcel(Parcel parcel) {
return new WindowAnimationFrameStats(parcel);
}
@Override
public WindowAnimationFrameStats[] newArray(int size) {
return new WindowAnimationFrameStats[size];
}
};
}

View File

@@ -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 WindowContentFrameStats;

View File

@@ -0,0 +1,152 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/**
* This class contains window content frame statistics. For example, a window content
* is rendred in frames when a view is scrolled. The frame statistics are a snapshot
* for the time interval from {@link #getStartTimeNano()} to {@link #getEndTimeNano()}.
* <p>
* The key idea is that in order to provide a smooth user experience an application
* has to draw a frame at a specific time interval obtained by calling {@link
* #getRefreshPeriodNano()}. If the application does not render a frame every refresh
* period the user will see irregular UI transitions.
* </p>
* <p>
* An application posts a frame for presentation by synchronously rendering its contents
* in a buffer which is then posted or posting a buffer to which the application is
* asychronously rendering the content via GL. After the frame is posted and rendered
* (potentially asynchronosly) it is presented to the user. The time a frame was posted
* can be obtained via {@link #getFramePostedTimeNano(int)}, the time a frame content
* was rendered and ready for dsiplay (GL case) via {@link #getFrameReadyTimeNano(int)},
* and the time a frame was presented on the screen via {@link #getFramePresentedTimeNano(int)}.
* </p>
*/
public final class WindowContentFrameStats extends FrameStats implements Parcelable {
private long[] mFramesPostedTimeNano;
private long[] mFramesReadyTimeNano;
/**
* @hide
*/
public WindowContentFrameStats() {
/* do nothing */
}
/**
* Initializes this isntance.
*
* @param refreshPeriodNano The display refresh period.
* @param framesPostedTimeNano The times in milliseconds for when the frame contents were posted.
* @param framesPresentedTimeNano The times in milliseconds for when the frame contents were presented.
* @param framesReadyTimeNano The times in milliseconds for when the frame contents were ready to be presented.
*
* @hide
*/
public void init(long refreshPeriodNano, long[] framesPostedTimeNano,
long[] framesPresentedTimeNano, long[] framesReadyTimeNano) {
mRefreshPeriodNano = refreshPeriodNano;
mFramesPostedTimeNano = framesPostedTimeNano;
mFramesPresentedTimeNano = framesPresentedTimeNano;
mFramesReadyTimeNano = framesReadyTimeNano;
}
private WindowContentFrameStats(Parcel parcel) {
mRefreshPeriodNano = parcel.readLong();
mFramesPostedTimeNano = parcel.createLongArray();
mFramesPresentedTimeNano = parcel.createLongArray();
mFramesReadyTimeNano = parcel.createLongArray();
}
/**
* Get the time a frame at a given index was posted by the producer (e.g. the application).
* It is either explicitly set or defaulted to the time when the render buffer was posted.
* <p>
* <strong>Note:</strong> A frame can be posted and still it contents being rendered
* asynchronously in GL. To get the time the frame content was completely rendered and
* ready to display call {@link #getFrameReadyTimeNano(int)}.
* </p>
*
* @param index The frame index.
* @return The posted time in nanoseconds.
*/
public long getFramePostedTimeNano(int index) {
if (mFramesPostedTimeNano == null) {
throw new IndexOutOfBoundsException();
}
return mFramesPostedTimeNano[index];
}
/**
* Get the time a frame at a given index was ready for presentation.
* <p>
* <strong>Note:</strong> A frame can be posted and still it contents being rendered
* asynchronously in GL. In such a case this is the time when the frame contents were
* completely rendered.
* </p>
*
* @param index The frame index.
* @return The ready time in nanoseconds or {@link #UNDEFINED_TIME_NANO}
* if the frame is not ready yet.
*/
public long getFrameReadyTimeNano(int index) {
if (mFramesReadyTimeNano == null) {
throw new IndexOutOfBoundsException();
}
return mFramesReadyTimeNano[index];
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeLong(mRefreshPeriodNano);
parcel.writeLongArray(mFramesPostedTimeNano);
parcel.writeLongArray(mFramesPresentedTimeNano);
parcel.writeLongArray(mFramesReadyTimeNano);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("WindowContentFrameStats[");
builder.append("frameCount:" + getFrameCount());
builder.append(", fromTimeNano:" + getStartTimeNano());
builder.append(", toTimeNano:" + getEndTimeNano());
builder.append(']');
return builder.toString();
}
public static final Parcelable.Creator<WindowContentFrameStats> CREATOR =
new Creator<WindowContentFrameStats>() {
@Override
public WindowContentFrameStats createFromParcel(Parcel parcel) {
return new WindowContentFrameStats(parcel);
}
@Override
public WindowContentFrameStats[] newArray(int size) {
return new WindowContentFrameStats[size];
}
};
}

View File

@@ -57,4 +57,6 @@ interface IAccessibilityManager {
void temporaryEnableAccessibilityStateUntilKeyguardRemoved(in ComponentName service,
boolean touchExplorationEnabled);
IBinder getWindowToken(int windowId);
}

View File

@@ -34,6 +34,7 @@
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayInfo.h>
#include <ui/FrameStats.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -64,6 +65,16 @@ void DeleteScreenshot(void* addr, void* context) {
delete ((ScreenshotClient*) context);
}
static struct {
nsecs_t UNDEFINED_TIME_NANO;
jmethodID init;
} gWindowContentFrameStatsClassInfo;
static struct {
nsecs_t UNDEFINED_TIME_NANO;
jmethodID init;
} gWindowAnimationFrameStatsClassInfo;
// ----------------------------------------------------------------------------
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
@@ -371,6 +382,151 @@ static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
SurfaceComposerClient::unblankDisplay(token);
}
static jboolean nativeClearContentFrameStats(JNIEnv* env, jclass clazz, jlong nativeObject) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
status_t err = ctrl->clearLayerFrameStats();
if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
// The other end is not ready, just report we failed.
if (err == NO_INIT) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static jboolean nativeGetContentFrameStats(JNIEnv* env, jclass clazz, jlong nativeObject,
jobject outStats) {
FrameStats stats;
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
status_t err = ctrl->getLayerFrameStats(&stats);
if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
// The other end is not ready, fine just return empty stats.
if (err == NO_INIT) {
return JNI_FALSE;
}
jlong refreshPeriodNano = static_cast<jlong>(stats.refreshPeriodNano);
size_t frameCount = stats.desiredPresentTimesNano.size();
jlongArray postedTimesNanoDst = env->NewLongArray(frameCount);
if (postedTimesNanoDst == NULL) {
return JNI_FALSE;
}
jlongArray presentedTimesNanoDst = env->NewLongArray(frameCount);
if (presentedTimesNanoDst == NULL) {
return JNI_FALSE;
}
jlongArray readyTimesNanoDst = env->NewLongArray(frameCount);
if (readyTimesNanoDst == NULL) {
return JNI_FALSE;
}
nsecs_t postedTimesNanoSrc[frameCount];
nsecs_t presentedTimesNanoSrc[frameCount];
nsecs_t readyTimesNanoSrc[frameCount];
for (size_t i = 0; i < frameCount; i++) {
nsecs_t postedTimeNano = stats.desiredPresentTimesNano[i];
if (postedTimeNano == INT64_MAX) {
postedTimeNano = gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO;
}
postedTimesNanoSrc[i] = postedTimeNano;
nsecs_t presentedTimeNano = stats.actualPresentTimesNano[i];
if (presentedTimeNano == INT64_MAX) {
presentedTimeNano = gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO;
}
presentedTimesNanoSrc[i] = presentedTimeNano;
nsecs_t readyTimeNano = stats.frameReadyTimesNano[i];
if (readyTimeNano == INT64_MAX) {
readyTimeNano = gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO;
}
readyTimesNanoSrc[i] = readyTimeNano;
}
env->SetLongArrayRegion(postedTimesNanoDst, 0, frameCount, postedTimesNanoSrc);
env->SetLongArrayRegion(presentedTimesNanoDst, 0, frameCount, presentedTimesNanoSrc);
env->SetLongArrayRegion(readyTimesNanoDst, 0, frameCount, readyTimesNanoSrc);
env->CallVoidMethod(outStats, gWindowContentFrameStatsClassInfo.init, refreshPeriodNano,
postedTimesNanoDst, presentedTimesNanoDst, readyTimesNanoDst);
if (env->ExceptionCheck()) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static jboolean nativeClearAnimationFrameStats(JNIEnv* env, jclass clazz) {
status_t err = SurfaceComposerClient::clearAnimationFrameStats();
if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
// The other end is not ready, just report we failed.
if (err == NO_INIT) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static jboolean nativeGetAnimationFrameStats(JNIEnv* env, jclass clazz, jobject outStats) {
FrameStats stats;
status_t err = SurfaceComposerClient::getAnimationFrameStats(&stats);
if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
// The other end is not ready, fine just return empty stats.
if (err == NO_INIT) {
return JNI_FALSE;
}
jlong refreshPeriodNano = static_cast<jlong>(stats.refreshPeriodNano);
size_t frameCount = stats.desiredPresentTimesNano.size();
jlongArray presentedTimesNanoDst = env->NewLongArray(frameCount);
if (presentedTimesNanoDst == NULL) {
return JNI_FALSE;
}
nsecs_t presentedTimesNanoSrc[frameCount];
for (size_t i = 0; i < frameCount; i++) {
nsecs_t presentedTimeNano = stats.desiredPresentTimesNano[i];
if (presentedTimeNano == INT64_MAX) {
presentedTimeNano = gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO;
}
presentedTimesNanoSrc[i] = presentedTimeNano;
}
env->SetLongArrayRegion(presentedTimesNanoDst, 0, frameCount, presentedTimesNanoSrc);
env->CallVoidMethod(outStats, gWindowAnimationFrameStatsClassInfo.init, refreshPeriodNano,
presentedTimesNanoDst);
if (env->ExceptionCheck()) {
return JNI_FALSE;
}
return JNI_TRUE;
}
// ----------------------------------------------------------------------------
static JNINativeMethod sSurfaceControlMethods[] = {
@@ -426,6 +582,14 @@ static JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeBlankDisplay },
{"nativeUnblankDisplay", "(Landroid/os/IBinder;)V",
(void*)nativeUnblankDisplay },
{"nativeClearContentFrameStats", "(J)Z",
(void*)nativeClearContentFrameStats },
{"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z",
(void*)nativeGetContentFrameStats },
{"nativeClearAnimationFrameStats", "()Z",
(void*)nativeClearAnimationFrameStats },
{"nativeGetAnimationFrameStats", "(Landroid/view/WindowAnimationFrameStats;)Z",
(void*)nativeGetAnimationFrameStats },
};
int register_android_view_SurfaceControl(JNIEnv* env)
@@ -441,6 +605,19 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F");
gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
jclass frameStatsClazz = env->FindClass("android/view/FrameStats");
jfieldID undefined_time_nano_field = env->GetStaticFieldID(frameStatsClazz, "UNDEFINED_TIME_NANO", "J");
nsecs_t undefined_time_nano = env->GetStaticLongField(frameStatsClazz, undefined_time_nano_field);
jclass contFrameStatsClazz = env->FindClass("android/view/WindowContentFrameStats");
gWindowContentFrameStatsClassInfo.init = env->GetMethodID(contFrameStatsClazz, "init", "(J[J[J[J)V");
gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO = undefined_time_nano;
jclass animFrameStatsClazz = env->FindClass("android/view/WindowAnimationFrameStats");
gWindowAnimationFrameStatsClassInfo.init = env->GetMethodID(animFrameStatsClazz, "init", "(J[J)V");
gWindowAnimationFrameStatsClassInfo.UNDEFINED_TIME_NANO = undefined_time_nano;
return err;
}

View File

@@ -1921,6 +1921,18 @@
android:description="@string/permdesc_filter_events"
android:protectionLevel="signature" />
<!-- @hide Allows an application to retrieve the window token from the accessibility manager. -->
<permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN"
android:label="@string/permlab_retrieveWindowToken"
android:description="@string/permdesc_retrieveWindowToken"
android:protectionLevel="signature" />
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
android:label="@string/permlab_frameStats"
android:description="@string/permdesc_frameStats"
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"

View File

@@ -829,6 +829,20 @@
enable accessibility on the device. Malicious apps may enable accessibility without
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_retrieveWindowToken">retrieve window token</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_retrieveWindowToken">Allows an application to retrieve
the window token. Malicious apps may perfrom unauthorized interaction with
the application window impersonating the system.</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_frameStats">retrieve frame statistics</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_frameStats">Allows an application to collect
frame statistics. Malicious apps may observe the frame statistics
of windows from other apps.</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. -->

View File

@@ -87,6 +87,8 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
<uses-permission android:name="android.permission.RENDER_STATS" />
<application android:label="@string/app_label">
<provider

View File

@@ -132,6 +132,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private static final String TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED =
"temporaryEnableAccessibilityStateUntilKeyguardRemoved";
private static final String GET_WINDOW_TOKEN = "getWindowToken";
private static final ComponentName sFakeAccessibilityServiceComponentName =
new ComponentName("foo.bar", "FakeService");
@@ -360,6 +362,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}, UserHandle.ALL, intentFilter, null, null);
}
@Override
public int addClient(IAccessibilityManagerClient client, int userId) {
synchronized (mLock) {
final int resolvedUserId = mSecurityPolicy
@@ -388,6 +391,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
synchronized (mLock) {
final int resolvedUserId = mSecurityPolicy
@@ -412,6 +416,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return (OWN_PROCESS_ID != Binder.getCallingPid());
}
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock) {
final int resolvedUserId = mSecurityPolicy
@@ -430,6 +435,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
int userId) {
List<AccessibilityServiceInfo> result = null;
@@ -463,6 +469,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return result;
}
@Override
public void interrupt(int userId) {
CopyOnWriteArrayList<Service> services;
synchronized (mLock) {
@@ -485,6 +492,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection, int userId) throws RemoteException {
synchronized (mLock) {
@@ -521,6 +529,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
synchronized (mLock) {
mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
@@ -570,6 +579,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return -1;
}
@Override
public void registerUiTestAutomationService(IBinder owner,
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo) {
@@ -612,6 +622,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
@@ -630,6 +641,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
ComponentName service, boolean touchExplorationEnabled) {
mSecurityPolicy.enforceCallingPermission(
@@ -662,6 +674,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
@Override
public IBinder getWindowToken(int windowId) {
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.RETRIEVE_WINDOW_TOKEN,
GET_WINDOW_TOKEN);
synchronized (mLock) {
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return null;
}
if (mSecurityPolicy.findWindowById(windowId) == null) {
return null;
}
IBinder token = mGlobalWindowTokens.get(windowId);
if (token != null) {
return token;
}
return getCurrentUserStateLocked().mWindowTokens.get(windowId);
}
}
boolean onGesture(int gestureId) {
synchronized (mLock) {
boolean handled = notifyGestureLocked(gestureId, false);
@@ -689,7 +724,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param outBounds The output to which to write the focus bounds.
* @return Whether accessibility focus was found and the bounds are populated.
*/
// TODO: (multi-display) Make sure this works for multiple displays.
// TODO: (multi-display) Make sure this works for multiple displays.
boolean getAccessibilityFocusBoundsInActiveWindow(Rect outBounds) {
// Instead of keeping track of accessibility focus events per
// window to be able to find the focus in the active window,

View File

@@ -24,6 +24,7 @@ import android.util.ArraySet;
import android.util.TimeUtils;
import android.view.IWindowId;
import android.view.WindowContentFrameStats;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.PhoneWindowManager;
@@ -626,6 +627,8 @@ public class WindowManagerService extends IWindowManager.Stub
private final PointerEventDispatcher mPointerEventDispatcher;
private WindowContentFrameStats mTempWindowRenderStats;
final class DragInputEventReceiver extends InputEventReceiver {
public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
@@ -10264,6 +10267,51 @@ public class WindowManagerService extends IWindowManager.Stub
return mSafeMode;
}
@Override
public boolean clearWindowContentFrameStats(IBinder token) {
if (!checkCallingPermission(Manifest.permission.FRAME_STATS,
"clearWindowContentFrameStats()")) {
throw new SecurityException("Requires FRAME_STATS permission");
}
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(token);
if (windowState == null) {
return false;
}
SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
if (surfaceControl == null) {
return false;
}
return surfaceControl.clearContentFrameStats();
}
}
@Override
public WindowContentFrameStats getWindowContentFrameStats(IBinder token) {
if (!checkCallingPermission(Manifest.permission.FRAME_STATS,
"getWindowContentFrameStats()")) {
throw new SecurityException("Requires FRAME_STATS permission");
}
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(token);
if (windowState == null) {
return null;
}
SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
if (surfaceControl == null) {
return null;
}
if (mTempWindowRenderStats == null) {
mTempWindowRenderStats = new WindowContentFrameStats();
}
WindowContentFrameStats stats = mTempWindowRenderStats;
if (!surfaceControl.getContentFrameStats(stats)) {
return null;
}
return stats;
}
}
void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
mPolicy.dump(" ", pw, args);

View File

@@ -23,22 +23,11 @@ import com.android.internal.view.IInputMethodClient;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.IWindowSession;
import java.util.List;
/**
* Basic implementation of {@link IWindowManager} so that {@link Display} (and
@@ -462,4 +451,16 @@ public class IWindowManagerImpl implements IWindowManager {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean clearWindowContentRenderStats(IBinder token) {
// TODO Auto-generated method stub
return false;
}
@Override
public WindowContentFrameStats getWindowContentRenderStats(IBinder token) {
// TODO Auto-generated method stub
return null;
}
}