Merge "Tracking merge of dalvik-dev to master"
diff --git a/api/current.xml b/api/current.xml
index 740c697..0a57d3e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -173149,7 +173149,7 @@
static="false"
final="false"
deprecated="not deprecated"
- visibility=""
+ visibility="public"
>
<method name="destroy"
return="void"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cb07135..751726a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -394,6 +394,8 @@
ParcelFileDescriptor fd;
}
+ native private void dumpGraphicsInfo(FileDescriptor fd);
+
private final class ApplicationThread extends ApplicationThreadNative {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
@@ -711,9 +713,14 @@
}
}
}
-
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (args != null && args.length == 1 && args[0].equals("graphics")) {
+ pw.flush();
+ dumpGraphicsInfo(fd);
+ return;
+ }
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index 5e9ead0..c8ea825 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -22,7 +22,21 @@
import android.util.Log;
/**
- * A class representing a USB accessory.
+ * A class representing a USB accessory, which is an external hardware component
+ * that communicates with an android application over USB.
+ * The accessory is the USB host and android the device side of the USB connection.
+ *
+ * <p>When the accessory connects, it reports its manufacturer and model names,
+ * the version of the accessory, and a user visible description of the accessory to the device.
+ * The manufacturer, model and version strings are used by the USB Manager to choose
+ * an appropriate application for the accessory.
+ * The accessory may optionally provide a unique serial number
+ * and a URL to for the accessory's website to the device as well.
+ *
+ * <p>An instance of this class is sent to the application via the
+ * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} Intent.
+ * The application can then call {@link UsbManager#openAccessory} to open a file descriptor
+ * for reading and writing data to and from the accessory.
*/
public class UsbAccessory implements Parcelable {
@@ -63,7 +77,7 @@
}
/**
- * Returns the manufacturer of the accessory.
+ * Returns the manufacturer name of the accessory.
*
* @return the accessory manufacturer
*/
diff --git a/core/java/android/hardware/usb/UsbConstants.java b/core/java/android/hardware/usb/UsbConstants.java
index 6626c9f..0e8d47c 100644
--- a/core/java/android/hardware/usb/UsbConstants.java
+++ b/core/java/android/hardware/usb/UsbConstants.java
@@ -22,45 +22,162 @@
*/
public final class UsbConstants {
+ /**
+ * Bitmask used for extracting the {@link UsbEndpoint} direction from its address field.
+ * @see UsbEndpoint#getAddress
+ * @see UsbEndpoint#getDirection
+ * @see #USB_DIR_OUT
+ * @see #USB_DIR_IN
+ *
+ */
public static final int USB_ENDPOINT_DIR_MASK = 0x80;
+ /**
+ * Used to signify direction of data for a {@link UsbEndpoint} is OUT (host to device)
+ * @see UsbEndpoint#getDirection
+ */
public static final int USB_DIR_OUT = 0;
+ /**
+ * Used to signify direction of data for a {@link UsbEndpoint} is IN (device to host)
+ * @see UsbEndpoint#getDirection
+ */
public static final int USB_DIR_IN = 0x80;
- public static final int USB_TYPE_MASK = (0x03 << 5);
- public static final int USB_TYPE_STANDARD = (0x00 << 5);
- public static final int USB_TYPE_CLASS = (0x01 << 5);
- public static final int USB_TYPE_VENDOR = (0x02 << 5);
- public static final int USB_TYPE_RESERVED = (0x03 << 5);
-
+ /**
+ * Bitmask used for extracting the {@link UsbEndpoint} number its address field.
+ * @see UsbEndpoint#getAddress
+ * @see UsbEndpoint#getEndpointNumber
+ */
public static final int USB_ENDPOINT_NUMBER_MASK = 0x0f;
- // flags for endpoint attributes
+ /**
+ * Bitmask used for extracting the {@link UsbEndpoint} type from its address field.
+ * @see UsbEndpoint#getAddress
+ * @see UsbEndpoint#getType
+ * @see #USB_ENDPOINT_XFER_CONTROL
+ * @see #USB_ENDPOINT_XFER_ISOC
+ * @see #USB_ENDPOINT_XFER_BULK
+ * @see #USB_ENDPOINT_XFER_INT
+ */
public static final int USB_ENDPOINT_XFERTYPE_MASK = 0x03;
+ /**
+ * Control endpoint type (endpoint zero)
+ * @see UsbEndpoint#getType
+ */
public static final int USB_ENDPOINT_XFER_CONTROL = 0;
+ /**
+ * Isochronous endpoint type (currently not supported)
+ * @see UsbEndpoint#getType
+ */
public static final int USB_ENDPOINT_XFER_ISOC = 1;
+ /**
+ * Bulk endpoint type
+ * @see UsbEndpoint#getType
+ */
public static final int USB_ENDPOINT_XFER_BULK = 2;
+ /**
+ * Interrupt endpoint type
+ * @see UsbEndpoint#getType
+ */
public static final int USB_ENDPOINT_XFER_INT = 3;
- // USB classes
+
+ /**
+ * Bitmask used for encoding the request type for a control request on endpoint zero.
+ */
+ public static final int USB_TYPE_MASK = (0x03 << 5);
+ /**
+ * Used to specify that an endpoint zero control request is a standard request.
+ */
+ public static final int USB_TYPE_STANDARD = (0x00 << 5);
+ /**
+ * Used to specify that an endpoint zero control request is a class specific request.
+ */
+ public static final int USB_TYPE_CLASS = (0x01 << 5);
+ /**
+ * Used to specify that an endpoint zero control request is a vendor specific request.
+ */
+ public static final int USB_TYPE_VENDOR = (0x02 << 5);
+ /**
+ * Reserved endpoint zero control request type (currently unused).
+ */
+ public static final int USB_TYPE_RESERVED = (0x03 << 5);
+
+
+ /**
+ * USB class indicating that the class is determined on a per-interface basis.
+ */
public static final int USB_CLASS_PER_INTERFACE = 0;
+ /**
+ * USB class for audio devices.
+ */
public static final int USB_CLASS_AUDIO = 1;
+ /**
+ * USB class for communication devices.
+ */
public static final int USB_CLASS_COMM = 2;
+ /**
+ * USB class for human interface devices (for example, mice and keyboards).
+ */
public static final int USB_CLASS_HID = 3;
+ /**
+ * USB class for physical devices.
+ */
public static final int USB_CLASS_PHYSICA = 5;
+ /**
+ * USB class for still image devices (digital cameras).
+ */
public static final int USB_CLASS_STILL_IMAGE = 6;
+ /**
+ * USB class for printers.
+ */
public static final int USB_CLASS_PRINTER = 7;
+ /**
+ * USB class for mass storage devices.
+ */
public static final int USB_CLASS_MASS_STORAGE = 8;
+ /**
+ * USB class for USB hubs.
+ */
public static final int USB_CLASS_HUB = 9;
+ /**
+ * USB class for CDC devices (communications device class).
+ */
public static final int USB_CLASS_CDC_DATA = 0x0a;
+ /**
+ * USB class for content smart card devices.
+ */
public static final int USB_CLASS_CSCID = 0x0b;
+ /**
+ * USB class for content security devices.
+ */
public static final int USB_CLASS_CONTENT_SEC = 0x0d;
+ /**
+ * USB class for video devices.
+ */
public static final int USB_CLASS_VIDEO = 0x0e;
+ /**
+ * USB class for wireless controller devices.
+ */
public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0;
+ /**
+ * USB class for wireless miscellaneous devices.
+ */
public static final int USB_CLASS_MISC = 0xef;
+ /**
+ * Application specific USB class.
+ */
public static final int USB_CLASS_APP_SPEC = 0xfe;
+ /**
+ * Vendor specific USB class.
+ */
public static final int USB_CLASS_VENDOR_SPEC = 0xff;
- // USB subclasses
- public static final int USB_INTERFACE_SUBCLASS_BOOT = 1; // for HID class
+ /**
+ * Boot subclass for HID devices.
+ */
+ public static final int USB_INTERFACE_SUBCLASS_BOOT = 1;
+ /**
+ * Vendor specific USB subclass.
+ */
public static final int USB_SUBCLASS_VENDOR_SPEC = 0xff;
-}
\ No newline at end of file
+}
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 9e536a7..af3f7f0 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -24,7 +24,16 @@
import java.io.FileDescriptor;
/**
- * A class representing a USB device.
+ * This class represents a USB device attached to the android device with the android device
+ * acting as the USB host.
+ * Each device contains one or more {@link UsbInterface}s, each of which contains a number of
+ * {@link UsbEndpoint}s (the channels via which data is transmitted over USB).
+ *
+ * <p> This class contains information (along with {@link UsbInterface} and {@link UsbEndpoint})
+ * that describes the capabilities of the USB device.
+ * To communicate with the device, you open a {@link UsbDeviceConnection} for the device
+ * and use {@link UsbRequest} to send and receive data on an endpoint.
+ * {@link UsbDeviceConnection#controlTransfer} is used for control requests on endpoint zero.
*/
public class UsbDevice implements Parcelable {
@@ -96,8 +105,7 @@
/**
* Returns the devices's class field.
- * Some useful constants for USB device classes can be found in
- * {@link android.hardware.usb.UsbConstants}
+ * Some useful constants for USB device classes can be found in {@link UsbConstants}.
*
* @return the devices's class
*/
@@ -115,7 +123,7 @@
}
/**
- * Returns the device's subclass field.
+ * Returns the device's protocol field.
*
* @return the device's protocol
*/
@@ -124,7 +132,7 @@
}
/**
- * Returns the number of {@link android.hardware.usb.UsbInterface}s this device contains.
+ * Returns the number of {@link UsbInterface}s this device contains.
*
* @return the number of interfaces
*/
@@ -133,7 +141,7 @@
}
/**
- * Returns the {@link android.hardware.usb.UsbInterface} at the given index.
+ * Returns the {@link UsbInterface} at the given index.
*
* @return the interface
*/
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 876287c..a153c0b 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -23,7 +23,8 @@
/**
- * A class representing a USB device.
+ * This class is used for sending and receiving data and control messages to a USB device.
+ * Instances of this class are created by {@link UsbManager#openDevice}.
*/
public class UsbDeviceConnection {
@@ -48,15 +49,20 @@
/**
* Releases all system resources related to the device.
+ * Once the object is closed it cannot be used again.
+ * The client must call {@link UsbManager#openDevice} again
+ * to retrieve a new instance to reestablish communication with the device.
*/
public void close() {
native_close();
}
/**
- * Returns an integer file descriptor for the device, or
+ * Returns the native file descriptor for the device, or
* -1 if the device is not opened.
- * This is intended for passing to native code to access the device
+ * This is intended for passing to native code to access the device.
+ *
+ * @return the native file descriptor
*/
public int getFileDescriptor() {
return native_get_fd();
@@ -65,7 +71,8 @@
/**
* Claims exclusive access to a {@link android.hardware.usb.UsbInterface}.
* This must be done before sending or receiving data on any
- * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface
+ * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface.
+ *
* @param intf the interface to claim
* @param force true to disconnect kernel driver if necessary
* @return true if the interface was successfully claimed
diff --git a/core/java/android/hardware/usb/UsbEndpoint.java b/core/java/android/hardware/usb/UsbEndpoint.java
index bc2c2c1..753a447 100644
--- a/core/java/android/hardware/usb/UsbEndpoint.java
+++ b/core/java/android/hardware/usb/UsbEndpoint.java
@@ -21,7 +21,14 @@
import android.os.Parcelable;
/**
- * A class representing an endpoint on a {@link android.hardware.usb.UsbInterface}.
+ * A class representing an endpoint on a {@link UsbInterface}.
+ * Endpoints are the channels for sending and receiving data over USB.
+ * Typically bulk endpoints are used for sending non-trivial amounts of data.
+ * Interrupt endpoints are used for sending small amounts of data, typically events,
+ * separately from the main data streams.
+ * The endpoint zero is a special endpoint for control messages sent from the host
+ * to device.
+ * Isochronous endpoints are currently unsupported.
*/
public class UsbEndpoint implements Parcelable {
@@ -43,6 +50,10 @@
/**
* Returns the endpoint's address field.
+ * The address is a bitfield containing both the endpoint number
+ * as well as the data direction of the endpoint.
+ * the endpoint number and direction can also be accessed via
+ * {@link #getEndpointNumber} and {@link #getDirection}.
*
* @return the endpoint's address
*/
@@ -61,10 +72,12 @@
/**
* Returns the endpoint's direction.
- * Returns {@link android.hardware.usb.UsbConstants#USB_DIR_OUT}
+ * Returns {@link UsbConstants#USB_DIR_OUT}
* if the direction is host to device, and
- * {@link android.hardware.usb.UsbConstants#USB_DIR_IN} if the
+ * {@link UsbConstants#USB_DIR_IN} if the
* direction is device to host.
+ * @see {@link UsbConstants#USB_DIR_IN}
+ * @see {@link UsbConstants#USB_DIR_OUT}
*
* @return the endpoint's direction
*/
@@ -85,10 +98,10 @@
* Returns the endpoint's type.
* Possible results are:
* <ul>
- * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero)
- * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint)
- * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint)
- * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint)
+ * <li>{@link UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero)
+ * <li>{@link UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint)
+ * <li>{@link UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint)
+ * <li>{@link UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint)
* </ul>
*
* @return the endpoint's type
diff --git a/core/java/android/hardware/usb/UsbInterface.java b/core/java/android/hardware/usb/UsbInterface.java
index 2b4c7c0..3b51063 100644
--- a/core/java/android/hardware/usb/UsbInterface.java
+++ b/core/java/android/hardware/usb/UsbInterface.java
@@ -21,7 +21,11 @@
import android.os.Parcelable;
/**
- * A class representing an interface on a {@link android.hardware.usb.UsbDevice}.
+ * A class representing an interface on a {@link UsbDevice}.
+ * USB devices can have one or more interfaces, each one providing a different
+ * piece of functionality, separate from the other interfaces.
+ * An interface will have one or more {@link UsbEndpoint}s, which are the
+ * channels by which the host transfers data with the device.
*/
public class UsbInterface implements Parcelable {
@@ -46,6 +50,7 @@
/**
* Returns the interface's ID field.
+ * This is an integer that uniquely identifies the interface on the device.
*
* @return the interface's ID
*/
@@ -55,8 +60,7 @@
/**
* Returns the interface's class field.
- * Some useful constants for USB classes can be found in
- * {@link android.hardware.usb.UsbConstants}
+ * Some useful constants for USB classes can be found in {@link UsbConstants}
*
* @return the interface's class
*/
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 7bf278a..60b37a1 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -31,7 +31,8 @@
import java.util.HashMap;
/**
- * This class allows you to access the state of USB, both in host and device mode.
+ * This class allows you to access the state of USB and communicate with USB devices.
+ * Currently only host mode is supported in the public API.
*
* <p>You can obtain an instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 5fe6c8c..2252248 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -24,8 +24,13 @@
* A class representing USB request packet.
* This can be used for both reading and writing data to or from a
* {@link android.hardware.usb.UsbDeviceConnection}.
- * UsbRequests are sent asynchronously via {@link #queue} and the results
- * are read by {@link android.hardware.usb.UsbDeviceConnection#requestWait}.
+ * UsbRequests can be used to transfer data on bulk and interrupt endpoints.
+ * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer}
+ * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}.
+ * Requests on interrupt endpoints are only send and received asynchronously.
+ *
+ * <p>Requests on endpoint zero are not supported by this class;
+ * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead.
*/
public class UsbRequest {
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index c1e1049..f284f51 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -245,6 +245,13 @@
*/
private VelocityTracker mVelocityTracker;
+ /**
+ * Consistency verifier for debugging purposes.
+ */
+ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
private class GestureHandler extends Handler {
GestureHandler() {
super();
@@ -443,6 +450,10 @@
* else false.
*/
public boolean onTouchEvent(MotionEvent ev) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
+ }
+
final int action = ev.getAction();
final float y = ev.getY();
final float x = ev.getX();
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 03189ca..87e7ea7 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -68,6 +68,14 @@
public abstract void setSource(int source);
/**
+ * Copies the event.
+ *
+ * @return A deep copy of the event.
+ * @hide
+ */
+ public abstract InputEvent copy();
+
+ /**
* Recycles the event.
* This method should only be used by the system since applications do not
* expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
@@ -76,6 +84,28 @@
*/
public abstract void recycle();
+ /**
+ * Gets a private flag that indicates when the system has detected that this input event
+ * may be inconsistent with respect to the sequence of previously delivered input events,
+ * such as when a key up event is sent but the key was not down or when a pointer
+ * move event is sent but the pointer is not down.
+ *
+ * @return True if this event is tainted.
+ * @hide
+ */
+ public abstract boolean isTainted();
+
+ /**
+ * Sets a private flag that indicates when the system has detected that this input event
+ * may be inconsistent with respect to the sequence of previously delivered input events,
+ * such as when a key up event is sent but the key was not down or when a pointer
+ * move event is sent but the pointer is not down.
+ *
+ * @param tainted True if this event is tainted.
+ * @hide
+ */
+ public abstract void setTainted(boolean tainted);
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
new file mode 100644
index 0000000..6618f07
--- /dev/null
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2010 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.Build;
+import android.util.Log;
+
+/**
+ * Checks whether a sequence of input events is self-consistent.
+ * Logs a description of each problem detected.
+ * <p>
+ * When a problem is detected, the event is tainted. This mechanism prevents the same
+ * error from being reported multiple times.
+ * </p>
+ *
+ * @hide
+ */
+public final class InputEventConsistencyVerifier {
+ private static final String TAG = "InputEventConsistencyVerifier";
+ private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
+
+ // The number of recent events to log when a problem is detected.
+ // Can be set to 0 to disable logging recent events but the runtime overhead of
+ // this feature is negligible on current hardware.
+ private static final int RECENT_EVENTS_TO_LOG = 5;
+
+ // The object to which the verifier is attached.
+ private final Object mCaller;
+
+ // Consistency verifier flags.
+ private final int mFlags;
+
+ // The most recently checked event and the nesting level at which it was checked.
+ // This is only set when the verifier is called from a nesting level greater than 0
+ // so that the verifier can detect when it has been asked to verify the same event twice.
+ // It does not make sense to examine the contents of the last event since it may have
+ // been recycled.
+ private InputEvent mLastEvent;
+ private int mLastNestingLevel;
+
+ // Copy of the most recent events.
+ private InputEvent[] mRecentEvents;
+ private int mMostRecentEventIndex;
+
+ // Current event and its type.
+ private InputEvent mCurrentEvent;
+ private String mCurrentEventType;
+
+ // Linked list of key state objects.
+ private KeyState mKeyStateList;
+
+ // Current state of the trackball.
+ private boolean mTrackballDown;
+
+ // Bitfield of pointer ids that are currently down.
+ // Assumes that the largest possible pointer id is 31, which is potentially subject to change.
+ // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ private int mTouchEventStreamPointers;
+
+ // The device id and source of the current stream of touch events.
+ private int mTouchEventStreamDeviceId = -1;
+ private int mTouchEventStreamSource;
+
+ // Set to true when we discover that the touch event stream is inconsistent.
+ // Reset on down or cancel.
+ private boolean mTouchEventStreamIsTainted;
+
+ // Set to true if we received hover enter.
+ private boolean mHoverEntered;
+
+ // The current violation message.
+ private StringBuilder mViolationMessage;
+
+ /**
+ * Indicates that the verifier is intended to act on raw device input event streams.
+ * Disables certain checks for invariants that are established by the input dispatcher
+ * itself as it delivers input events, such as key repeating behavior.
+ */
+ public static final int FLAG_RAW_DEVICE_INPUT = 1 << 0;
+
+ /**
+ * Creates an input consistency verifier.
+ * @param caller The object to which the verifier is attached.
+ * @param flags Flags to the verifier, or 0 if none.
+ */
+ public InputEventConsistencyVerifier(Object caller, int flags) {
+ this.mCaller = caller;
+ this.mFlags = flags;
+ }
+
+ /**
+ * Determines whether the instrumentation should be enabled.
+ * @return True if it should be enabled.
+ */
+ public static boolean isInstrumentationEnabled() {
+ return IS_ENG_BUILD;
+ }
+
+ /**
+ * Resets the state of the input event consistency verifier.
+ */
+ public void reset() {
+ mLastEvent = null;
+ mLastNestingLevel = 0;
+ mTrackballDown = false;
+ mTouchEventStreamPointers = 0;
+ mTouchEventStreamIsTainted = false;
+ mHoverEntered = false;
+ }
+
+ /**
+ * Checks an arbitrary input event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onInputEvent(InputEvent event, int nestingLevel) {
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)event;
+ onKeyEvent(keyEvent, nestingLevel);
+ } else {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ if (motionEvent.isTouchEvent()) {
+ onTouchEvent(motionEvent, nestingLevel);
+ } else if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ onTrackballEvent(motionEvent, nestingLevel);
+ } else {
+ onGenericMotionEvent(motionEvent, nestingLevel);
+ }
+ }
+ }
+
+ /**
+ * Checks a key event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onKeyEvent(KeyEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "KeyEvent")) {
+ return;
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int action = event.getAction();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+ final int keyCode = event.getKeyCode();
+ switch (action) {
+ case KeyEvent.ACTION_DOWN: {
+ KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
+ if (state != null) {
+ // If the key is already down, ensure it is a repeat.
+ // We don't perform this check when processing raw device input
+ // because the input dispatcher itself is responsible for setting
+ // the key repeat count before it delivers input events.
+ if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0
+ && event.getRepeatCount() == 0) {
+ problem("ACTION_DOWN but key is already down and this event "
+ + "is not a key repeat.");
+ }
+ } else {
+ addKeyState(deviceId, source, keyCode);
+ }
+ break;
+ }
+ case KeyEvent.ACTION_UP: {
+ KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ true);
+ if (state == null) {
+ problem("ACTION_UP but key was not down.");
+ } else {
+ state.recycle();
+ }
+ break;
+ }
+ case KeyEvent.ACTION_MULTIPLE:
+ break;
+ default:
+ problem("Invalid action " + KeyEvent.actionToString(action)
+ + " for key event.");
+ break;
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Checks a trackball event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onTrackballEvent(MotionEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "TrackballEvent")) {
+ return;
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int action = event.getAction();
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (mTrackballDown) {
+ problem("ACTION_DOWN but trackball is already down.");
+ } else {
+ mTrackballDown = true;
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ if (!mTrackballDown) {
+ problem("ACTION_UP but trackball is not down.");
+ } else {
+ mTrackballDown = false;
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ default:
+ problem("Invalid action " + MotionEvent.actionToString(action)
+ + " for trackball event.");
+ break;
+ }
+
+ if (mTrackballDown && event.getPressure() <= 0) {
+ problem("Trackball is down but pressure is not greater than 0.");
+ } else if (!mTrackballDown && event.getPressure() != 0) {
+ problem("Trackball is up but pressure is not equal to 0.");
+ }
+ } else {
+ problem("Source was not SOURCE_CLASS_TRACKBALL.");
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Checks a touch event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onTouchEvent(MotionEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "TouchEvent")) {
+ return;
+ }
+
+ final int action = event.getAction();
+ final boolean newStream = action == MotionEvent.ACTION_DOWN
+ || action == MotionEvent.ACTION_CANCEL;
+ if (mTouchEventStreamIsTainted) {
+ if (newStream) {
+ mTouchEventStreamIsTainted = false;
+ } else {
+ finishEvent(true);
+ return;
+ }
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+
+ if (!newStream && mTouchEventStreamDeviceId != -1
+ && (mTouchEventStreamDeviceId != deviceId
+ || mTouchEventStreamSource != source)) {
+ problem("Touch event stream contains events from multiple sources: "
+ + "previous device id " + mTouchEventStreamDeviceId
+ + ", previous source " + Integer.toHexString(mTouchEventStreamSource)
+ + ", new device id " + deviceId
+ + ", new source " + Integer.toHexString(source));
+ }
+ mTouchEventStreamDeviceId = deviceId;
+ mTouchEventStreamSource = source;
+
+ final int pointerCount = event.getPointerCount();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (mTouchEventStreamPointers != 0) {
+ problem("ACTION_DOWN but pointers are already down. "
+ + "Probably missing ACTION_UP from previous gesture.");
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ mTouchEventStreamPointers = 1 << event.getPointerId(0);
+ break;
+ case MotionEvent.ACTION_UP:
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ mTouchEventStreamPointers = 0;
+ mTouchEventStreamIsTainted = false;
+ break;
+ case MotionEvent.ACTION_MOVE: {
+ final int expectedPointerCount =
+ Integer.bitCount(mTouchEventStreamPointers);
+ if (pointerCount != expectedPointerCount) {
+ problem("ACTION_MOVE contained " + pointerCount
+ + " pointers but there are currently "
+ + expectedPointerCount + " pointers down.");
+ mTouchEventStreamIsTainted = true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ mTouchEventStreamPointers = 0;
+ mTouchEventStreamIsTainted = false;
+ break;
+ case MotionEvent.ACTION_OUTSIDE:
+ if (mTouchEventStreamPointers != 0) {
+ problem("ACTION_OUTSIDE but pointers are still down.");
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ mTouchEventStreamIsTainted = false;
+ break;
+ default: {
+ final int actionMasked = event.getActionMasked();
+ final int actionIndex = event.getActionIndex();
+ if (actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
+ if (mTouchEventStreamPointers == 0) {
+ problem("ACTION_POINTER_DOWN but no other pointers were down.");
+ mTouchEventStreamIsTainted = true;
+ }
+ if (actionIndex < 0 || actionIndex >= pointerCount) {
+ problem("ACTION_POINTER_DOWN index is " + actionIndex
+ + " but the pointer count is " + pointerCount + ".");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ final int id = event.getPointerId(actionIndex);
+ final int idBit = 1 << id;
+ if ((mTouchEventStreamPointers & idBit) != 0) {
+ problem("ACTION_POINTER_DOWN specified pointer id " + id
+ + " which is already down.");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ mTouchEventStreamPointers |= idBit;
+ }
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ } else if (actionMasked == MotionEvent.ACTION_POINTER_UP) {
+ if (actionIndex < 0 || actionIndex >= pointerCount) {
+ problem("ACTION_POINTER_UP index is " + actionIndex
+ + " but the pointer count is " + pointerCount + ".");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ final int id = event.getPointerId(actionIndex);
+ final int idBit = 1 << id;
+ if ((mTouchEventStreamPointers & idBit) == 0) {
+ problem("ACTION_POINTER_UP specified pointer id " + id
+ + " which is not currently down.");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ mTouchEventStreamPointers &= ~idBit;
+ }
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ } else {
+ problem("Invalid action " + MotionEvent.actionToString(action)
+ + " for touch event.");
+ }
+ break;
+ }
+ }
+ } else {
+ problem("Source was not SOURCE_CLASS_POINTER.");
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Checks a generic motion event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onGenericMotionEvent(MotionEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "GenericMotionEvent")) {
+ return;
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int action = event.getAction();
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ ensurePointerCountIsOneForThisAction(event);
+ mHoverEntered = true;
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ ensurePointerCountIsOneForThisAction(event);
+ if (!mHoverEntered) {
+ problem("ACTION_HOVER_EXIT without prior ACTION_HOVER_ENTER");
+ }
+ mHoverEntered = false;
+ break;
+ case MotionEvent.ACTION_SCROLL:
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ default:
+ problem("Invalid action for generic pointer event.");
+ break;
+ }
+ } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ default:
+ problem("Invalid action for generic joystick event.");
+ break;
+ }
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ private void ensureMetaStateIsNormalized(int metaState) {
+ final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState);
+ if (normalizedMetaState != metaState) {
+ problem(String.format("Metastate not normalized. Was 0x%08x but expected 0x%08x.",
+ metaState, normalizedMetaState));
+ }
+ }
+
+ private void ensurePointerCountIsOneForThisAction(MotionEvent event) {
+ final int pointerCount = event.getPointerCount();
+ if (pointerCount != 1) {
+ problem("Pointer count is " + pointerCount + " but it should always be 1 for "
+ + MotionEvent.actionToString(event.getAction()));
+ }
+ }
+
+ private void ensureHistorySizeIsZeroForThisAction(MotionEvent event) {
+ final int historySize = event.getHistorySize();
+ if (historySize != 0) {
+ problem("History size is " + historySize + " but it should always be 0 for "
+ + MotionEvent.actionToString(event.getAction()));
+ }
+ }
+
+ private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
+ // Ignore the event if it is already tainted.
+ if (event.isTainted()) {
+ return false;
+ }
+
+ // Ignore the event if we already checked it at a higher nesting level.
+ if (event == mLastEvent && nestingLevel < mLastNestingLevel) {
+ return false;
+ }
+
+ if (nestingLevel > 0) {
+ mLastEvent = event;
+ mLastNestingLevel = nestingLevel;
+ } else {
+ mLastEvent = null;
+ mLastNestingLevel = 0;
+ }
+
+ mCurrentEvent = event;
+ mCurrentEventType = eventType;
+ return true;
+ }
+
+ private void finishEvent(boolean tainted) {
+ if (mViolationMessage != null && mViolationMessage.length() != 0) {
+ mViolationMessage.append("\n in ").append(mCaller);
+ mViolationMessage.append("\n ").append(mCurrentEvent);
+
+ if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
+ mViolationMessage.append("\n -- recent events --");
+ for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
+ final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
+ % RECENT_EVENTS_TO_LOG;
+ final InputEvent event = mRecentEvents[index];
+ if (event == null) {
+ break;
+ }
+ mViolationMessage.append("\n ").append(i + 1).append(": ").append(event);
+ }
+ }
+
+ Log.d(TAG, mViolationMessage.toString());
+ mViolationMessage.setLength(0);
+ tainted = true;
+ }
+
+ if (tainted) {
+ // Taint the event so that we do not generate additional violations from it
+ // further downstream.
+ mCurrentEvent.setTainted(true);
+ }
+
+ if (RECENT_EVENTS_TO_LOG != 0) {
+ if (mRecentEvents == null) {
+ mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG];
+ }
+ final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG;
+ mMostRecentEventIndex = index;
+ if (mRecentEvents[index] != null) {
+ mRecentEvents[index].recycle();
+ }
+ mRecentEvents[index] = mCurrentEvent.copy();
+ }
+
+ mCurrentEvent = null;
+ mCurrentEventType = null;
+ }
+
+ private void problem(String message) {
+ if (mViolationMessage == null) {
+ mViolationMessage = new StringBuilder();
+ }
+ if (mViolationMessage.length() == 0) {
+ mViolationMessage.append(mCurrentEventType).append(": ");
+ } else {
+ mViolationMessage.append("\n ");
+ }
+ mViolationMessage.append(message);
+ }
+
+ private KeyState findKeyState(int deviceId, int source, int keyCode, boolean remove) {
+ KeyState last = null;
+ KeyState state = mKeyStateList;
+ while (state != null) {
+ if (state.deviceId == deviceId && state.source == source
+ && state.keyCode == keyCode) {
+ if (remove) {
+ if (last != null) {
+ last.next = state.next;
+ } else {
+ mKeyStateList = state.next;
+ }
+ state.next = null;
+ }
+ return state;
+ }
+ last = state;
+ state = state.next;
+ }
+ return null;
+ }
+
+ private void addKeyState(int deviceId, int source, int keyCode) {
+ KeyState state = KeyState.obtain(deviceId, source, keyCode);
+ state.next = mKeyStateList;
+ mKeyStateList = state;
+ }
+
+ private static final class KeyState {
+ private static Object mRecycledListLock = new Object();
+ private static KeyState mRecycledList;
+
+ public KeyState next;
+ public int deviceId;
+ public int source;
+ public int keyCode;
+
+ private KeyState() {
+ }
+
+ public static KeyState obtain(int deviceId, int source, int keyCode) {
+ KeyState state;
+ synchronized (mRecycledListLock) {
+ state = mRecycledList;
+ if (state != null) {
+ mRecycledList = state.next;
+ } else {
+ state = new KeyState();
+ }
+ }
+ state.deviceId = deviceId;
+ state.source = source;
+ state.keyCode = keyCode;
+ return state;
+ }
+
+ public void recycle() {
+ synchronized (mRecycledListLock) {
+ next = mRecycledList;
+ mRecycledList = next;
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8070c6a..4320160 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1170,7 +1170,18 @@
* @hide
*/
public static final int FLAG_START_TRACKING = 0x40000000;
-
+
+ /**
+ * Private flag that indicates when the system has detected that this key event
+ * may be inconsistent with respect to the sequence of previously delivered key events,
+ * such as when a key up event is sent but the key was not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ public static final int FLAG_TAINTED = 0x80000000;
+
/**
* Returns the maximum keycode.
*/
@@ -1535,6 +1546,33 @@
}
/**
+ * Obtains a (potentially recycled) copy of another key event.
+ *
+ * @hide
+ */
+ public static KeyEvent obtain(KeyEvent other) {
+ KeyEvent ev = obtain();
+ ev.mDownTime = other.mDownTime;
+ ev.mEventTime = other.mEventTime;
+ ev.mAction = other.mAction;
+ ev.mKeyCode = other.mKeyCode;
+ ev.mRepeatCount = other.mRepeatCount;
+ ev.mMetaState = other.mMetaState;
+ ev.mDeviceId = other.mDeviceId;
+ ev.mScanCode = other.mScanCode;
+ ev.mFlags = other.mFlags;
+ ev.mSource = other.mSource;
+ ev.mCharacters = other.mCharacters;
+ return ev;
+ }
+
+ /** @hide */
+ @Override
+ public KeyEvent copy() {
+ return obtain(this);
+ }
+
+ /**
* Recycles a key event.
* Key events should only be recycled if they are owned by the system since user
* code expects them to be essentially immutable, "tracking" notwithstanding.
@@ -1635,7 +1673,19 @@
event.mFlags = flags;
return event;
}
-
+
+ /** @hide */
+ @Override
+ public final boolean isTainted() {
+ return (mFlags & FLAG_TAINTED) != 0;
+ }
+
+ /** @hide */
+ @Override
+ public final void setTainted(boolean tainted) {
+ mFlags = tainted ? mFlags | FLAG_TAINTED : mFlags & ~FLAG_TAINTED;
+ }
+
/**
* Don't use in new code, instead explicitly check
* {@link #getAction()}.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 3c34479..7611b08 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -308,6 +308,17 @@
public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
/**
+ * Private flag that indicates when the system has detected that this motion event
+ * may be inconsistent with respect to the sequence of previously delivered motion events,
+ * such as when a pointer move event is sent but the pointer is not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ public static final int FLAG_TAINTED = 0x80000000;
+
+ /**
* Flag indicating the motion event intersected the top edge of the screen.
*/
public static final int EDGE_TOP = 0x00000001;
@@ -1054,6 +1065,7 @@
private static native void nativeSetAction(int nativePtr, int action);
private static native boolean nativeIsTouchEvent(int nativePtr);
private static native int nativeGetFlags(int nativePtr);
+ private static native void nativeSetFlags(int nativePtr, int flags);
private static native int nativeGetEdgeFlags(int nativePtr);
private static native void nativeSetEdgeFlags(int nativePtr, int action);
private static native int nativeGetMetaState(int nativePtr);
@@ -1290,6 +1302,12 @@
return ev;
}
+ /** @hide */
+ @Override
+ public MotionEvent copy() {
+ return obtain(this);
+ }
+
/**
* Recycle the MotionEvent, to be re-used by a later caller. After calling
* this function you must not ever touch the event again.
@@ -1403,6 +1421,20 @@
return nativeGetFlags(mNativePtr);
}
+ /** @hide */
+ @Override
+ public final boolean isTainted() {
+ final int flags = getFlags();
+ return (flags & FLAG_TAINTED) != 0;
+ }
+
+ /** @hide */
+ @Override
+ public final void setTainted(boolean tainted) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED);
+ }
+
/**
* Returns the time (in ms) when the user originally pressed down to start
* a stream of position events.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index d638e70..456857a 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -163,6 +163,13 @@
private int mActiveId1;
private boolean mActive0MostRecent;
+ /**
+ * Consistency verifier for debugging purposes.
+ */
+ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
ViewConfiguration config = ViewConfiguration.get(context);
mContext = context;
@@ -171,6 +178,10 @@
}
public boolean onTouchEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+ }
+
final int action = event.getActionMasked();
boolean handled = true;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0ef56cc..e329e97d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2384,6 +2384,14 @@
Rect mLocalDirtyRect;
/**
+ * Consistency verifier for debugging purposes.
+ * @hide
+ */
+ protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -4590,13 +4598,16 @@
* @return True if the event was handled, false otherwise.
*/
public boolean dispatchKeyEvent(KeyEvent event) {
- // If any attached key listener a first crack at the event.
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onKeyEvent(event, 0);
+ }
//noinspection SimplifiableIfStatement,deprecation
if (android.util.Config.LOGV) {
captureViewInfo("captureViewKeyEvent", this);
}
+ // Give any attached key listener a first crack at the event.
//noinspection SimplifiableIfStatement
if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
@@ -4625,6 +4636,10 @@
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+ }
+
if (!onFilterTouchEventForSecurity(event)) {
return false;
}
@@ -4662,6 +4677,10 @@
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTrackballEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+ }
+
//Log.i("view", "view=" + this + ", " + event.toString());
return onTrackballEvent(event);
}
@@ -4679,6 +4698,10 @@
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ }
+
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
final int action = event.getAction();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f7f2685..0d4f3d0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1126,6 +1126,10 @@
*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onKeyEvent(event, 1);
+ }
+
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
return super.dispatchKeyEvent(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
@@ -1152,6 +1156,10 @@
*/
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
+ }
+
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
return super.dispatchTrackballEvent(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
@@ -1332,6 +1340,10 @@
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
+ }
+
if (!onFilterTouchEventForSecurity(ev)) {
return false;
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 3c386b4..2f9d501 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -250,6 +250,13 @@
private final int mDensity;
+ /**
+ * Consistency verifier for debugging purposes.
+ */
+ protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
@@ -2316,6 +2323,14 @@
}
private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+ if (mInputEventConsistencyVerifier != null) {
+ if (event.isTouchEvent()) {
+ mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+ } else {
+ mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ }
+ }
+
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishMotionEvent(event, sendDone, false);
@@ -2422,6 +2437,10 @@
private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+ }
+
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishMotionEvent(event, sendDone, false);
@@ -2550,6 +2569,10 @@
}
private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ }
+
final int source = event.getSource();
final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
@@ -2785,6 +2808,10 @@
}
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onKeyEvent(event, 0);
+ }
+
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishKeyEvent(event, sendDone, false);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 74155b1..2a11c87 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -165,6 +165,7 @@
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
+extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
@@ -1299,6 +1300,7 @@
REG_JNI(register_android_backup_FileBackupHelperBase),
REG_JNI(register_android_backup_BackupHelperDispatcher),
+ REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputQueue),
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index a4931ac..9a727a7 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -592,6 +592,21 @@
}
// ----------------------------------------------------------------------------
+// Logging
+// ----------------------------------------------------------------------------
+
+jfieldID gFileDescriptorField;
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
+{
+#ifdef USE_OPENGL_RENDERER
+ int fd = env->GetIntField(javaFileDescriptor, gFileDescriptorField);
+ android::uirenderer::DisplayList::outputLogBuffer(fd);
+#endif // USE_OPENGL_RENDERER
+}
+
+// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -690,6 +705,12 @@
#endif
};
+static JNINativeMethod gActivityThreadMethods[] = {
+ { "dumpGraphicsInfo", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_app_ActivityThread_dumpGraphics }
+};
+
+
#ifdef USE_OPENGL_RENDERER
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
@@ -711,4 +732,19 @@
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
+const char* const kActivityThreadPathName = "android/app/ActivityThread";
+
+int register_android_app_ActivityThread(JNIEnv* env)
+{
+ jclass gFileDescriptorClass = env->FindClass("java/io/FileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+ gFileDescriptorField = env->GetFieldID(gFileDescriptorClass, "descriptor", "I");
+ LOG_FATAL_IF(gFileDescriptorField == NULL,
+ "Unable to find descriptor field in java.io.FileDescriptor");
+
+ return AndroidRuntime::registerNativeMethods(
+ env, kActivityThreadPathName,
+ gActivityThreadMethods, NELEM(gActivityThreadMethods));
+}
+
};
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 4ce471e..2ede7ec 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -430,6 +430,12 @@
return event->getFlags();
}
+static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
+ jint nativePtr, jint flags) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setFlags(flags);
+}
+
static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
jint nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -656,6 +662,9 @@
{ "nativeGetFlags",
"(I)I",
(void*)android_view_MotionEvent_nativeGetFlags },
+ { "nativeSetFlags",
+ "(II)V",
+ (void*)android_view_MotionEvent_nativeSetFlags },
{ "nativeGetEdgeFlags",
"(I)I",
(void*)android_view_MotionEvent_nativeGetEdgeFlags },
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 7fcd7b6..78f71ac 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -11,10 +11,14 @@
</div><!-- end homeTitle -->
<div id="announcement-block">
<!-- total max width is 520px -->
- <img src="{@docRoot}assets/images/home/GDC2011.png" alt="Android at GDC 2011" width="203px" style="padding-left:22px;padding-bottom:28px;padding-top:22px;"/>
+ <img src="{@docRoot}assets/images/home/IO-logo-2011.png"
+alt="Android at Google IO 2011" width="200px"
+style="padding-left:22px;padding-bottom:15px;padding-top:15px;"/>
<div id="announcement" style="width:275px">
- <p>Thanks to everyone who visited us at the <a href="http://www.gdconf.com/">Game Developers Conference</a> in San Francisco. We're looking forward to seeing your games running on Android!</p>
- <p><a href="http://android-developers.blogspot.com/2011/02/heading-for-gdc.html">Learn more »</a></p>
+ <p>Google I/O is a two-day developer event that will take place May 10-11 at Moscone Center, San
+Francisco. The agenda includes several sessions about Android, presented by Android engineers and
+other team members.</p><p><a href="http://www.google.com/events/io/2011/sessions.html">Learn
+more »</a></p>
</div> <!-- end annoucement -->
</div> <!-- end annoucement-block -->
</div><!-- end topAnnouncement -->
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 73d7fc1..5d7b651 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
<div class="dashboard-panel">
<img alt="" height="250" width="460"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:3.0,4.8,29.0,61.3,0.7,1.0,0.2&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:2.7,3.5,27.2,63.9,0.8,1.7,0.2&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -60,16 +60,16 @@
<th>API Level</th>
<th>Distribution</th>
</tr>
-<tr><td>Android 1.5</td><td>3</td><td>3.0%</td></tr>
-<tr><td>Android 1.6</td><td>4</td><td>4.8%</td></tr>
-<tr><td>Android 2.1</td><td>7</td><td>29.0%</td></tr>
-<tr><td>Android 2.2</td><td>8</td><td>61.3%</td></tr>
-<tr><td>Android 2.3</td><td>9</td><td>0.7%</td></tr>
-<tr><td>Android 2.3.3</td><td>10</td><td>1.0%</td></tr>
+<tr><td>Android 1.5</td><td>3</td><td>2.7%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>3.5%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>27.2%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>63.9%</td></tr>
+<tr><td>Android 2.3</td><td>9</td><td>0.8%</td></tr>
+<tr><td>Android 2.3.3</td><td>10</td><td>1.7%</td></tr>
<tr><td>Android 3.0</td><td>11</td><td>0.2%</td></tr>
</table>
-<p><em>Data collected during two weeks ending on March 15, 2011</em></p>
+<p><em>Data collected during two weeks ending on April 1, 2011</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -98,9 +98,9 @@
<div class="dashboard-panel">
<img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C09/15%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C03/15%7C1%3A%7C2010%7C%7C%7C%7C%7C%7C%7C2011%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.9,99.9,99.9,100.0,99.9,99.8,99.7,100.0,99.9,99.9,99.9,100.0,99.8|89.2,90.2,91.1,92.0,92.7,93.4,94.1,95.2,95.6,96.0,96.3,96.7,96.8|72.1,73.8,75.3,77.4,79.6,82.2,84.4,87.2,88.3,89.7,90.5,91.5,92.0|32.1,33.4,34.5,37.1,40.5,44.3,47.7,51.8,54.3,58.3,59.7,61.5,63.0|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.4,0.6,0.7,0.8,1.1,1.7|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0&chm=tAndroid 1.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid 1.6,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C1%3A%7C2010%7C%7C%7C%7C%7C%7C2011%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.9,99.9,100.0,99.9,99.8,99.7,100.0,99.9,99.9,99.9,100.0,99.8,99.7|90.2,91.1,92.0,92.7,93.4,94.1,95.2,95.6,96.0,96.3,96.7,96.8,97.0|73.8,75.3,77.4,79.6,82.2,84.4,87.2,88.3,89.7,90.5,91.5,92.0,93.5|33.4,34.5,37.1,40.5,44.3,47.7,51.8,54.3,58.3,59.7,61.5,63.0,66.3|0.0,0.0,0.0,0.0,0.0,0.0,0.4,0.6,0.7,0.8,1.1,1.7,2.5|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.7&chm=b,c3df9b,0,1,0|tAndroid 1.6,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
-<p><em>Last historical dataset collected during two weeks ending on March 15, 2011</em></p>
+<p><em>Last historical dataset collected during two weeks ending on April 1, 2011</em></p>
</div><!-- end dashboard-panel -->
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 8ce1d9a..a17e735 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -24,7 +24,7 @@
* disconecting the object from the native allocation for early cleanup.
*
**/
-class BaseObj {
+public class BaseObj {
BaseObj(int id, RenderScript rs) {
rs.validate();
mRS = rs;
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 6ed3c6f..d2d3bb8 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -36,6 +36,8 @@
public:
DECLARE_META_INTERFACE(SurfaceTexture);
+ enum { BUFFER_NEEDS_REALLOCATION = 1 };
+
// requestBuffer requests a new buffer for the given index. The server (i.e.
// the ISurfaceTexture implementation) assigns the newly created buffer to
// the given slot index, and the client is expected to mirror the
@@ -56,6 +58,8 @@
// should call requestBuffer to assign a new buffer to that slot. The client
// is expected to either call cancelBuffer on the dequeued slot or to fill
// in the contents of its associated buffer contents and call queueBuffer.
+ // If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is
+ // expected to call requestBuffer immediately.
virtual status_t dequeueBuffer(int *slot) = 0;
// queueBuffer indicates that the client has finished filling in the
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index afa64d3..585d288 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -121,6 +121,12 @@
// buffers before the client is done with them.
sp<IBinder> getAllocator();
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // requestBuffers when a with and height of zero is requested.
+ // A call to setDefaultBufferSize() may trigger requestBuffers() to
+ // be called from the client.
+ status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
private:
// freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
@@ -158,6 +164,23 @@
// for a slot when requestBuffer is called with that slot's index.
BufferSlot mSlots[NUM_BUFFER_SLOTS];
+ // mDefaultWidth holds the default width of allocated buffers. It is used
+ // in requestBuffers() if a width and height of zero is specified.
+ uint32_t mDefaultWidth;
+
+ // mDefaultHeight holds the default height of allocated buffers. It is used
+ // in requestBuffers() if a width and height of zero is specified.
+ uint32_t mDefaultHeight;
+
+ // mPixelFormat holds the pixel format of allocated buffers. It is used
+ // in requestBuffers() if a format of zero is specified.
+ uint32_t mPixelFormat;
+
+ // mUseDefaultSize indicates whether or not the default size should be used
+ // that is, if the last requestBuffer has been called with both width
+ // and height null.
+ bool mUseDefaultSize;
+
// mBufferCount is the number of buffer slots that the client and server
// must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by
// calling setBufferCount.
diff --git a/include/ui/Input.h b/include/ui/Input.h
index b22986d..0dc29c8 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -37,10 +37,16 @@
* Additional private constants not defined in ndk/ui/input.h.
*/
enum {
- /*
- * Private control to determine when an app is tracking a key sequence.
- */
- AKEY_EVENT_FLAG_START_TRACKING = 0x40000000
+ /* Private control to determine when an app is tracking a key sequence. */
+ AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
+
+ /* Key event is inconsistent with previously sent key events. */
+ AKEY_EVENT_FLAG_TAINTED = 0x80000000,
+};
+
+enum {
+ /* Motion event is inconsistent with previously sent motion events. */
+ AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
};
enum {
@@ -328,6 +334,8 @@
inline int32_t getFlags() const { return mFlags; }
+ inline void setFlags(int32_t flags) { mFlags = flags; }
+
inline int32_t getEdgeFlags() const { return mEdgeFlags; }
inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index cdaca47..f4e2a67 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -76,6 +76,10 @@
static void mtxMul(float out[16], const float a[16], const float b[16]);
SurfaceTexture::SurfaceTexture(GLuint tex) :
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mPixelFormat(PIXEL_FORMAT_RGBA_8888),
+ mUseDefaultSize(true),
mBufferCount(MIN_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
mCurrentTransform(0),
@@ -115,6 +119,16 @@
return OK;
}
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
+{
+ Mutex::Autolock lock(mMutex);
+ if ((w != mDefaultWidth) || (h != mDefaultHeight)) {
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ }
+ return OK;
+}
+
sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
LOGV("SurfaceTexture::requestBuffer");
@@ -124,12 +138,34 @@
mBufferCount, buf);
return 0;
}
+ if ((w && !h) || (!w & h)) {
+ LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf);
+ return 0;
+ }
+
+ const bool useDefaultSize = !w && !h;
+ if (useDefaultSize) {
+ // use the default size
+ w = mDefaultWidth;
+ h = mDefaultHeight;
+ }
+
+ const bool updateFormat = (format != 0);
+ if (!updateFormat) {
+ // keep the current (or default) format
+ format = mPixelFormat;
+ }
+
usage |= GraphicBuffer::USAGE_HW_TEXTURE;
sp<GraphicBuffer> graphicBuffer(
mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
if (graphicBuffer == 0) {
LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
} else {
+ mUseDefaultSize = useDefaultSize;
+ if (updateFormat) {
+ mPixelFormat = format;
+ }
mSlots[buf].mGraphicBuffer = graphicBuffer;
if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
@@ -155,7 +191,18 @@
if (found == INVALID_BUFFER_SLOT) {
return -EBUSY;
}
+
*buf = found;
+
+ const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+ if (buffer == NULL) {
+ return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ }
+ if ((mUseDefaultSize) &&
+ ((uint32_t(buffer->width) != mDefaultWidth) ||
+ (uint32_t(buffer->height) != mDefaultHeight))) {
+ return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ }
return OK;
}
@@ -312,10 +359,10 @@
} else {
tx = 0.0f;
}
- if (mCurrentCrop.right < buf->getWidth()) {
+ if (mCurrentCrop.right < int32_t(buf->getWidth())) {
xshrink++;
}
- if (mCurrentCrop.bottom < buf->getHeight()) {
+ if (mCurrentCrop.bottom < int32_t(buf->getHeight())) {
ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
float(buf->getHeight());
yshrink++;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index a4812d0..29fc4d3 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -25,8 +25,8 @@
SurfaceTextureClient::SurfaceTextureClient(
const sp<ISurfaceTexture>& surfaceTexture):
- mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(1),
- mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
+ mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
+ mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = setSwapInterval;
@@ -100,7 +100,8 @@
return err;
}
sp<GraphicBuffer>& gbuf(mSlots[buf]);
- if (gbuf == 0 || gbuf->getWidth() != mReqWidth ||
+ if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
+ gbuf == 0 || gbuf->getWidth() != mReqWidth ||
gbuf->getHeight() != mReqHeight ||
uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
(gbuf->getUsage() & mReqUsage) != mReqUsage) {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 94b05bc..348171d 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -100,4 +100,151 @@
eglTerminate(dpy);
}
+TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) {
+ sp<ANativeWindow> anw(mSTC);
+
+ EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1, 0, 0));
+ EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 0, -1, 0));
+ EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, -1));
+ EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1, -1, 0));
+ EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 0, 8, 0));
+ EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 8, 0, 0));
+}
+
+TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) {
+ sp<ANativeWindow> anw(mSTC);
+ android_native_buffer_t* buf;
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(1, buf->width);
+ EXPECT_EQ(1, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) {
+ sp<ANativeWindow> anw(mSTC);
+ android_native_buffer_t* buf;
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, PIXEL_FORMAT_RGB_565));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(16, buf->width);
+ EXPECT_EQ(8, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) {
+ sp<ANativeWindow> anw(mSTC);
+ android_native_buffer_t* buf;
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(1, buf->width);
+ EXPECT_EQ(1, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) {
+ sp<ANativeWindow> anw(mSTC);
+ android_native_buffer_t* buf;
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(16, buf->width);
+ EXPECT_EQ(8, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) {
+ sp<ANativeWindow> anw(mSTC);
+ android_native_buffer_t* buf;
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(16, buf->width);
+ EXPECT_EQ(8, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(1, buf->width);
+ EXPECT_EQ(1, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
+ sp<ANativeWindow> anw(mSTC);
+ android_native_buffer_t* buf;
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(1, buf->width);
+ EXPECT_EQ(1, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(16, buf->width);
+ EXPECT_EQ(8, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf;
+ EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+ EXPECT_EQ(16, buf->width);
+ EXPECT_EQ(8, buf->height);
+ EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[2];
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_NE(buf[0], buf[1]);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+ EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_EQ(16, buf[0]->width);
+ EXPECT_EQ(16, buf[1]->width);
+ EXPECT_EQ(8, buf[0]->height);
+ EXPECT_EQ(8, buf[1]->height);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[2];
+ EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_EQ(16, buf[0]->width);
+ EXPECT_EQ(16, buf[1]->width);
+ EXPECT_EQ(8, buf[0]->height);
+ EXPECT_EQ(8, buf[1]->height);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+ EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 12, 24, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_EQ(12, buf[0]->width);
+ EXPECT_EQ(12, buf[1]->width);
+ EXPECT_EQ(24, buf[0]->height);
+ EXPECT_EQ(24, buf[1]->height);
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+}
+
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index b465fee..a98e4cd 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -9,6 +9,7 @@
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
+ DisplayListLogBuffer.cpp \
DisplayListRenderer.cpp \
FboCache.cpp \
GradientCache.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4f5edd5..cd48429 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "OpenGLRenderer"
#include <utils/Log.h>
+#include <utils/String8.h>
#include "Caches.h"
#include "Properties.h"
@@ -69,30 +70,43 @@
///////////////////////////////////////////////////////////////////////////////
void Caches::dumpMemoryUsage() {
- LOGD("Current memory usage / total memory usage (bytes):");
- LOGD(" TextureCache %8d / %8d", textureCache.getSize(), textureCache.getMaxSize());
- LOGD(" LayerCache %8d / %8d", layerCache.getSize(), layerCache.getMaxSize());
- LOGD(" GradientCache %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize());
- LOGD(" PathCache %8d / %8d", pathCache.getSize(), pathCache.getMaxSize());
- LOGD(" CircleShapeCache %8d / %8d",
+ String8 stringLog;
+ dumpMemoryUsage(stringLog);
+ LOGD("%s", stringLog.string());
+ delete stringLog;
+}
+
+void Caches::dumpMemoryUsage(String8 &log) {
+ log.appendFormat("Current memory usage / total memory usage (bytes):\n");
+ log.appendFormat(" TextureCache %8d / %8d\n",
+ textureCache.getSize(), textureCache.getMaxSize());
+ log.appendFormat(" LayerCache %8d / %8d\n",
+ layerCache.getSize(), layerCache.getMaxSize());
+ log.appendFormat(" GradientCache %8d / %8d\n",
+ gradientCache.getSize(), gradientCache.getMaxSize());
+ log.appendFormat(" PathCache %8d / %8d\n",
+ pathCache.getSize(), pathCache.getMaxSize());
+ log.appendFormat(" CircleShapeCache %8d / %8d\n",
circleShapeCache.getSize(), circleShapeCache.getMaxSize());
- LOGD(" OvalShapeCache %8d / %8d",
+ log.appendFormat(" OvalShapeCache %8d / %8d\n",
ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
- LOGD(" RoundRectShapeCache %8d / %8d",
+ log.appendFormat(" RoundRectShapeCache %8d / %8d\n",
roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
- LOGD(" RectShapeCache %8d / %8d",
+ log.appendFormat(" RectShapeCache %8d / %8d\n",
rectShapeCache.getSize(), rectShapeCache.getMaxSize());
- LOGD(" ArcShapeCache %8d / %8d",
+ log.appendFormat(" ArcShapeCache %8d / %8d\n",
arcShapeCache.getSize(), arcShapeCache.getMaxSize());
- LOGD(" TextDropShadowCache %8d / %8d", dropShadowCache.getSize(),
+ log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
const uint32_t size = fontRenderer.getFontRendererSize(i);
- LOGD(" FontRenderer %d %8d / %8d", i, size, size);
+ log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size);
}
- LOGD("Other:");
- LOGD(" FboCache %8d / %8d", fboCache.getSize(), fboCache.getMaxSize());
- LOGD(" PatchCache %8d / %8d", patchCache.getSize(), patchCache.getMaxSize());
+ log.appendFormat("Other:");
+ log.appendFormat(" FboCache %8d / %8d\n",
+ fboCache.getSize(), fboCache.getMaxSize());
+ log.appendFormat(" PatchCache %8d / %8d\n",
+ patchCache.getSize(), patchCache.getMaxSize());
uint32_t total = 0;
total += textureCache.getSize();
@@ -109,9 +123,8 @@
total += fontRenderer.getFontRendererSize(i);
}
- LOGD("Total memory usage:");
- LOGD(" %d bytes, %.2f MB", total, total / 1024.0f / 1024.0f);
- LOGD("\n");
+ log.appendFormat("Total memory usage:\n");
+ log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 0a9335f..7d02cf8 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -140,6 +140,7 @@
* Displays the memory usage of each cache and the total sum.
*/
void dumpMemoryUsage();
+ void dumpMemoryUsage(String8& log);
bool blend;
GLenum lastSrcMode;
diff --git a/libs/hwui/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp
new file mode 100644
index 0000000..f204644
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "DisplayListLogBuffer.h"
+
+// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
+// that mStart always points at the next command, not just the next item
+#define COMMAND_SIZE 2
+#define NUM_COMMANDS 50
+#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1)
+
+/**
+ * DisplayListLogBuffer is a utility class which logs the most recent display
+ * list operations in a circular buffer. The log is process-wide, because we
+ * only care about the most recent operations, not the operations on a per-window
+ * basis for a given activity. The purpose of the log is to provide more debugging
+ * information in a bug report, by telling us not just where a process hung (which
+ * generally is just reported as a stack trace at the Java level) or crashed, but
+ * also what happened immediately before that hang or crash. This may help track down
+ * problems in the native rendering code or driver interaction related to the display
+ * list operations that led up to the hang or crash.
+ *
+ * The log is implemented as a circular buffer for both space and performance
+ * reasons - we only care about the last several operations to give us context
+ * leading up to the problem, and we don't want to constantly copy data around or do
+ * additional mallocs to keep the most recent operations logged. Only numbers are
+ * logged to make the operation fast. If and when the log is output, we process this
+ * data into meaningful strings.
+ *
+ * There is an assumption about the format of the command (currently 2 ints: the
+ * opcode and the nesting level). If the type of information logged changes (for example,
+ * we may want to save a timestamp), then the size of the buffer and the way the
+ * information is recorded in writeCommand() should change to suit.
+ */
+
+namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer);
+#endif
+
+namespace uirenderer {
+
+
+DisplayListLogBuffer::DisplayListLogBuffer() {
+ mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int));
+ mStart = mBufferFirst;
+ mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
+ mEnd = mStart;
+}
+
+DisplayListLogBuffer::~DisplayListLogBuffer() {
+ free(mBufferFirst);
+}
+
+/**
+ * Called from DisplayListRenderer to output the current buffer into the
+ * specified FILE. This only happens in a dumpsys/bugreport operation.
+ */
+void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[])
+{
+ int *tmpBufferPtr = mStart;
+ while (true) {
+ if (tmpBufferPtr == mEnd) {
+ break;
+ }
+ int level = *tmpBufferPtr++;
+ if (tmpBufferPtr > mBufferLast) {
+ tmpBufferPtr = mBufferFirst;
+ }
+ int op = *tmpBufferPtr++;
+ if (tmpBufferPtr > mBufferLast) {
+ tmpBufferPtr = mBufferFirst;
+ }
+ uint32_t count = (level + 1) * 2;
+ char indent[count + 1];
+ for (uint32_t i = 0; i < count; i++) {
+ indent[i] = ' ';
+ }
+ indent[count] = '\0';
+ fprintf(file, "%s%s\n", indent, opNames[op]);
+ }
+}
+
+void DisplayListLogBuffer::writeCommand(int level, int op) {
+ writeInt(level);
+ writeInt(op);
+}
+
+/**
+ * Store the given value in the buffer and increment/wrap the mEnd
+ * and mStart values as appropriate.
+ */
+void DisplayListLogBuffer::writeInt(int value) {
+ *((int*)mEnd) = value;
+ if (mEnd == mBufferLast) {
+ mEnd = mBufferFirst;
+ } else {
+ mEnd++;
+ }
+ if (mEnd == mStart) {
+ mStart++;
+ if (mStart > mBufferLast) {
+ mStart = mBufferFirst;
+ }
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h
new file mode 100644
index 0000000..bf16f29
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+
+#include <utils/Singleton.h>
+#include <stdio.h>
+
+namespace android {
+namespace uirenderer {
+
+class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> {
+ DisplayListLogBuffer();
+ ~DisplayListLogBuffer();
+
+ friend class Singleton<DisplayListLogBuffer>;
+
+public:
+ void writeCommand(int level, int op);
+ void writeInt(int value);
+ void outputCommands(FILE *file, const char* opNames[]);
+
+ bool isEmpty() {
+ return (mStart == mEnd);
+ }
+
+private:
+ int *mBufferFirst; // where the memory starts
+ int* mStart; // where the current command stream starts
+ int* mEnd; // where the current commands end
+ int* mBufferLast; // where the buffer memory ends
+
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index c7459d1..34dda9a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -16,11 +16,16 @@
#define LOG_TAG "OpenGLRenderer"
+
+#include "DisplayListLogBuffer.h"
#include "DisplayListRenderer.h"
+#include <utils/String8.h>
+#include "Caches.h"
namespace android {
namespace uirenderer {
+
///////////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////////
@@ -64,6 +69,20 @@
"DrawGLFunction"
};
+void DisplayList::outputLogBuffer(int fd) {
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ if (logBuffer.isEmpty()) {
+ return;
+ }
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ FILE *file = fdopen(fd, "a");
+ fprintf(file, "\nCaches:\n%s", cachesLog.string());
+ fprintf(file, "\nRecent DisplayList operations\n");
+ logBuffer.outputCommands(file, OP_NAMES);
+ fflush(file);
+}
+
DisplayList::DisplayList(const DisplayListRenderer& recorder) {
initFromDisplayListRenderer(recorder);
}
@@ -173,9 +192,11 @@
DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this);
#endif
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
while (!mReader.eof()) {
int op = mReader.readInt();
+ logBuffer.writeCommand(level, op);
switch (op) {
case DrawGLFunction: {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index da57e4a..b782103 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -26,6 +26,7 @@
#include <SkTDArray.h>
#include <SkTSearch.h>
+#include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h"
#include "utils/Functor.h"
@@ -106,6 +107,8 @@
bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
+ static void outputLogBuffer(int fd);
+
private:
void init();
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 440ec00..bbe579e 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -724,7 +724,7 @@
LOGD(" %d: position (%0.3f, %0.3f), vx=%0.3f, vy=%0.3f, speed=%0.3f",
id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
} else {
- assert(vx == 0 && vy == 0);
+ LOG_ASSERT(vx == 0 && vy == 0);
LOGD(" %d: position (%0.3f, %0.3f), velocity not available",
id, positions[index].x, positions[index].y);
}
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index af37c9e..47bb8c9 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -61,7 +61,9 @@
}
/**
- * Closes all resources related to the MtpDevice object
+ * Closes all resources related to the MtpDevice object.
+ * After this is called, the object can not be used until {@link #open} is called again
+ * with a new {@link android.hardware.usb.UsbDeviceConnection}.
*/
public void close() {
native_close();
@@ -78,6 +80,8 @@
/**
* Returns the name of the USB device
+ * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceName}
+ * for the device's {@link android.hardware.usb.UsbDevice}
*
* @return the device name
*/
@@ -86,7 +90,9 @@
}
/**
- * Returns the ID of the USB device
+ * Returns the USB ID of the USB device.
+ * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceId}
+ * for the device's {@link android.hardware.usb.UsbDevice}
*
* @return the device ID
*/
@@ -100,7 +106,7 @@
}
/**
- * Returns the {@link android.mtp.MtpDeviceInfo} for this device
+ * Returns the {@link MtpDeviceInfo} for this device
*
* @return the device info
*/
@@ -110,8 +116,9 @@
/**
* Returns the list of IDs for all storage units on this device
+ * Information about each storage unit can be accessed via {@link #getStorageInfo}.
*
- * @return the storage IDs
+ * @return the list of storage IDs
*/
public int[] getStorageIds() {
return native_get_storage_ids();
@@ -120,6 +127,7 @@
/**
* Returns the list of object handles for all objects on the given storage unit,
* with the given format and parent.
+ * Information about each object can be accessed via {@link #getObjectInfo}.
*
* @param storageId the storage unit to query
* @param format the format of the object to return, or zero for all formats
@@ -132,10 +140,12 @@
/**
* Returns the data for an object as a byte array.
+ * This call may block for an arbitrary amount of time depending on the size
+ * of the data and speed of the devices.
*
* @param objectHandle handle of the object to read
* @param objectSize the size of the object (this should match
- * {@link android.mtp.MtpObjectInfo#getCompressedSize}
+ * {@link MtpObjectInfo#getCompressedSize}
* @return the object's data, or null if reading fails
*/
public byte[] getObject(int objectHandle, int objectSize) {
@@ -144,6 +154,10 @@
/**
* Returns the thumbnail data for an object as a byte array.
+ * The size and format of the thumbnail data can be determined via
+ * {@link MtpObjectInfo#getThumbCompressedSize} and
+ * {@link MtpObjectInfo#getThumbFormat}.
+ * For typical devices the format is JPEG.
*
* @param objectHandle handle of the object to read
* @return the object's thumbnail, or null if reading fails
@@ -153,7 +167,7 @@
}
/**
- * Retrieves the {@link android.mtp.MtpStorageInfo} for a storage unit.
+ * Retrieves the {@link MtpStorageInfo} for a storage unit.
*
* @param storageId the ID of the storage unit
* @return the MtpStorageInfo
@@ -163,7 +177,7 @@
}
/**
- * Retrieves the {@link android.mtp.MtpObjectInfo} for an object.
+ * Retrieves the {@link MtpObjectInfo} for an object.
*
* @param objectHandle the handle of the object
* @return the MtpObjectInfo
@@ -173,7 +187,9 @@
}
/**
- * Deletes an object on the device.
+ * Deletes an object on the device. This call may block, since
+ * deleting a directory containing many files may take a long time
+ * on some devices.
*
* @param objectHandle handle of the object to delete
* @return true if the deletion succeeds
@@ -204,6 +220,8 @@
/**
* Copies the data for an object to a file in external storage.
+ * This call may block for an arbitrary amount of time depending on the size
+ * of the data and speed of the devices.
*
* @param objectHandle handle of the object to read
* @param destPath path to destination for the file transfer.
diff --git a/media/java/android/mtp/MtpStorageInfo.java b/media/java/android/mtp/MtpStorageInfo.java
index 09736a8..d1b86fc 100644
--- a/media/java/android/mtp/MtpStorageInfo.java
+++ b/media/java/android/mtp/MtpStorageInfo.java
@@ -34,7 +34,8 @@
}
/**
- * Returns the storage ID for the storage unit
+ * Returns the storage ID for the storage unit.
+ * The storage ID uniquely identifies the storage unit on the MTP device.
*
* @return the storage ID
*/
@@ -61,7 +62,9 @@
}
/**
- * Returns the description string for the storage unit
+ * Returns the description string for the storage unit.
+ * This is typically displayed to the user in the user interface on the
+ * MTP host.
*
* @return the storage unit description
*/
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6d074e9..0f9cbec 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -317,8 +317,7 @@
if (surfaceTexture != NULL) {
sp<ISurfaceTexture> native_surfaceTexture(
getSurfaceTexture(env, surfaceTexture));
- LOGV("%s: texture=%p (id=%d)", prefix,
- native_surfaceTexture.get(), native_surfaceTexture->getIdentity());
+ LOGV("%s: texture=%p", prefix, native_surfaceTexture.get());
mp->setVideoSurfaceTexture(native_surfaceTexture);
}
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 6b0b1b9..f25d498 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -55,7 +55,6 @@
#include <cutils/properties.h>
#define USE_SURFACE_ALLOC 1
-#define FRAME_DROP_FREQ 0
namespace android {
@@ -1440,7 +1439,6 @@
if (mFlags & FIRST_FRAME) {
mFlags &= ~FIRST_FRAME;
- mSinceLastDropped = 0;
mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
}
@@ -1487,17 +1485,13 @@
if (latenessUs > 40000) {
// We're more than 40ms late.
- LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
- if ( mSinceLastDropped > FRAME_DROP_FREQ)
- {
- LOGV("we're late by %lld us (%.2f secs) dropping one after %d frames", latenessUs, latenessUs / 1E6, mSinceLastDropped);
- mSinceLastDropped = 0;
- mVideoBuffer->release();
- mVideoBuffer = NULL;
+ LOGV("we're late by %lld us (%.2f secs), dropping frame",
+ latenessUs, latenessUs / 1E6);
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
- postVideoEvent_l();
- return;
- }
+ postVideoEvent_l();
+ return;
}
if (latenessUs < -10000) {
@@ -1515,7 +1509,6 @@
}
if (mVideoRenderer != NULL) {
- mSinceLastDropped++;
mVideoRenderer->render(mVideoBuffer);
}
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index a9b7ae8..7fd7724 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -163,7 +163,6 @@
uint32_t mFlags;
uint32_t mExtractorFlags;
- uint32_t mSinceLastDropped;
int64_t mTimeSourceDeltaUs;
int64_t mVideoTimeUs;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index b3e29b9..64266b8 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -291,6 +291,7 @@
Mutex::Autolock autoLock(mExtractor->mLock);
mCluster = mExtractor->mSegment->GetFirst();
+ mBlockEntry = NULL;
mBlockEntryIndex = 0;
do {
@@ -302,11 +303,13 @@
Mutex::Autolock autoLock(mExtractor->mLock);
mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
+ mBlockEntry = NULL;
mBlockEntryIndex = 0;
- while (!eos() && block()->GetTrackNumber() != mTrackNum) {
+ do {
advance_l();
}
+ while (!eos() && block()->GetTrackNumber() != mTrackNum);
while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
advance_l();
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
index ae1993d..8d42edb 100644
--- a/native/android/native_window.cpp
+++ b/native/android/native_window.cpp
@@ -68,8 +68,7 @@
int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
int32_t height, int32_t format) {
- native_window_set_buffers_geometry(window, width, height, format);
- return 0;
+ return native_window_set_buffers_geometry(window, width, height, format);
}
int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index f3d7550..337fa96 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -95,6 +95,9 @@
*
* For all of these parameters, if 0 is supplied then the window's base
* value will come back in force.
+ *
+ * width and height must be either both zero or both non-zero.
+ *
*/
int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format);
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 7625adb..9a9d9e5 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -450,7 +450,7 @@
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
// Note that we only allow one caller to getEvents(), so don't need
// to do locking here... only when adding/removing devices.
- assert(bufferSize >= 1);
+ LOG_ASSERT(bufferSize >= 1);
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 8363e8b..253d070 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -369,7 +369,7 @@
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if we intend to drop them.
- assert(mPendingEvent != NULL);
+ LOG_ASSERT(mPendingEvent != NULL);
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
@@ -431,7 +431,7 @@
}
default:
- assert(false);
+ LOG_ASSERT(false);
break;
}
@@ -558,7 +558,7 @@
reason = "inbound event was dropped because it is stale";
break;
default:
- assert(false);
+ LOG_ASSERT(false);
return;
}
@@ -962,7 +962,7 @@
toString(resumeWithAppendedMotionSample));
#endif
- assert(eventEntry->dispatchInProgress); // should already have been set to true
+ LOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(eventEntry);
@@ -1596,9 +1596,10 @@
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = mMonitoringChannels[i];
- target.flags = 0;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
target.xOffset = 0;
target.yOffset = 0;
+ target.pointerIds.clear();
}
}
@@ -1711,7 +1712,7 @@
// Make sure we are never called for streaming when splitting across multiple windows.
bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
- assert(! (resumeWithAppendedMotionSample && isSplit));
+ LOG_ASSERT(! (resumeWithAppendedMotionSample && isSplit));
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
@@ -1725,7 +1726,7 @@
// Split a motion event if needed.
if (isSplit) {
- assert(eventEntry->type == EventEntry::TYPE_MOTION);
+ LOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
@@ -1832,7 +1833,7 @@
resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS);
// If the outbound queue was previously empty, start the dispatch cycle going.
- if (wasEmpty) {
+ if (wasEmpty && !connection->outboundQueue.isEmpty()) {
activateConnectionLocked(connection.get());
startDispatchCycleLocked(currentTime, connection);
}
@@ -1880,11 +1881,11 @@
connection->getInputChannelName());
#endif
- assert(connection->status == Connection::STATUS_NORMAL);
- assert(! connection->outboundQueue.isEmpty());
+ LOG_ASSERT(connection->status == Connection::STATUS_NORMAL);
+ LOG_ASSERT(! connection->outboundQueue.isEmpty());
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
- assert(! dispatchEntry->inProgress);
+ LOG_ASSERT(! dispatchEntry->inProgress);
// Mark the dispatch entry as in progress.
dispatchEntry->inProgress = true;
@@ -2005,7 +2006,7 @@
}
default: {
- assert(false);
+ LOG_ASSERT(false);
}
}
@@ -2242,7 +2243,7 @@
InputDispatcher::MotionEntry*
InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
- assert(pointerIds.value != 0);
+ LOG_ASSERT(pointerIds.value != 0);
uint32_t splitPointerIndexMap[MAX_POINTERS];
int32_t splitPointerIds[MAX_POINTERS];
@@ -3510,7 +3511,7 @@
return;
}
- assert(connection->outboundQueue.headSentinel.next == dispatchEntry);
+ LOG_ASSERT(connection->outboundQueue.headSentinel.next == dispatchEntry);
// Latch the fallback keycode for this key on an initial down.
// The fallback keycode cannot change at any other point in the lifecycle.
@@ -3523,7 +3524,7 @@
connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
}
- assert(fallbackKeyCode != -1);
+ LOG_ASSERT(fallbackKeyCode != -1);
// Cancel the fallback key if the policy decides not to send it anymore.
// We will continue to dispatch the key to the policy but we will no
@@ -3761,7 +3762,7 @@
if (injectionState->refCount == 0) {
mInjectionStatePool.free(injectionState);
} else {
- assert(injectionState->refCount > 0);
+ LOG_ASSERT(injectionState->refCount > 0);
}
}
@@ -3777,7 +3778,7 @@
releaseMotionEntry(static_cast<MotionEntry*>(entry));
break;
default:
- assert(false);
+ LOG_ASSERT(false);
break;
}
}
@@ -3789,7 +3790,7 @@
releaseEventEntryInjectionState(entry);
mConfigurationChangeEntryPool.free(entry);
} else {
- assert(entry->refCount > 0);
+ LOG_ASSERT(entry->refCount > 0);
}
}
@@ -3799,7 +3800,7 @@
releaseEventEntryInjectionState(entry);
mKeyEntryPool.free(entry);
} else {
- assert(entry->refCount > 0);
+ LOG_ASSERT(entry->refCount > 0);
}
}
@@ -3814,7 +3815,7 @@
}
mMotionEntryPool.free(entry);
} else {
- assert(entry->refCount > 0);
+ LOG_ASSERT(entry->refCount > 0);
}
}
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 82cf62f..6db445e 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -293,7 +293,7 @@
handleConfigurationChanged(rawEvent->when);
break;
default:
- assert(false); // can't happen
+ LOG_ASSERT(false); // can't happen
break;
}
}
@@ -1378,7 +1378,7 @@
dump.append(INDENT4 "Mode: navigation\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
@@ -1801,7 +1801,7 @@
mPointerSource = AINPUT_SOURCE_MOUSE;
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
// Configure absolute axis information.
@@ -1874,7 +1874,7 @@
dump.append(INDENT4 "DeviceType: pointer\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
@@ -2509,7 +2509,7 @@
dump.append(INDENT4 "touch.touchSize.calibration: pressure\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
// Tool Size
@@ -2527,7 +2527,7 @@
dump.append(INDENT4 "touch.toolSize.calibration: area\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
if (mCalibration.haveToolSizeLinearScale) {
@@ -2567,7 +2567,7 @@
dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
switch (mCalibration.pressureSource) {
@@ -2580,7 +2580,7 @@
case Calibration::PRESSURE_SOURCE_DEFAULT:
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
if (mCalibration.havePressureScale) {
@@ -2597,7 +2597,7 @@
dump.append(INDENT4 "touch.size.calibration: normalized\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
// Orientation
@@ -2612,7 +2612,7 @@
dump.append(INDENT4 "touch.orientation.calibration: vector\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
}
@@ -2901,7 +2901,7 @@
// Although applications receive new locations as part of individual pointer up
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded) {
- assert(moveIdBits.value == dispatchedIdBits.value);
+ LOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
dispatchMotion(when, policyFlags, mTouchSource,
AMOTION_EVENT_ACTION_MOVE, 0, metaState, 0,
mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, -1,
@@ -3550,7 +3550,7 @@
// Case 4. Exactly one finger down, button is not pressed. (HOVER)
// The pointer follows the active touch point.
// Emit HOVER_MOVE events at the pointer location.
- assert(activeTouchId >= 0);
+ LOG_ASSERT(activeTouchId >= 0);
#if DEBUG_GESTURES
LOGD("Gestures: HOVER");
@@ -3600,7 +3600,7 @@
// Fix the centroid of the figure when the gesture actually starts.
// We do not recalculate the centroid at any other time during the gesture because
// it would affect the relationship of the touch points relative to the pointer location.
- assert(activeTouchId >= 0);
+ LOG_ASSERT(activeTouchId >= 0);
uint32_t currentTouchPointerCount = mCurrentTouch.pointerCount;
if (currentTouchPointerCount > MAX_POINTERS) {
@@ -3712,7 +3712,7 @@
"activeGestureId=%d, currentTouchPointerCount=%d",
activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount);
#endif
- assert(mPointerGesture.activeGestureId >= 0);
+ LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
float x = (mCurrentTouch.pointers[0].x + mCurrentTouch.pointers[1].x
- mPointerGesture.initialCentroidX * 2) * 0.5f
@@ -3736,7 +3736,7 @@
"activeGestureId=%d, currentTouchPointerCount=%d",
activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount);
#endif
- assert(mPointerGesture.activeGestureId >= 0);
+ LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
mPointerGesture.currentGesturePointerCount = currentTouchPointerCount;
mPointerGesture.currentGestureIdBits.clear();
@@ -3895,7 +3895,7 @@
pointerCount += 1;
}
- assert(pointerCount != 0);
+ LOG_ASSERT(pointerCount != 0);
if (changedId >= 0 && pointerCount == 1) {
// Replace initial down and final up action.
@@ -3907,7 +3907,7 @@
action = AMOTION_EVENT_ACTION_UP;
} else {
// Can't happen.
- assert(false);
+ LOG_ASSERT(false);
}
}
@@ -4072,7 +4072,7 @@
// Previous iterations consumed the root element of the heap.
// Pop root element off of the heap (sift down).
heapSize -= 1;
- assert(heapSize > 0);
+ LOG_ASSERT(heapSize > 0);
// Sift down.
heap[0] = heap[heapSize];
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 12ac052..3997702 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -543,18 +543,8 @@
*/
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
- for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
- if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
- continue;
- }
- NetworkStateTracker t = mNetTrackers[type];
- NetworkInfo info = t.getNetworkInfo();
- if (info.isConnected()) {
- if (DBG && type != mActiveDefaultNetwork) {
- loge("connected default network is not mActiveDefaultNetwork!");
- }
- return info;
- }
+ if (mActiveDefaultNetwork != -1) {
+ return mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
}
return null;
}
@@ -1353,6 +1343,19 @@
handleApplyDefaultProxy(netType);
addDefaultRoute(mNetTrackers[netType]);
} else {
+ // many radios add a default route even when we don't want one.
+ // remove the default route unless we need it for our active network
+ if (mActiveDefaultNetwork != -1) {
+ LinkProperties defaultLinkProperties =
+ mNetTrackers[mActiveDefaultNetwork].getLinkProperties();
+ LinkProperties newLinkProperties =
+ mNetTrackers[netType].getLinkProperties();
+ String defaultIface = defaultLinkProperties.getInterfaceName();
+ if (defaultIface != null &&
+ !defaultIface.equals(newLinkProperties.getInterfaceName())) {
+ removeDefaultRoute(mNetTrackers[netType]);
+ }
+ }
addPrivateDnsRoutes(mNetTrackers[netType]);
}
} else {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 50fffd0..5f471fe 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1276,6 +1276,7 @@
ServiceManager.addService("activity", m);
ServiceManager.addService("meminfo", new MemBinder(m));
+ ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(m));
}
@@ -1429,6 +1430,46 @@
}
}
+ static class GraphicsBinder extends Binder {
+ ActivityManagerService mActivityManagerService;
+ GraphicsBinder(ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ ActivityManagerService service = mActivityManagerService;
+ ArrayList<ProcessRecord> procs;
+ synchronized (mActivityManagerService) {
+ if (args != null && args.length > 0
+ && args[0].charAt(0) != '-') {
+ procs = new ArrayList<ProcessRecord>();
+ int pid = -1;
+ try {
+ pid = Integer.parseInt(args[0]);
+ } catch (NumberFormatException e) {
+
+ }
+ for (int i=service.mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord proc = service.mLruProcesses.get(i);
+ if (proc.pid == pid) {
+ procs.add(proc);
+ } else if (proc.processName.equals(args[0])) {
+ procs.add(proc);
+ }
+ }
+ if (procs.size() <= 0) {
+ pw.println("No process found for: " + args[0]);
+ return;
+ }
+ } else {
+ procs = new ArrayList<ProcessRecord>(service.mLruProcesses);
+ }
+ }
+ dumpGraphicsHardwareUsage(fd, pw, procs);
+ }
+ }
+
static class CpuBinder extends Binder {
ActivityManagerService mActivityManagerService;
CpuBinder(ActivityManagerService activityManagerService) {
@@ -8471,6 +8512,28 @@
}
}
+ static final void dumpGraphicsHardwareUsage(FileDescriptor fd,
+ PrintWriter pw, List list) {
+ String args[] = {"graphics"};
+ pw.println("-------------------------------------------------------------------------------");
+ pw.println("DUMP OF GRAPHICS ACCELERATION INFO:");
+ for (int i = list.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = (ProcessRecord)list.get(i);
+ if (r.thread != null) {
+ pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+ pw.flush();
+ try {
+ r.thread.asBinder().dump(fd, args);
+ } catch (RemoteException e) {
+ pw.println("Got RemoteException!");
+ pw.flush();
+ }
+ }
+ }
+ pw.println("\n");
+ pw.flush();
+ }
+
static final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, List list, String prefix, String[] args) {
final boolean isCheckinRequest = scanArgs(args, "--checkin");
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
index 78b87fe..7e1ab07 100644
--- a/services/java/com/android/server/wm/InputFilter.java
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -20,6 +20,7 @@
import android.os.Looper;
import android.os.Message;
import android.view.InputEvent;
+import android.view.InputEventConsistencyVerifier;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
@@ -100,6 +101,16 @@
private final H mH;
private Host mHost;
+ // Consistency verifiers for debugging purposes.
+ private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this,
+ InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null;
+ private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this,
+ InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null;
+
/**
* Creates the input filter.
*
@@ -152,6 +163,9 @@
throw new IllegalStateException("Cannot send input event because the input filter " +
"is not installed.");
}
+ if (mOutboundInputEventConsistencyVerifier != null) {
+ mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+ }
mHost.sendInputEvent(event, policyFlags);
}
@@ -201,6 +215,12 @@
switch (msg.what) {
case MSG_INSTALL:
mHost = (Host)msg.obj;
+ if (mInboundInputEventConsistencyVerifier != null) {
+ mInboundInputEventConsistencyVerifier.reset();
+ }
+ if (mOutboundInputEventConsistencyVerifier != null) {
+ mOutboundInputEventConsistencyVerifier.reset();
+ }
onInstalled();
break;
@@ -215,6 +235,9 @@
case MSG_INPUT_EVENT: {
final InputEvent event = (InputEvent)msg.obj;
try {
+ if (mInboundInputEventConsistencyVerifier != null) {
+ mInboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+ }
onInputEvent(event, msg.arg1);
} finally {
event.recycle();
diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index bd1bc1d..a86ea7e 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -42,7 +42,8 @@
ArrayList<ApnSetting> mWaitingApns = null;
- private int mWaitingApnsPermanentFailureCountDown = 0;
+ /** A zero indicates that all waiting APNs had a permanent error */
+ private int mWaitingApnsPermanentFailureCountDown;
ApnSetting mApnSetting;
@@ -68,14 +69,6 @@
pendingAction = pa;
}
- public int getPermFailCount() {
- return mWaitingApnsPermanentFailureCountDown;
- }
-
- public void decPermFailCount() {
- mWaitingApnsPermanentFailureCountDown--;
- }
-
public String getApnType() {
return mApnType;
}
@@ -98,9 +91,18 @@
public void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
mWaitingApns = waitingApns;
+ mWaitingApnsPermanentFailureCountDown = mWaitingApns.size();
}
- public ApnSetting getNextApn() {
+ public int getWaitingApnsPermFailCount() {
+ return mWaitingApnsPermanentFailureCountDown;
+ }
+
+ public void decWaitingApnsPermFailCount() {
+ mWaitingApnsPermanentFailureCountDown--;
+ }
+
+ public ApnSetting getNextWaitingApn() {
ArrayList<ApnSetting> list = mWaitingApns;
ApnSetting apn = null;
@@ -112,7 +114,7 @@
return apn;
}
- public void removeNextApn() {
+ public void removeNextWaitingApn() {
if ((mWaitingApns != null) && (!mWaitingApns.isEmpty())) {
mWaitingApns.remove(0);
}
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 1b5bad9..5501361 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -68,6 +68,9 @@
protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
+ /* Radio power off pending flag and tag counter */
+ private boolean mPendingRadioPowerOffAfterDataOff = false;
+ private int mPendingRadioPowerOffAfterDataOffTag = 0;
protected static final boolean DBG = true;
@@ -77,8 +80,6 @@
/** Waiting period before recheck gprs and voice registration. */
public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
- public static final int DATA_STATE_POLL_SLEEP_MS = 100;
-
/** GSM events */
protected static final int EVENT_RADIO_STATE_CHANGED = 1;
protected static final int EVENT_NETWORK_STATE_CHANGED = 2;
@@ -262,7 +263,29 @@
}
}
- public abstract void handleMessage(Message msg);
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_SET_RADIO_POWER_OFF:
+ synchronized(this) {
+ if (mPendingRadioPowerOffAfterDataOff &&
+ (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
+ if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOffTag += 1;
+ mPendingRadioPowerOffAfterDataOff = false;
+ } else {
+ log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
+ "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
+ }
+ }
+ break;
+
+ default:
+ log("Unhandled message with number: " + msg.what);
+ break;
+ }
+ }
protected abstract Phone getPhone();
protected abstract void handlePollStateResult(int what, AsyncResult ar);
@@ -370,7 +393,52 @@
*
* Hang up the existing voice calls to decrease call drop rate.
*/
- public abstract void powerOffRadioSafely();
+ public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
+ synchronized (this) {
+ if (!mPendingRadioPowerOffAfterDataOff) {
+ if (dcTracker.isAnyActiveDataConnections()) {
+ dcTracker.cleanUpAllConnections(null);
+ Message msg = Message.obtain(this);
+ msg.what = EVENT_SET_RADIO_POWER_OFF;
+ msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
+ if (sendMessageDelayed(msg, 30000)) {
+ if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+ mPendingRadioPowerOffAfterDataOff = true;
+ } else {
+ log("Cannot send delayed Msg, turn off radio right away.");
+ hangupAndPowerOff();
+ }
+ } else {
+ dcTracker.cleanUpAllConnections(null);
+ if (DBG) log("Data disconnected, turn off radio right away.");
+ hangupAndPowerOff();
+ }
+ }
+ }
+ }
+
+ /**
+ * process the pending request to turn radio off after data is disconnected
+ *
+ * return true if there is pending request to process; false otherwise.
+ */
+ public boolean processPendingRadioPowerOffAfterDataOff() {
+ synchronized(this) {
+ if (mPendingRadioPowerOffAfterDataOff) {
+ if (DBG) log("Process pending request to turn radio off.");
+ mPendingRadioPowerOffAfterDataOffTag += 1;
+ hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOff = false;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Hang up all voice call and turn off radio. Implemented by derived class.
+ */
+ protected abstract void hangupAndPowerOff();
/** Cancel a pending (if any) pollState() operation */
protected void cancelPollState() {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index c4fab66..4927006 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -75,7 +75,7 @@
/** 'true' if device supports both LTE and CDMA mode of operation.
* Availability: Set only on devices supporting LTE and CDMA.
*/
- static final String PROPERTY_NETWORK_LTE_ON_CDMA = "ro.telephony.lte_on_cdma";
+ static final String PROPERTY_NETWORK_LTE_ON_CDMA = "telephony.lte_on_cdma";
static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type";
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 3d10c9c..05361cd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -907,6 +907,11 @@
}
@Override
+ public boolean isAnyActiveDataConnections() {
+ return (mState != State.IDLE);
+ }
+
+ @Override
protected void log(String s) {
Log.d(LOG_TAG, "[CdmaDataConnectionTracker] " + s);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 4c6cd17..07f9e38 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -139,8 +139,6 @@
private boolean isEriTextLoaded = false;
private boolean isSubscriptionFromRuim = false;
- private boolean mPendingRadioPowerOffAfterDataOff = false;
-
/* Used only for debugging purposes. */
private String mRegistrationDeniedReason;
@@ -485,18 +483,8 @@
}
break;
- case EVENT_SET_RADIO_POWER_OFF:
- synchronized(this) {
- if (mPendingRadioPowerOffAfterDataOff) {
- if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
- hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOff = false;
- }
- }
- break;
-
default:
- Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
+ super.handleMessage(msg);
break;
}
}
@@ -513,35 +501,10 @@
DataConnectionTracker dcTracker = phone.mDataConnection;
// If it's on and available and we want it off gracefully
- powerOffRadioSafely();
+ powerOffRadioSafely(dcTracker);
} // Otherwise, we're in the desired state
}
- // TODO: Consider moving this method to DataConnectionTracker
- @Override
- public void powerOffRadioSafely() {
- DataConnectionTracker dcTracker = phone.mDataConnection;
-
- synchronized (this) {
- if (!mPendingRadioPowerOffAfterDataOff) {
- if (dcTracker.isAnyActiveDataConnections()) {
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
- if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
- mPendingRadioPowerOffAfterDataOff = true;
- } else {
- Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
- hangupAndPowerOff();
- }
- } else {
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- if (DBG) log("Data disconnected, turn off radio right away.");
- hangupAndPowerOff();
- }
- }
- }
- }
-
@Override
protected void updateSpnDisplay() {
// TODO RUIM SPN is not implemented, EF_SPN has to be read and Display Condition
@@ -1658,24 +1621,6 @@
}
/**
- * process the pending request to turn radio off after data is disconnected
- *
- * return true if there is pending request to process; false otherwise.
- */
- public boolean processPendingRadioPowerOffAfterDataOff() {
- synchronized(this) {
- if (mPendingRadioPowerOffAfterDataOff) {
- if (DBG) log("Process pending request to turn radio off.");
- removeMessages(EVENT_SET_RADIO_POWER_OFF);
- hangupAndPowerOff();
- mPendingRadioPowerOffAfterDataOff = false;
- return true;
- }
- return false;
- }
- }
-
- /**
* Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
*/
int getOtasp() {
@@ -1701,7 +1646,8 @@
Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
}
- private void hangupAndPowerOff() {
+ @Override
+ protected void hangupAndPowerOff() {
// hang up all active voice calls
phone.mCT.ringingCall.hangupIfAlive();
phone.mCT.backgroundCall.hangupIfAlive();
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 4d94b27..891a237 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -574,9 +574,11 @@
}
private boolean trySetupData(String reason, String type) {
- if (DBG)
- log("***trySetupData for type:" + type+" due to " + (reason == null ? "(unspecified)" : reason));
- log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
+ if (DBG) {
+ log("***trySetupData for type:" + type +
+ " due to " + (reason == null ? "(unspecified)" : reason) +
+ " isPsRestricted=" + mIsPsRestricted);
+ }
if (type == null) {
type = Phone.APN_TYPE_DEFAULT;
@@ -585,12 +587,8 @@
ApnContext apnContext = mApnContexts.get(type);
if (apnContext == null ){
- if (DBG) log("***new apn context for type:" + type);
+ if (DBG) log("new apn context for type:" + type);
apnContext = new ApnContext(type, LOG_TAG);
- if (apnContext == null) {
- if (DBG) log("***new apn context failed ");
- return false;
- }
mApnContexts.put(type, apnContext);
}
apnContext.setReason(reason);
@@ -839,7 +837,7 @@
GsmDataConnection dc;
int profileId = getApnProfileID(apnContext.getApnType());
- apn = apnContext.getNextApn();
+ apn = apnContext.getNextWaitingApn();
if (apn == null) {
if (DBG) log("setupData: return for no apn found!");
return false;
@@ -1073,7 +1071,7 @@
protected void restartRadio() {
log("************TURN OFF RADIO**************");
cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
- mPhone.getServiceStateTracker().powerOffRadioSafely();
+ mPhone.getServiceStateTracker().powerOffRadioSafely(this);
/* Note: no need to call setRadioPower(true). Assuming the desired
* radio power state is still ON (as tracked by ServiceStateTracker),
* ServiceStateTracker will call setRadioPower when it receives the
@@ -1449,19 +1447,22 @@
}
// Count permanent failures and remove the APN we just tried
- // TODO: Where is mWaitingApnsPermanentFailureCountDown initialized
- if (cause.isPermanentFail())
- apnContext.decPermFailCount();
+ if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
- apnContext.removeNextApn();
- if (DBG) log(String.format("onDataSetupComplete: mWaitingApns.size=%d" +
- " mWaitingApnsPermanenatFailureCountDown=%d",
- apnContext.getWaitingApns().size(), apnContext.getPermFailCount()));
+ apnContext.removeNextWaitingApn();
+ if (DBG) {
+ log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
+ " WaitingApnsPermFailureCountDown=%d",
+ apnContext.getWaitingApns().size(),
+ apnContext.getWaitingApnsPermFailCount()));
+ }
// See if there are more APN's to try
if (apnContext.getWaitingApns().isEmpty()) {
- if (apnContext.getPermFailCount() == 0) {
- if (DBG) log("onDataSetupComplete: Permanent failures stop retrying");
+ if (apnContext.getWaitingApnsPermFailCount() == 0) {
+ if (DBG) {
+ log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
+ }
apnContext.setState(State.FAILED);
notifyDataConnection(Phone.REASON_APN_FAILED);
} else {
@@ -1473,7 +1474,8 @@
apnContext.setState(State.SCANNING);
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel
- sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext), APN_DELAY_MILLIS);
+ sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),
+ APN_DELAY_MILLIS);
}
}
}
@@ -1491,14 +1493,25 @@
}
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+
+ apnContext.setState(State.IDLE);
+ apnContext.setApnSetting(null);
+
+ // if all data connection are gone, check whether Airplane mode request was
+ // pending.
+ if (!isConnected()) {
+ if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
+ // Radio will be turned off. No need to retry data setup
+ return;
+ }
+ }
+
// Check if APN disabled.
if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
mApnContexts.remove(apnContext.getApnType());
return;
}
- apnContext.setState(State.IDLE);
- apnContext.setApnSetting(null);
if (TextUtils.equals(apnContext.getApnType(), Phone.APN_TYPE_DEFAULT)
&& retryAfterDisconnected(apnContext.getReason())) {
SystemProperties.set("gsm.defaultpdpcontext.active", "false");
@@ -1894,6 +1907,11 @@
}
@Override
+ public boolean isAnyActiveDataConnections() {
+ return isConnected();
+ }
+
+ @Override
protected void log(String s) {
Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 3f521a0..ef3eed8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -164,8 +164,6 @@
static final int PS_NOTIFICATION = 888; // Id to update and cancel PS restricted
static final int CS_NOTIFICATION = 999; // Id to update and cancel CS restricted
- static final int MAX_NUM_DATA_STATE_READS = 15;
-
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -459,7 +457,7 @@
break;
default:
- Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
+ super.handleMessage(msg);
break;
}
}
@@ -471,23 +469,13 @@
cm.setRadioPower(true, null);
} else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
// If it's on and available and we want it off gracefully
- powerOffRadioSafely();
+ DataConnectionTracker dcTracker = phone.mDataConnection;
+ powerOffRadioSafely(dcTracker);
} // Otherwise, we're in the desired state
}
@Override
- public void powerOffRadioSafely() {
- // Cleanup all connections
- DataConnectionTracker dcTracker = phone.mDataConnection;
- Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_ALL_CONNECTIONS);
- dcTracker.sendMessage(msg);
-
- // poll data state up to 15 times, with a 100ms delay
- // totaling 1.5 sec. Normal data disable action will finish in 100ms.
- for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
- SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
- }
-
+ protected void hangupAndPowerOff() {
// hang up all active voice calls
if (phone.isInCall()) {
phone.mCT.ringingCall.hangupIfAlive();