diff options
98 files changed, 1904 insertions, 473 deletions
diff --git a/api/current.xml b/api/current.xml index c4fc0e470c56..a36f0665498b 100644 --- a/api/current.xml +++ b/api/current.xml @@ -35340,6 +35340,17 @@ visibility="public" > </field> +<field name="EXTRA_NEW_SEARCH" + type="java.lang.String" + transient="false" + volatile="false" + value=""new_search"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_SELECT_QUERY" type="java.lang.String" transient="false" @@ -156122,6 +156133,17 @@ visibility="public" > </field> +<field name="EXTRA_CREATE_NEW_TAB" + type="java.lang.String" + transient="false" + volatile="false" + value=""create_new_tab"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_HEADERS" type="java.lang.String" transient="false" diff --git a/core/java/android/animation/package.html b/core/java/android/animation/package.html index ff4326092c41..92eeb20c96be 100644 --- a/core/java/android/animation/package.html +++ b/core/java/android/animation/package.html @@ -15,7 +15,7 @@ behaviors.</p> <p> For a guide on how to use the property animation system, see the -<a href="{@docRoot}guide/topics/media/index.html">Animation</a> developer guide. +<a href="{@docRoot}guide/topics/graphics/animation.html">Animation</a> developer guide. </p> </body> </html> diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 671501255e28..aab087fcf54f 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -138,6 +138,12 @@ public class SearchManager public final static String EXTRA_SELECT_QUERY = "select_query"; /** + * Boolean extra data key for {@link Intent#ACTION_WEB_SEARCH} intents. If {@code true}, + * this search should open a new browser window, rather than using an existing one. + */ + public final static String EXTRA_NEW_SEARCH = "new_search"; + + /** * Boolean extra data key for a suggestion provider to return in {@link Cursor#getExtras} to * indicate that the search is not complete yet. This can be used by the search UI * to indicate that a search is in progress. The suggestion provider can return partial results diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1f4fe801e638..e1c904420a38 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1052,6 +1052,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.INPUT_DEVICE) { BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); return true; + } else if (profile == BluetoothProfile.PAN) { + BluetoothPan pan = new BluetoothPan(context, listener); + return true; } else { return false; } diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 1f07349b4c13..9ffed26f4acb 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -27,187 +27,261 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; + /** - * @hide + * This class provides the APIs to control the Bluetooth Pan + * Profile. + * + *<p>BluetoothPan is a proxy object for controlling the Bluetooth + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothPan proxy object. + * + *<p>Each method is protected with its appropriate permission. + *@hide */ -public final class BluetoothPan { +public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = false; - //TODO: This needs to inherit from BluetoothProfile like other profiles. - - /** int extra for ACTION_PAN_STATE_CHANGED */ - public static final String EXTRA_PAN_STATE = "android.bluetooth.pan.extra.STATE"; - - /** int extra for ACTION_PAN_STATE_CHANGED */ - public static final String EXTRA_PREVIOUS_PAN_STATE = - "android.bluetooth.pan.extra.PREVIOUS_STATE"; + /** + * Intent used to broadcast the change in connection state of the Pan + * profile. + * + * <p>This intent will have 4 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is + * bound to. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or + * {@link #LOCAL_PANU_ROLE} + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; - /** int extra for ACTION_PAN_STATE_CHANGED */ + /** + * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent + * The local role of the PAN profile that the remote device is bound to. + * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}. + */ public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + /** + * The local device is acting as a Network Access Point. + */ public static final int LOCAL_NAP_ROLE = 1; - public static final int LOCAL_PANU_ROLE = 2; /** - * Indicates the state of an PAN device has changed. - * This intent will always contain EXTRA_DEVICE_STATE, - * EXTRA_PREVIOUS_DEVICE_STATE, BluetoothDevice.EXTRA_DEVICE - * and EXTRA_LOCAL_ROLE. - * extras. + * The local device is acting as a PAN User. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PAN_STATE_CHANGED = - "android.bluetooth.pan.action.STATE_CHANGED"; - - public static final String NAP_ROLE = "nap"; - public static final String NAP_BRIDGE = "pan1"; - - public static final int MAX_CONNECTIONS = 7; - - public static final int STATE_DISCONNECTED = 0; - public static final int STATE_CONNECTING = 1; - public static final int STATE_CONNECTED = 2; - public static final int STATE_DISCONNECTING = 3; + public static final int LOCAL_PANU_ROLE = 2; /** * Return codes for the connect and disconnect Bluez / Dbus calls. + * @hide */ public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; + /** + * @hide + */ public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001; + /** + * @hide + */ public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002; + /** + * @hide + */ public static final int PAN_OPERATION_GENERIC_FAILURE = 1003; + /** + * @hide + */ public static final int PAN_OPERATION_SUCCESS = 1004; - private final IBluetooth mService; - private final Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetooth mService; /** * Create a BluetoothPan proxy object for interacting with the local - * Bluetooth Pan service. - * @param c Context + * Bluetooth Service which handles the Pan profile + * */ - public BluetoothPan(Context c) { - mContext = c; - + /*package*/ BluetoothPan(Context mContext, ServiceListener l) { IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); if (b != null) { mService = IBluetooth.Stub.asInterface(b); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.PAN, this); + } } else { Log.w(TAG, "Bluetooth Service not available!"); // Instead of throwing an exception which prevents people from going - // into Wireless settings in the emulator. Let it crash later - // when it is actually used. + // into Wireless settings in the emulator. Let it crash later when it is actually used. mService = null; } } /** - * Initiate a PAN connection. - * - * This function returns false on error and true if the connection - * attempt is being made. - * - * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when the - * connection is completed. - * - * @param device Remote BT device. - * @return false on immediate error, true otherwise + * {@inheritDoc} * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - try { - return mService.connectPanDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connectPanDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Initiate disconnect from PAN. - * - * This function return false on error and true if the disconnection - * attempt is being made. - * - * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when - * disconnect is completed. - * - * @param device Remote BT device. - * @return false on immediate error, true otherwise + * {@inheritDoc} * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - try { - return mService.disconnectPanDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnectPanDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Get the state of a PAN Device. - * - * This function returns an int representing the state of the PAN connection - * - * @param device Remote BT device. - * @return The current state of the PAN Device - * @hide + * {@inheritDoc} */ - public int getPanDeviceState(BluetoothDevice device) { - if (DBG) log("getPanDeviceState(" + device + ")"); - try { - return mService.getPanDeviceState(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return STATE_DISCONNECTED; + public List<BluetoothDevice> getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedPanDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); } /** - * Returns a set of all the connected PAN Devices - * - * Does not include devices that are currently connecting or disconnecting - * - * @return List of PAN devices or empty on Error + * {@inheritDoc} + */ + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getPanDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPanDeviceConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * {@inheritDoc} * @hide */ - public List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices"); - try { - return mService.getConnectedPanDevices(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return new ArrayList<BluetoothDevice>(); - } + public boolean setPriority(BluetoothDevice device, int priority) { + // Priorities are not supported for PAN devices - since we don't + // auto connect. + return false; } - private static void log(String msg) { - Log.d(TAG, msg); + /** + * {@inheritDoc} + * @hide + */ + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); + // Priorities are not supported for PAN devices - since we don't + // auto connect. + return BluetoothProfile.PRIORITY_ON; } public void setBluetoothTethering(boolean value) { + if (DBG) log("setBluetoothTethering(" + value + ")"); try { mService.setBluetoothTethering(value); } catch (RemoteException e) { - Log.e(TAG, "", e); + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } } public boolean isTetheringOn() { + if (DBG) log("isTetheringOn()"); try { return mService.isTetheringOn(); } catch (RemoteException e) { - Log.e(TAG, "", e); + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } -} + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +}
\ No newline at end of file diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 1ffee7205395..1ad66f71b938 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -58,10 +58,12 @@ public interface BluetoothProfile { * Headset and Handsfree profile */ public static final int HEADSET = 1; + /** * A2DP profile. */ public static final int A2DP = 2; + /** * Input Device Profile * @hide @@ -69,6 +71,12 @@ public interface BluetoothProfile { public static final int INPUT_DEVICE = 3; /** + * PAN Profile + * @hide + */ + public static final int PAN = 4; + + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile * @hide diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index aa1adcbbc2a8..c08f14f3f81f 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -96,17 +96,32 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void startMonitoring(Context context, Handler target) { mContext = context; mCsHandler = target; - mBluetoothPan = new BluetoothPan(mContext); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); + } } + private BluetoothProfile.ServiceListener mProfileServiceListener = + new BluetoothProfile.ServiceListener() { + public void onServiceConnected(int profile, BluetoothProfile proxy) { + mBluetoothPan = (BluetoothPan) proxy; + } + public void onServiceDisconnected(int profile) { + mBluetoothPan = null; + } + }; + /** * Disable connectivity to a network * TODO: do away with return value after making MobileDataStateTracker async */ public boolean teardown() { mTeardownRequested.set(true); - for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { - mBluetoothPan.disconnect(device); + if (mBluetoothPan != null) { + for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { + mBluetoothPan.disconnect(device); + } } return true; } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 69fb06a0f7b0..d25f5d0cb336 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -92,8 +92,9 @@ interface IBluetooth boolean isTetheringOn(); void setBluetoothTethering(boolean value); - int getPanDeviceState(in BluetoothDevice device); + int getPanDeviceConnectionState(in BluetoothDevice device); List<BluetoothDevice> getConnectedPanDevices(); + List<BluetoothDevice> getPanDevicesMatchingConnectionStates(in int[] states); boolean connectPanDevice(in BluetoothDevice device); boolean disconnectPanDevice(in BluetoothDevice device); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index c2f3ae70b8b2..d8a5b45c9cda 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -508,23 +508,86 @@ public class Camera { * finish processing the data in them. * * <p>The size of the buffer is determined by multiplying the preview - * image width, height, and bytes per pixel. The width and height can be - * read from {@link Camera.Parameters#getPreviewSize()}. Bytes per pixel + * image width, height, and bytes per pixel. The width and height can be + * read from {@link Camera.Parameters#getPreviewSize()}. Bytes per pixel * can be computed from * {@link android.graphics.ImageFormat#getBitsPerPixel(int)} / 8, * using the image format from {@link Camera.Parameters#getPreviewFormat()}. * * <p>This method is only necessary when - * {@link #setPreviewCallbackWithBuffer(PreviewCallback)} is used. When + * {@link #setPreviewCallbackWithBuffer(PreviewCallback)} is used. When * {@link #setPreviewCallback(PreviewCallback)} or * {@link #setOneShotPreviewCallback(PreviewCallback)} are used, buffers - * are automatically allocated. + * are automatically allocated. When a supplied buffer is too small to + * hold the preview frame data, preview callback will return null and + * the buffer will be removed from the buffer queue. * * @param callbackBuffer the buffer to add to the queue. * The size should be width * height * bits_per_pixel / 8. * @see #setPreviewCallbackWithBuffer(PreviewCallback) */ - public native final void addCallbackBuffer(byte[] callbackBuffer); + public final void addCallbackBuffer(byte[] callbackBuffer) + { + _addCallbackBuffer(callbackBuffer, CAMERA_MSG_PREVIEW_FRAME); + } + + /** + * Adds a pre-allocated buffer to the raw image callback buffer queue. + * Applications can add one or more buffers to the queue. When a raw image + * frame arrives and there is still at least one available buffer, the + * buffer will be used to hold the raw image data and removed from the + * queue. Then raw image callback is invoked with the buffer. If a raw + * image frame arrives but there is no buffer left, the frame is + * discarded. Applications should add buffers back when they finish + * processing the data in them by calling this method again in order + * to avoid running out of raw image callback buffers. + * + * <p>The size of the buffer is determined by multiplying the raw image + * width, height, and bytes per pixel. The width and height can be + * read from {@link Camera.Parameters#getPictureSize()}. Bytes per pixel + * can be computed from + * {@link android.graphics.ImageFormat#getBitsPerPixel(int)} / 8, + * using the image format from {@link Camera.Parameters#getPreviewFormat()}. + * + * <p>This method is only necessary when the PictureCallbck for raw image + * is used while calling {@link #takePicture(Camera.ShutterCallback, + * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}. + * + * Please note that by calling this method, the mode for application-managed + * callback buffers is triggered. If this method has never been called, + * null will be returned by the raw image callback since there is + * no image callback buffer available. Furthermore, When a supplied buffer + * is too small to hold the raw image data, raw image callback will return + * null and the buffer will be removed from the buffer queue. + * + * @param callbackBuffer the buffer to add to the raw image callback buffer + * queue. The size should be width * height * (bits per pixel) / 8. An + * null callbackBuffer will be ignored and won't be added to the queue. + * + * @see #takePicture(Camera.ShutterCallback, + * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}. + * + * {@hide} + */ + public final void addRawImageCallbackBuffer(byte[] callbackBuffer) + { + addCallbackBuffer(callbackBuffer, CAMERA_MSG_RAW_IMAGE); + } + + private final void addCallbackBuffer(byte[] callbackBuffer, int msgType) + { + // CAMERA_MSG_VIDEO_FRAME may be allowed in the future. + if (msgType != CAMERA_MSG_PREVIEW_FRAME && + msgType != CAMERA_MSG_RAW_IMAGE) { + throw new IllegalArgumentException( + "Unsupported message type: " + msgType); + } + + _addCallbackBuffer(callbackBuffer, msgType); + } + + private native final void _addCallbackBuffer( + byte[] callbackBuffer, int msgType); private class EventHandler extends Handler { @@ -735,7 +798,7 @@ public class Camera { PictureCallback jpeg) { takePicture(shutter, raw, null, jpeg); } - private native final void native_takePicture(); + private native final void native_takePicture(int msgType); /** * Triggers an asynchronous image capture. The camera service will initiate @@ -743,7 +806,8 @@ public class Camera { * The shutter callback occurs after the image is captured. This can be used * to trigger a sound to let the user know that image has been captured. The * raw callback occurs when the raw image data is available (NOTE: the data - * may be null if the hardware does not have enough memory to make a copy). + * will be null if there is no raw image callback buffer available or the + * raw image callback buffer is not large enough to hold the raw image). * The postview callback occurs when a scaled, fully processed postview * image is available (NOTE: not all hardware supports this). The jpeg * callback occurs when the compressed image is available. If the @@ -762,6 +826,8 @@ public class Camera { * @param raw the callback for raw (uncompressed) image data, or null * @param postview callback with postview image data, may be null * @param jpeg the callback for JPEG image data, or null + * + * @see #addRawImageCallbackBuffer(byte[]) */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback postview, PictureCallback jpeg) { @@ -769,7 +835,23 @@ public class Camera { mRawImageCallback = raw; mPostviewCallback = postview; mJpegCallback = jpeg; - native_takePicture(); + + // If callback is not set, do not send me callbacks. + int msgType = 0; + if (mShutterCallback != null) { + msgType |= CAMERA_MSG_SHUTTER; + } + if (mRawImageCallback != null) { + msgType |= CAMERA_MSG_RAW_IMAGE; + } + if (mPostviewCallback != null) { + msgType |= CAMERA_MSG_POSTVIEW_FRAME; + } + if (mJpegCallback != null) { + msgType |= CAMERA_MSG_COMPRESSED_IMAGE; + } + + native_takePicture(msgType); } /** diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 8c55bf33788b..78d7991796b9 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -122,13 +122,13 @@ public class SensorEvent { * * final float alpha = 0.8; * - * gravity[0] = alpha * gravity[0] + (1 - alpha) * event.data[0]; - * gravity[1] = alpha * gravity[1] + (1 - alpha) * event.data[1]; - * gravity[2] = alpha * gravity[2] + (1 - alpha) * event.data[2]; + * gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]; + * gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]; + * gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]; * - * linear_acceleration[0] = event.data[0] - gravity[0]; - * linear_acceleration[1] = event.data[1] - gravity[1]; - * linear_acceleration[2] = event.data[2] - gravity[2]; + * linear_acceleration[0] = event.values[0] - gravity[0]; + * linear_acceleration[1] = event.values[1] - gravity[1]; + * linear_acceleration[2] = event.values[2] - gravity[2]; * } * </pre> * @@ -186,9 +186,9 @@ public class SensorEvent { * { * if (timestamp != 0) { * final float dT = (event.timestamp - timestamp) * NS2S; - * angle[0] += event.data[0] * dT; - * angle[1] += event.data[1] * dT; - * angle[2] += event.data[2] * dT; + * angle[0] += event.values[0] * dT; + * angle[1] += event.values[1] * dT; + * angle[2] += event.values[2] * dT; * } * timestamp = event.timestamp; * } diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 3bfd005d2058..57ee440755cd 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -139,8 +139,6 @@ public class Browser { public static final int SEARCHES_PROJECTION_SEARCH_INDEX = 1; public static final int SEARCHES_PROJECTION_DATE_INDEX = 2; - private static final String SEARCHES_WHERE_CLAUSE = "search = ?"; - /* Set a cap on the count of history items in the history/bookmark table, to prevent db and layout operations from dragging to a crawl. Revisit this cap when/if db/layout performance @@ -167,6 +165,13 @@ public class Browser { } /** + * Passed along with an Intent to a browser, specifying that a new tab + * be created. Overrides EXTRA_APPLICATION_ID; if both are set, a new tab + * will be used, rather than using the same one. + */ + public static final String EXTRA_CREATE_NEW_TAB = "create_new_tab"; + + /** * Stores a Bitmap extra in an {@link Intent} representing the screenshot of * a page to share. When receiving an {@link Intent#ACTION_SEND} from the * Browser, use this to access the screenshot. diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b05b078a27d0..4f88612f0e39 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -862,8 +862,9 @@ public final class ContactsContract { } /** - * Types of data used to produce the display name for a contact. Listed in the order - * of increasing priority. + * Types of data used to produce the display name for a contact. In the order + * of increasing priority: {@link #EMAIL}, {@link #PHONE}, + * {@link #ORGANIZATION}, {@link #NICKNAME}, {@link #STRUCTURED_NAME}. */ public interface DisplayNameSources { public static final int UNDEFINED = 0; diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index c0b3cc8fb523..578580a180b1 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -785,7 +785,7 @@ class BluetoothEventLoop { boolean connected = false; BluetoothDevice device = mAdapter.getRemoteDevice(address); - int state = mBluetoothService.getPanDeviceState(device); + int state = mBluetoothService.getPanDeviceConnectionState(device); if (state == BluetoothPan.STATE_CONNECTING) { if (result == BluetoothPan.PAN_CONNECT_FAILED_ALREADY_CONNECTED) { connected = true; diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java index fb964396e4fc..3f24811e2805 100644 --- a/core/java/android/server/BluetoothPanProfileHandler.java +++ b/core/java/android/server/BluetoothPanProfileHandler.java @@ -58,6 +58,9 @@ final class BluetoothPanProfileHandler { private Context mContext; private BluetoothService mBluetoothService; + static final String NAP_ROLE = "nap"; + static final String NAP_BRIDGE = "pan1"; + private BluetoothPanProfileHandler(Context context, BluetoothService service) { mContext = context; mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); @@ -114,7 +117,7 @@ final class BluetoothPanProfileHandler { } } - synchronized int getPanDeviceState(BluetoothDevice device) { + synchronized int getPanDeviceConnectionState(BluetoothDevice device) { BluetoothPanDevice panDevice = mPanDevices.get(device); if (panDevice == null) { return BluetoothPan.STATE_DISCONNECTED; @@ -125,13 +128,13 @@ final class BluetoothPanProfileHandler { synchronized boolean connectPanDevice(BluetoothDevice device) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")"); - if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) { + if (getPanDeviceConnectionState(device) != BluetoothPan.STATE_DISCONNECTED) { errorLog(device + " already connected to PAN"); } int connectedCount = 0; for (BluetoothDevice panDevice: mPanDevices.keySet()) { - if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) { + if (getPanDeviceConnectionState(panDevice) == BluetoothPan.STATE_CONNECTED) { connectedCount ++; } } @@ -187,18 +190,33 @@ final class BluetoothPanProfileHandler { List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); for (BluetoothDevice device: mPanDevices.keySet()) { - if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { + if (getPanDeviceConnectionState(device) == BluetoothPan.STATE_CONNECTED) { devices.add(device); } } return devices; } + synchronized List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) { + List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); + + for (BluetoothDevice device: mPanDevices.keySet()) { + int panDeviceState = getPanDeviceConnectionState(device); + for (int state : states) { + if (state == panDeviceState) { + devices.add(device); + break; + } + } + } + return devices; + } + synchronized boolean disconnectPanDevice(BluetoothDevice device) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); debugLog("disconnect PAN(" + objectPath + ")"); - int state = getPanDeviceState(device); + int state = getPanDeviceConnectionState(device); if (state != BluetoothPan.STATE_CONNECTED) { debugLog(device + " already disconnected from PAN"); return false; @@ -274,14 +292,10 @@ final class BluetoothPanProfileHandler { panDevice.mLocalRole = role; } - if (state == BluetoothPan.STATE_DISCONNECTED) { - mPanDevices.remove(device); - } - - Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED); + Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState); - intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state); + intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothPan.EXTRA_STATE, state); intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 70aaf0a009f1..ebe3ef284029 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -85,6 +85,7 @@ public class BluetoothService extends IBluetooth.Stub { private BluetoothEventLoop mEventLoop; private BluetoothHeadset mBluetoothHeadset; private BluetoothInputDevice mInputDevice; + private BluetoothPan mPan; private boolean mIsAirplaneSensitive; private boolean mIsAirplaneToggleable; private int mBluetoothState; @@ -357,7 +358,8 @@ public class BluetoothService extends IBluetooth.Stub { setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF); if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles); - setBluetoothTetheringNative(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE); + setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE, + BluetoothPanProfileHandler.NAP_BRIDGE); // Allow 3 seconds for profiles to gracefully disconnect // TODO: Introduce a callback mechanism so that each profile can notify @@ -606,7 +608,8 @@ public class BluetoothService extends IBluetooth.Stub { addReservedSdpRecords(uuids); // Enable profiles maintained by Bluez userspace. - setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE); + setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE, + BluetoothPanProfileHandler.NAP_BRIDGE); // Add SDP records for profiles maintained by Bluez userspace uuids.add(BluetoothUuid.AudioSource); @@ -2082,6 +2085,8 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); mAdapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE); + mAdapter.getProfileProxy(mContext, + mBluetoothProfileServiceListener, BluetoothProfile.PAN); pw.println("\n--Known devices--"); for (String address : mDeviceProperties.keySet()) { @@ -2125,6 +2130,7 @@ public class BluetoothService extends IBluetooth.Stub { dumpHeadsetProfile(pw); dumpInputDeviceProfile(pw); + dumpPanProfile(pw); pw.println("\n--Application Service Records--"); for (Integer handle : mServiceRecordToPid.keySet()) { @@ -2138,10 +2144,10 @@ public class BluetoothService extends IBluetooth.Stub { if (mBluetoothHeadset != null) { List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); if (deviceList.size() == 0) { - pw.println("\n--No headsets connected--"); + pw.println("No headsets connected"); } else { BluetoothDevice device = deviceList.get(0); - pw.println("\ngetConnectedDevices[0] = " + device); + pw.println("getConnectedDevices[0] = " + device); switch (mBluetoothHeadset.getConnectionState(device)) { case BluetoothHeadset.STATE_CONNECTING: @@ -2164,7 +2170,7 @@ public class BluetoothService extends IBluetooth.Stub { deviceList.clear(); deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] { BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); - pw.println("\n--Connected and Disconnected Headsets"); + pw.println("--Connected and Disconnected Headsets"); for (BluetoothDevice device: deviceList) { pw.println(device); if (mBluetoothHeadset.isAudioConnected(device)) { @@ -2180,9 +2186,9 @@ public class BluetoothService extends IBluetooth.Stub { if (mInputDevice != null) { List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices(); if (deviceList.size() == 0) { - pw.println("\nNo input devices connected--"); + pw.println("No input devices connected"); } else { - pw.println("\nNumber of connected devices:" + deviceList.size()); + pw.println("Number of connected devices:" + deviceList.size()); BluetoothDevice device = deviceList.get(0); pw.println("getConnectedDevices[0] = " + device); pw.println("Priority of Connected device = " + mInputDevice.getPriority(device)); @@ -2210,6 +2216,41 @@ public class BluetoothService extends IBluetooth.Stub { mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset); } + private void dumpPanProfile(PrintWriter pw) { + pw.println("\n--Bluetooth Service- Pan Profile"); + if (mPan != null) { + List<BluetoothDevice> deviceList = mPan.getConnectedDevices(); + if (deviceList.size() == 0) { + pw.println("No Pan devices connected"); + } else { + pw.println("Number of connected devices:" + deviceList.size()); + BluetoothDevice device = deviceList.get(0); + pw.println("getConnectedDevices[0] = " + device); + pw.println("Priority of Connected device = " + mPan.getPriority(device)); + + switch (mPan.getConnectionState(device)) { + case BluetoothInputDevice.STATE_CONNECTING: + pw.println("getConnectionState() = STATE_CONNECTING"); + break; + case BluetoothInputDevice.STATE_CONNECTED: + pw.println("getConnectionState() = STATE_CONNECTED"); + break; + case BluetoothInputDevice.STATE_DISCONNECTING: + pw.println("getConnectionState() = STATE_DISCONNECTING"); + break; + } + } + deviceList.clear(); + deviceList = mPan.getDevicesMatchingConnectionStates(new int[] { + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); + pw.println("--Connected and Disconnected Pan devices"); + for (BluetoothDevice device: deviceList) { + pw.println(device); + } + } + mAdapter.closeProfileProxy(BluetoothProfile.PAN, mBluetoothHeadset); + } + private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { @@ -2217,6 +2258,8 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothHeadset = (BluetoothHeadset) proxy; } else if (profile == BluetoothProfile.INPUT_DEVICE) { mInputDevice = (BluetoothInputDevice) proxy; + } else if (profile == BluetoothProfile.PAN) { + mPan = (BluetoothPan) proxy; } } public void onServiceDisconnected(int profile) { @@ -2224,6 +2267,8 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothHeadset = null; } else if (profile == BluetoothProfile.INPUT_DEVICE) { mInputDevice = null; + } else if (profile == BluetoothProfile.PAN) { + mPan = null; } } }; @@ -2302,9 +2347,9 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothPanProfileHandler.setBluetoothTethering(value); } - public synchronized int getPanDeviceState(BluetoothDevice device) { + public synchronized int getPanDeviceConnectionState(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothPanProfileHandler.getPanDeviceState(device); + return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device); } public synchronized boolean connectPanDevice(BluetoothDevice device) { @@ -2318,6 +2363,12 @@ public class BluetoothService extends IBluetooth.Stub { return mBluetoothPanProfileHandler.getConnectedPanDevices(); } + public synchronized List<BluetoothDevice> getPanDevicesMatchingConnectionStates( + int[] states) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states); + } + public synchronized boolean disconnectPanDevice(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java index ea09fc0c0ed0..104deb1c82f0 100644 --- a/core/java/android/webkit/SelectActionModeCallback.java +++ b/core/java/android/webkit/SelectActionModeCallback.java @@ -83,6 +83,7 @@ class SelectActionModeCallback implements ActionMode.Callback { case com.android.internal.R.id.websearch: mode.finish(); Intent i = new Intent(Intent.ACTION_WEB_SEARCH); + i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); i.putExtra(SearchManager.QUERY, mWebView.getSelection()); mWebView.getContext().startActivity(i); break; diff --git a/core/java/android/widget/ListAdapter.java b/core/java/android/widget/ListAdapter.java index 0fd2e7090b46..d8fd1c987bff 100644 --- a/core/java/android/widget/ListAdapter.java +++ b/core/java/android/widget/ListAdapter.java @@ -26,10 +26,14 @@ package android.widget; public interface ListAdapter extends Adapter { /** - * Are all items in this ListAdapter enabled? - * If yes it means all items are selectable and clickable. + * Indicates whether all the items in this adapter are enabled. If the + * value returned by this method changes over time, there is no guarantee + * it will take effect. If true, it means all items are selectable and + * clickable (there is no separator.) * - * @return True if all items are enabled + * @return True if all items are enabled, false otherwise. + * + * @see #isEnabled(int) */ public boolean areAllItemsEnabled(); @@ -41,7 +45,10 @@ public interface ListAdapter extends Adapter { * should be thrown in that case for fast failure. * * @param position Index of the item + * * @return True if the item is not a separator + * + * @see #areAllItemsEnabled() */ boolean isEnabled(int position); } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 9f70509f43c0..bfbfd374bbe7 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -53,25 +53,48 @@ public: virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr); virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); - void addCallbackBuffer(JNIEnv *env, jbyteArray cbb); + void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType); void setCallbackMode(JNIEnv *env, bool installed, bool manualMode); sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; } + bool isRawImageCallbackBufferAvailable() const; void release(); private: void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType); + void clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers); void clearCallbackBuffers_l(JNIEnv *env); + jbyteArray getCallbackBuffer(JNIEnv *env, Vector<jbyteArray> *buffers, size_t bufferSize); jobject mCameraJObjectWeak; // weak reference to java object jclass mCameraJClass; // strong reference to java class sp<Camera> mCamera; // strong reference to native object Mutex mLock; + /* + * Global reference application-managed raw image buffer queue. + * + * Manual-only mode is supported for raw image callbacks, which is + * set whenever method addCallbackBuffer() with msgType = + * CAMERA_MSG_RAW_IMAGE is called; otherwise, null is returned + * with raw image callbacks. + */ + Vector<jbyteArray> mRawImageCallbackBuffers; + + /* + * Application-managed preview buffer queue and the flags + * associated with the usage of the preview buffer callback. + */ Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[] bool mManualBufferMode; // Whether to use application managed buffers. - bool mManualCameraCallbackSet; // Whether the callback has been set, used to reduce unnecessary calls to set the callback. + bool mManualCameraCallbackSet; // Whether the callback has been set, used to + // reduce unnecessary calls to set the callback. }; +bool JNICameraContext::isRawImageCallbackBufferAvailable() const +{ + return !mRawImageCallbackBuffers.isEmpty(); +} + sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext) { sp<Camera> camera; @@ -128,10 +151,48 @@ void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2) return; } JNIEnv *env = AndroidRuntime::getJNIEnv(); + + /* + * If the notification or msgType is CAMERA_MSG_RAW_IMAGE_NOTIFY, change it + * to CAMERA_MSG_RAW_IMAGE since CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed + * to the Java app. + */ + if (msgType == CAMERA_MSG_RAW_IMAGE_NOTIFY) { + msgType = CAMERA_MSG_RAW_IMAGE; + } + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, ext1, ext2, NULL); } +jbyteArray JNICameraContext::getCallbackBuffer( + JNIEnv* env, Vector<jbyteArray>* buffers, size_t bufferSize) +{ + jbyteArray obj = NULL; + + // Vector access should be protected by lock in postData() + if (!buffers->isEmpty()) { + LOGV("Using callback buffer from queue of length %d", buffers->size()); + jbyteArray globalBuffer = buffers->itemAt(0); + buffers->removeAt(0); + + obj = (jbyteArray)env->NewLocalRef(globalBuffer); + env->DeleteGlobalRef(globalBuffer); + + if (obj != NULL) { + jsize bufferLength = env->GetArrayLength(obj); + if ((int)bufferLength < (int)bufferSize) { + LOGE("Callback buffer was too small! Expected %d bytes, but got %d bytes!", + bufferSize, bufferLength); + env->DeleteLocalRef(obj); + return NULL; + } + } + } + + return obj; +} + void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) { jbyteArray obj = NULL; @@ -141,7 +202,7 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int ssize_t offset; size_t size; sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); - LOGV("postData: off=%d, size=%d", offset, size); + LOGV("copyAndPost: off=%ld, size=%d", offset, size); uint8_t *heapBase = (uint8_t*)heap->base(); if (heapBase != NULL) { @@ -151,32 +212,28 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int LOGV("Allocating callback buffer"); obj = env->NewByteArray(size); } else { - // Vector access should be protected by lock in postData() - if(!mCallbackBuffers.isEmpty()) { - LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size()); - jbyteArray globalBuffer = mCallbackBuffers.itemAt(0); - mCallbackBuffers.removeAt(0); - - obj = (jbyteArray)env->NewLocalRef(globalBuffer); - env->DeleteGlobalRef(globalBuffer); - - if (obj != NULL) { - jsize bufferLength = env->GetArrayLength(obj); - if ((int)bufferLength < (int)size) { - LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!", - size, bufferLength); - env->DeleteLocalRef(obj); - return; + switch (msgType) { + case CAMERA_MSG_PREVIEW_FRAME: { + obj = getCallbackBuffer(env, &mCallbackBuffers, size); + + if (mCallbackBuffers.isEmpty()) { + LOGV("Out of buffers, clearing callback!"); + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); + mManualCameraCallbackSet = false; + + if (obj == NULL) { + return; + } } + break; } - } - - if(mCallbackBuffers.isEmpty()) { - LOGV("Out of buffers, clearing callback!"); - mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); - mManualCameraCallbackSet = false; - - if (obj == NULL) { + case CAMERA_MSG_RAW_IMAGE: { + obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size); + break; + } + default: { + jniThrowException(env, + "java/lang/RuntimeException", "Unsupported message type"); return; } } @@ -212,21 +269,27 @@ void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr) } // return data based on callback type - switch(msgType) { - case CAMERA_MSG_VIDEO_FRAME: - // should never happen - break; - // don't return raw data to Java - case CAMERA_MSG_RAW_IMAGE: - LOGV("rawCallback"); - env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, msgType, 0, 0, NULL); - break; - default: - // TODO: Change to LOGV - LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); - copyAndPost(env, dataPtr, msgType); - break; + switch (msgType) { + case CAMERA_MSG_VIDEO_FRAME: + // should never happen + break; + + // For backward-compatibility purpose, if there is no callback + // buffer for raw image, the callback returns null. + case CAMERA_MSG_RAW_IMAGE: + LOGV("rawCallback"); + if (mRawImageCallbackBuffers.isEmpty()) { + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, 0, 0, NULL); + } else { + copyAndPost(env, dataPtr, msgType); + } + break; + + default: + LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); + copyAndPost(env, dataPtr, msgType); + break; } } @@ -251,7 +314,7 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM if (!installed) { mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); - clearCallbackBuffers_l(env); + clearCallbackBuffers_l(env, &mCallbackBuffers); } else if (mManualBufferMode) { if (!mCallbackBuffers.isEmpty()) { mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA); @@ -259,24 +322,44 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM } } else { mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER); - clearCallbackBuffers_l(env); + clearCallbackBuffers_l(env, &mCallbackBuffers); } } -void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb) +void JNICameraContext::addCallbackBuffer( + JNIEnv *env, jbyteArray cbb, int msgType) { + LOGV("addCallbackBuffer: 0x%x", msgType); if (cbb != NULL) { Mutex::Autolock _l(mLock); - jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); - mCallbackBuffers.push(cbb); - - LOGV("Adding callback buffer to queue, %d total", mCallbackBuffers.size()); - - // We want to make sure the camera knows we're ready for the next frame. - // This may have come unset had we not had a callbackbuffer ready for it last time. - if (mManualBufferMode && !mManualCameraCallbackSet) { - mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA); - mManualCameraCallbackSet = true; + switch (msgType) { + case CAMERA_MSG_PREVIEW_FRAME: { + jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); + mCallbackBuffers.push(callbackBuffer); + + LOGV("Adding callback buffer to queue, %d total", + mCallbackBuffers.size()); + + // We want to make sure the camera knows we're ready for the + // next frame. This may have come unset had we not had a + // callbackbuffer ready for it last time. + if (mManualBufferMode && !mManualCameraCallbackSet) { + mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA); + mManualCameraCallbackSet = true; + } + break; + } + case CAMERA_MSG_RAW_IMAGE: { + jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); + mRawImageCallbackBuffers.push(callbackBuffer); + break; + } + default: { + jniThrowException(env, + "java/lang/IllegalArgumentException", + "Unsupported message type"); + return; + } } } else { LOGE("Null byte array!"); @@ -285,10 +368,15 @@ void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb) void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env) { - LOGV("Clearing callback buffers, %d remained", mCallbackBuffers.size()); - while(!mCallbackBuffers.isEmpty()) { - env->DeleteGlobalRef(mCallbackBuffers.top()); - mCallbackBuffers.pop(); + clearCallbackBuffers_l(env, &mCallbackBuffers); + clearCallbackBuffers_l(env, &mRawImageCallbackBuffers); +} + +void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers) { + LOGV("Clearing callback buffers, %d remained", buffers->size()); + while (!buffers->isEmpty()) { + env->DeleteGlobalRef(buffers->top()); + buffers->pop(); } } @@ -458,13 +546,13 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t context->setCallbackMode(env, installed, manualBuffer); } -static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) { - LOGV("addCallbackBuffer"); +static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, int msgType) { + LOGV("addCallbackBuffer: 0x%x", msgType); JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context)); if (context != NULL) { - context->addCallbackBuffer(env, bytes); + context->addCallbackBuffer(env, bytes, msgType); } } @@ -492,14 +580,32 @@ static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz) } } -static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) +static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, int msgType) { LOGV("takePicture"); JNICameraContext* context; sp<Camera> camera = get_native_camera(env, thiz, &context); if (camera == 0) return; - if (camera->takePicture() != NO_ERROR) { + /* + * When CAMERA_MSG_RAW_IMAGE is requested, if the raw image callback + * buffer is available, CAMERA_MSG_RAW_IMAGE is enabled to get the + * notification _and_ the data; otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY + * is enabled to receive the callback notification but no data. + * + * Note that CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the + * Java application. + */ + if (msgType & CAMERA_MSG_RAW_IMAGE) { + LOGV("Enable raw image callback buffer"); + if (!context->isRawImageCallbackBufferAvailable()) { + LOGV("Enable raw image notification, since no callback buffer exists"); + msgType &= ~CAMERA_MSG_RAW_IMAGE; + msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY; + } + } + + if (camera->takePicture(msgType) != NO_ERROR) { jniThrowException(env, "java/lang/RuntimeException", "takePicture failed"); return; } @@ -638,8 +744,8 @@ static JNINativeMethod camMethods[] = { { "setHasPreviewCallback", "(ZZ)V", (void *)android_hardware_Camera_setHasPreviewCallback }, - { "addCallbackBuffer", - "([B)V", + { "_addCallbackBuffer", + "([BI)V", (void *)android_hardware_Camera_addCallbackBuffer }, { "native_autoFocus", "()V", @@ -648,7 +754,7 @@ static JNINativeMethod camMethods[] = { "()V", (void *)android_hardware_Camera_cancelAutoFocus }, { "native_takePicture", - "()V", + "(I)V", (void *)android_hardware_Camera_takePicture }, { "native_setParameters", "(Ljava/lang/String;)V", diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index fc806a502f15..667ba75ebd46 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -556,6 +556,18 @@ static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, job return doBooleanCommand(cmdstr, "OK"); } +static void android_net_wifi_enableBackgroundScan(JNIEnv* env, jobject clazz, jboolean enable) +{ + //Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml + //and will need an update if the names are changed + if (enable) { + doBooleanCommand("DRIVER BGSCAN-START", "OK"); + } + else { + doBooleanCommand("DRIVER BGSCAN-STOP", "OK"); + } +} + // ---------------------------------------------------------------------------- /* @@ -623,6 +635,7 @@ static JNINativeMethod gWifiMethods[] = { (void*) android_net_wifi_setSuspendOptimizationsCommand}, { "setCountryCodeCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_setCountryCodeCommand}, + { "enableBackgroundScan", "(Z)V", (void*) android_net_wifi_enableBackgroundScan}, }; int register_android_net_wifi_WifiManager(JNIEnv* env) diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d9eccd6f2729..b48adf136286 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3176,7 +3176,10 @@ <attr name="visible" format="boolean" /> </declare-styleable> + <!-- Drawable used to render several states. Each state is represented by + a child drawable. --> <declare-styleable name="StateListDrawable"> + <!-- Indicates whether the drawable should be initially visible. --> <attr name="visible" /> <!-- If true, allows the drawable's padding to change based on the current state that is selected. If false, the padding will @@ -3200,6 +3203,7 @@ <attr name="exitFadeDuration" format="integer" /> </declare-styleable> + <!-- Drawable used to render several animated frames. --> <declare-styleable name="AnimationDrawable"> <attr name="visible" /> <attr name="variablePadding" /> @@ -3209,6 +3213,7 @@ <attr name="oneshot" format="boolean" /> </declare-styleable> + <!-- Represents a single frame inside an AnimationDrawable. --> <declare-styleable name="AnimationDrawableItem"> <!-- Amount of time (in milliseconds) to display this frame. --> <attr name="duration" format="integer" /> @@ -3217,12 +3222,21 @@ <attr name="drawable" format="reference" /> </declare-styleable> + <!-- Drawable used to render a geometric shape, with a gradient or a solid color. --> <declare-styleable name="GradientDrawable"> + <!-- Indicates whether the drawable should intially be visible. --> <attr name="visible" /> + <!-- Enables or disables dithering. --> + <attr name="dither" /> + <!-- Indicates what shape to fill with a gradient. --> <attr name="shape"> + <!-- Rectangle shape, with optional rounder corners. --> <enum name="rectangle" value="0" /> + <!-- Oval shape. --> <enum name="oval" value="1" /> + <!-- Line shape. --> <enum name="line" value="2" /> + <!-- Ring shape. --> <enum name="ring" value="3" /> </attr> <!-- Inner radius of the ring expressed as a ratio of the ring's width. For instance, @@ -3237,71 +3251,123 @@ <attr name="innerRadius" format="dimension" /> <!-- Thickness of the ring. When defined, thicknessRatio is ignored. --> <attr name="thickness" format="dimension" /> + <!-- Indicates whether the drawable's level affects the way the gradient is drawn. --> <attr name="useLevel" /> </declare-styleable> + <!-- Used to specify the size of the shape for GradientDrawable. --> <declare-styleable name="GradientDrawableSize"> + <!-- Width of the gradient shape. --> <attr name="width" /> + <!-- Height of the gradient shape. --> <attr name="height" /> </declare-styleable> + <!-- Used to describe the gradient used to fill the shape of a GradientDrawable. --> <declare-styleable name="GradientDrawableGradient"> + <!-- Start color of the gradient. --> <attr name="startColor" format="color" /> - <!-- Optional center color. For linear gradients, use centerX or centerY to place the center color. --> + <!-- Optional center color. For linear gradients, use centerX or centerY + to place the center color. --> <attr name="centerColor" format="color" /> + <!-- End color of the gradient. --> <attr name="endColor" format="color" /> <attr name="useLevel" format="boolean" /> + <!-- Angle of the gradient. --> <attr name="angle" format="float" /> + <!-- Type of gradient. The default type is linear. --> <attr name="type"> + <!-- Linear gradient. --> <enum name="linear" value="0" /> + <!-- Radial, or circular, gradient. --> <enum name="radial" value="1" /> + <!-- Sweep, or angled or diamond, gradient. --> <enum name="sweep" value="2" /> </attr> + <!-- X coordinate of the origin of the gradient within the shape. --> <attr name="centerX" format="float|fraction" /> + <!-- Y coordinate of the origin of the gradient within the shape. --> <attr name="centerY" format="float|fraction" /> + <!-- Radius of the gradient, used only with radial gradient. --> <attr name="gradientRadius" format="float|fraction" /> </declare-styleable> + <!-- Used to fill the shape of GradientDrawable with a solid color. --> <declare-styleable name="GradientDrawableSolid"> + <!-- Solid color for the gradient shape. --> <attr name="color" format="color" /> </declare-styleable> + <!-- Used to describe the optional stroke of a GradientDrawable. --> <declare-styleable name="GradientDrawableStroke"> + <!-- Width of the gradient shape's stroke. --> <attr name="width" /> + <!-- Color of the gradient shape's stroke. --> <attr name="color" /> + <!-- Length of a dash in the stroke. --> <attr name="dashWidth" format="dimension" /> + <!-- Gap between dashes in the stroke. --> <attr name="dashGap" format="dimension" /> </declare-styleable> + <!-- Describes the corners for the rectangle shape of a GradientDrawable. + This can be used to render rounded corners. --> <declare-styleable name="DrawableCorners"> + <!-- Defines the radius of the four corners. --> <attr name="radius" format="dimension" /> + <!-- Radius of the top left corner. --> <attr name="topLeftRadius" format="dimension" /> + <!-- Radius of the top right corner. --> <attr name="topRightRadius" format="dimension" /> + <!-- Radius of the bottom left corner. --> <attr name="bottomLeftRadius" format="dimension" /> + <!-- Radius of the bottom right corner. --> <attr name="bottomRightRadius" format="dimension" /> </declare-styleable> + <!-- Used to specify the optional padding of a GradientDrawable. --> <declare-styleable name="GradientDrawablePadding"> + <!-- Amount of left padding inside the gradient shape. --> <attr name="left" format="dimension" /> + <!-- Amount of top padding inside the gradient shape. --> <attr name="top" format="dimension" /> + <!-- Amount of right padding inside the gradient shape. --> <attr name="right" format="dimension" /> + <!-- Amount of bottom padding inside the gradient shape. --> <attr name="bottom" format="dimension" /> </declare-styleable> + <!-- Drawable used to render several drawables stacked on top of each other. + Each child drawable can be controlled individually. --> <declare-styleable name="LayerDrawable"> + <!-- Indicates the opacity of the layer. This can be useful to allow the + system to enable drawing optimizations. The default value is + translucent. --> <attr name="opacity"> + <!-- Indicates that the layer is opaque and contains no transparent + nor translucent pixels. --> <enum name="opaque" value="-1" /> + <!-- The layer is completely transparent (no pixel will be drawn.) --> <enum name="transparent" value="-2" /> + <!-- The layer has translucent pixels. --> <enum name="translucent" value="-3" /> </attr> </declare-styleable> + <!-- Describes an item (or child) of a LayerDrawable. --> <declare-styleable name="LayerDrawableItem"> + <!-- Left coordinate of the layer. --> <attr name="left" /> + <!-- Top coordinate of the layer. --> <attr name="top" /> + <!-- Right coordinate of the layer. --> <attr name="right" /> + <!-- Bottom coordinate of the layer. --> <attr name="bottom" /> + <!-- Drawable used to render the layer. --> <attr name="drawable" /> + <!-- Identifier of the layer. This can be used to retrieve the layer + from a drawbable container. --> <attr name="id" /> </declare-styleable> @@ -3317,6 +3383,7 @@ <attr name="drawable" /> </declare-styleable> + <!-- Drawable used to rotate another drawable. --> <declare-styleable name="RotateDrawable"> <attr name="visible" /> <attr name="fromDegrees" format="float" /> @@ -3475,6 +3542,8 @@ <attr name="width" /> <!-- Defines the height of the shape. --> <attr name="height" /> + <!-- Enables or disables dithering. --> + <attr name="dither" /> </declare-styleable> <!-- ========================== --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0edd33e35c86..ce37943b492c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -194,6 +194,13 @@ <!-- Boolean indicating whether the wifi chipset has dual frequency band support --> <bool translatable="false" name="config_wifi_dual_band_support">false</bool> + <!-- Boolean indicating whether the wifi chipset supports background scanning mechanism. + This mechanism allows the host to remain in suspend state and the dongle to actively + scan and wake the host when a configured SSID is detected by the dongle. This chipset + capability can provide power savings when wifi needs to be always kept on. + The driver commands needed to support the feature are BGSCAN-START and BGSCAN-STOP --> + <bool translatable="false" name="config_wifi_background_scan_support">false</bool> + <!-- Flag indicating whether the keyguard should be bypassed when the slider is open. This can be set or unset depending how easily the slider can be opened (for example, in a pocket or purse). --> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java index 1374e7f8afef..41104fe4ad72 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java @@ -108,15 +108,6 @@ public class WifiApStress fail("thread in sleep is interrupted"); } assertTrue("no uplink data connection after Wi-Fi tethering", mAct.pingTest(null)); - // Wait for 5 minutes, and verify the data connection again. - // bug id: 3400027 - try { - Thread.sleep(5 * 60 * 1000); - } catch (Exception e) { - fail("thread in sleep is interrupted"); - } - // Verify the uplink data connection - assertTrue("no uplink data connection", mAct.pingTest(null)); // Disable soft AP assertTrue(mAct.mWifiManager.setWifiApEnabled(config, false)); // Wait for 30 seconds until Wi-Fi tethering is stopped diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java index 35210e5c7b3d..85c5eaa85b9b 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -296,8 +298,8 @@ public class BluetoothTestUtils extends Assert { return; } - if (BluetoothPan.ACTION_PAN_STATE_CHANGED.equals(intent.getAction())) { - int state = intent.getIntExtra(BluetoothPan.EXTRA_PAN_STATE, -1); + if (BluetoothPan.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { + int state = intent.getIntExtra(BluetoothPan.EXTRA_STATE, -1); assertNotSame(-1, state); switch (state) { case BluetoothPan.STATE_DISCONNECTED: @@ -331,6 +333,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.INPUT_DEVICE: mInput = (BluetoothInputDevice) proxy; break; + case BluetoothProfile.PAN: + mPan = (BluetoothPan) proxy; + break; } } } @@ -347,6 +352,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.INPUT_DEVICE: mInput = null; break; + case BluetoothProfile.PAN: + mPan = null; + break; } } } @@ -362,6 +370,7 @@ public class BluetoothTestUtils extends Assert { private BluetoothA2dp mA2dp; private BluetoothHeadset mHeadset; private BluetoothInputDevice mInput; + private BluetoothPan mPan; /** * Creates a utility instance for testing Bluetooth. @@ -706,13 +715,13 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. */ public void enablePan(BluetoothAdapter adapter) { - BluetoothPan pan = new BluetoothPan(mContext); - assertNotNull(pan); + if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); long start = System.currentTimeMillis(); - pan.setBluetoothTethering(true); + mPan.setBluetoothTethering(true); long stop = System.currentTimeMillis(); - assertTrue(pan.isTetheringOn()); + assertTrue(mPan.isTetheringOn()); writeOutput(String.format("enablePan() completed in %d ms", (stop - start))); } @@ -724,13 +733,13 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. */ public void disablePan(BluetoothAdapter adapter) { - BluetoothPan pan = new BluetoothPan(mContext); - assertNotNull(pan); + if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); long start = System.currentTimeMillis(); - pan.setBluetoothTethering(false); + mPan.setBluetoothTethering(false); long stop = System.currentTimeMillis(); - assertFalse(pan.isTetheringOn()); + assertFalse(mPan.isTetheringOn()); writeOutput(String.format("disablePan() completed in %d ms", (stop - start))); } @@ -1102,11 +1111,11 @@ public class BluetoothTestUtils extends Assert { fail(String.format("%s device not paired: device=%s", methodName, device)); } - BluetoothPan pan = new BluetoothPan(mContext); - assertNotNull(pan); + if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask); - int state = pan.getPanDeviceState(device); + int state = mPan.getConnectionState(device); switch (state) { case BluetoothPan.STATE_CONNECTED: removeReceiver(receiver); @@ -1119,7 +1128,7 @@ public class BluetoothTestUtils extends Assert { start = System.currentTimeMillis(); if (role == BluetoothPan.LOCAL_PANU_ROLE) { Log.i("BT", "connect to pan"); - assertTrue(pan.connect(device)); + assertTrue(mPan.connect(device)); } break; default: @@ -1130,7 +1139,7 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = pan.getPanDeviceState(device); + state = mPan.getConnectionState(device); if (state == BluetoothPan.STATE_CONNECTED && (receiver.getFiredFlags() & mask) == mask) { long finish = receiver.getCompletedTime(); @@ -1209,23 +1218,23 @@ public class BluetoothTestUtils extends Assert { fail(String.format("%s device not paired: device=%s", methodName, device)); } - BluetoothPan pan = new BluetoothPan(mContext); - assertNotNull(pan); + if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask); - int state = pan.getPanDeviceState(device); + int state = mPan.getConnectionState(device); switch (state) { - case BluetoothInputDevice.STATE_CONNECTED: - case BluetoothInputDevice.STATE_CONNECTING: + case BluetoothPan.STATE_CONNECTED: + case BluetoothPan.STATE_CONNECTING: start = System.currentTimeMillis(); if (role == BluetoothPan.LOCAL_PANU_ROLE) { - assertTrue(pan.disconnect(device)); + assertTrue(mPan.disconnect(device)); } break; - case BluetoothInputDevice.STATE_DISCONNECTED: + case BluetoothPan.STATE_DISCONNECTED: removeReceiver(receiver); return; - case BluetoothInputDevice.STATE_DISCONNECTING: + case BluetoothPan.STATE_DISCONNECTING: mask = 0; // Don't check for received intents since we might have missed them. break; default: @@ -1236,7 +1245,7 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = pan.getPanDeviceState(device); + state = mPan.getConnectionState(device); if (state == BluetoothInputDevice.STATE_DISCONNECTED && (receiver.getFiredFlags() & mask) == mask) { long finish = receiver.getCompletedTime(); @@ -1321,7 +1330,7 @@ public class BluetoothTestUtils extends Assert { private ConnectPanReceiver getConnectPanReceiver(BluetoothDevice device, int role, int expectedFlags) { - String[] actions = {BluetoothPan.ACTION_PAN_STATE_CHANGED}; + String[] actions = {BluetoothPan.ACTION_CONNECTION_STATE_CHANGED}; ConnectPanReceiver receiver = new ConnectPanReceiver(device, role, expectedFlags); addReceiver(receiver, actions); return receiver; @@ -1351,6 +1360,11 @@ public class BluetoothTestUtils extends Assert { sleep(POLL_TIME); } return mInput; + case BluetoothProfile.PAN: + while (mPan == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mPan; default: return null; } diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd index 998c7c36b07a..6c087bb41b7c 100644 --- a/docs/html/sdk/android-3.0.jd +++ b/docs/html/sdk/android-3.0.jd @@ -868,8 +868,8 @@ dimensions.</p> <h3>JSON utilities</h3> <p>New classes, {@link android.util.JsonReader} and {@link android.util.JsonWriter}, help you -read and write JSON streams. The new APIs compliment the {@link org.json} classes which manipulate a -document in memory.</p> +read and write JSON streams. The new APIs complement the {@link org.json} classes, which manipulate +a document in memory.</p> <p>You can create an instance of {@link android.util.JsonReader} by calling its constructor method and passing the {@link java.io.InputStreamReader} that feeds the JSON string. diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index df8869e4b854..7f6f0105ad67 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -2,20 +2,20 @@ page.title=Android SDK sdk.redirect=0 sdk.win_installer=installer_r10-windows.exe -sdk.win_installer_bytes=32845713 -sdk.win_installer_checksum=4e4356c472a6271ac9c062df0219dcb3 +sdk.win_installer_bytes=32878481 +sdk.win_installer_checksum=8ffa2dd734829d0bbd3ea601b50b36c7 sdk.win_download=android-sdk_r10-windows.zip -sdk.win_bytes=30112516 -sdk.win_checksum=643a75d99f5d4ca39dcf743fe894d599 +sdk.win_bytes=32832260 +sdk.win_checksum=1e42b8f528d9ca6d9b887c58c6f1b9a2 sdk.mac_download=android-sdk_r10-mac_x86.zip -sdk.mac_bytes=28224540 -sdk.mac_checksum=4d0a99a458e4f4bde65a01f8545f27e9 +sdk.mac_bytes=28847132 +sdk.mac_checksum=e3aa5578a6553b69cc36659c9505be3f sdk.linux_download=android-sdk_r10-linux_x86.tgz -sdk.linux_bytes=26556013 -sdk.linux_checksum=10cafdd44771bfe2ba9d4440886389e7 +sdk.linux_bytes=26981997 +sdk.linux_checksum=c022dda3a56c8a67698e6a39b0b1a4e0 @jd:body diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index cffee5fd4e9c..8d1756152eca 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -240,7 +240,7 @@ public class BitmapFactory { /** * The resulting height of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, - * outHeight will be set to -1. + * outHeight will be set to -1. */ public int outHeight; diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 65c6ccfa25d7..33f050c40028 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -624,6 +624,8 @@ public class GradientDrawable extends Drawable { int shapeType = a.getInt( com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE); + boolean dither = a.getBoolean( + com.android.internal.R.styleable.GradientDrawable_dither, false); if (shapeType == RING) { st.mInnerRadius = a.getDimensionPixelSize( @@ -645,10 +647,11 @@ public class GradientDrawable extends Drawable { a.recycle(); setShape(shapeType); - + setDither(dither); + int type; - final int innerDepth = parser.getDepth()+1; + final int innerDepth = parser.getDepth() + 1; int depth; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && ((depth=parser.getDepth()) >= innerDepth @@ -811,11 +814,12 @@ public class GradientDrawable extends Drawable { com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius); if (topLeftRadius != radius || topRightRadius != radius || bottomLeftRadius != radius || bottomRightRadius != radius) { + // The corner radii are specified in clockwise order (see Path.addRoundRect()) setCornerRadii(new float[] { topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, - bottomLeftRadius, bottomLeftRadius, - bottomRightRadius, bottomRightRadius + bottomRightRadius, bottomRightRadius, + bottomLeftRadius, bottomLeftRadius }); } a.recycle(); diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index cb8774ddf874..4445b6a8636e 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -286,7 +286,7 @@ public class ShapeDrawable extends Drawable { protected boolean inflateTag(String name, Resources r, XmlPullParser parser, AttributeSet attrs) { - if (name.equals("padding")) { + if ("padding".equals(name)) { TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawablePadding); setPadding( @@ -315,7 +315,10 @@ public class ShapeDrawable extends Drawable { int color = mShapeState.mPaint.getColor(); color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color); mShapeState.mPaint.setColor(color); - + + boolean dither = a.getBoolean(com.android.internal.R.styleable.ShapeDrawable_dither, false); + mShapeState.mPaint.setDither(dither); + setIntrinsicWidth((int) a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f)); setIntrinsicHeight((int) diff --git a/include/camera/Camera.h b/include/camera/Camera.h index e5f7e623c476..f3c8f64ad3de 100644 --- a/include/camera/Camera.h +++ b/include/camera/Camera.h @@ -66,16 +66,17 @@ namespace android { // msgType in notifyCallback and dataCallback functions enum { - CAMERA_MSG_ERROR = 0x001, - CAMERA_MSG_SHUTTER = 0x002, - CAMERA_MSG_FOCUS = 0x004, - CAMERA_MSG_ZOOM = 0x008, - CAMERA_MSG_PREVIEW_FRAME = 0x010, - CAMERA_MSG_VIDEO_FRAME = 0x020, - CAMERA_MSG_POSTVIEW_FRAME = 0x040, - CAMERA_MSG_RAW_IMAGE = 0x080, - CAMERA_MSG_COMPRESSED_IMAGE = 0x100, - CAMERA_MSG_ALL_MSGS = 0x1FF + CAMERA_MSG_ERROR = 0x0001, + CAMERA_MSG_SHUTTER = 0x0002, + CAMERA_MSG_FOCUS = 0x0004, + CAMERA_MSG_ZOOM = 0x0008, + CAMERA_MSG_PREVIEW_FRAME = 0x0010, + CAMERA_MSG_VIDEO_FRAME = 0x0020, + CAMERA_MSG_POSTVIEW_FRAME = 0x0040, + CAMERA_MSG_RAW_IMAGE = 0x0080, + CAMERA_MSG_COMPRESSED_IMAGE = 0x0100, + CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x0200, + CAMERA_MSG_ALL_MSGS = 0xFFFF }; // cmdType in sendCommand functions @@ -207,7 +208,7 @@ public: status_t cancelAutoFocus(); // take a picture - picture returned from callback - status_t takePicture(); + status_t takePicture(int msgType); // set preview/capture parameters - key/value pairs status_t setParameters(const String8& params); diff --git a/include/camera/ICamera.h b/include/camera/ICamera.h index b2310a64c7d7..2344b3f3f423 100644 --- a/include/camera/ICamera.h +++ b/include/camera/ICamera.h @@ -70,7 +70,7 @@ public: virtual status_t startRecording() = 0; // stop recording mode - virtual void stopRecording() = 0; + virtual void stopRecording() = 0; // get recording state virtual bool recordingEnabled() = 0; @@ -84,8 +84,14 @@ public: // cancel auto focus virtual status_t cancelAutoFocus() = 0; - // take a picture - virtual status_t takePicture() = 0; + /* + * take a picture. + * @param msgType the message type an application selectively turn on/off + * on a photo-by-photo basis. The supported message types are: + * CAMERA_MSG_SHUTTER, CAMERA_MSG_RAW_IMAGE, CAMERA_MSG_COMPRESSED_IMAGE, + * and CAMERA_MSG_POSTVIEW_FRAME. Any other message types will be ignored. + */ + virtual status_t takePicture(int msgType) = 0; // set preview/capture parameters - key/value pairs virtual status_t setParameters(const String8& params) = 0; diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index 0bfb166a464d..cce9129c0f80 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -54,6 +54,22 @@ public: virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IOMX> getOMX() = 0; + + // codecs usage tracking for the battery app + enum BatteryDataBits { + // tracking audio codec + kBatteryDataTrackAudio = 1, + // tracking video codec + kBatteryDataTrackVideo = 2, + // codec is started, otherwise codec is paused + kBatteryDataCodecStarted = 4, + // tracking decoder (for media player), + // otherwise tracking encoder (for media recorder) + kBatteryDataTrackDecoder = 8, + }; + + virtual void addBatteryData(uint32_t params) = 0; + virtual status_t pullBatteryData(Parcel* reply) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index c24c0dbcb28f..f8d96cf1e84c 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -18,7 +18,6 @@ #define ANDROID_REF_BASE_H #include <cutils/atomic.h> -#include <utils/TextOutput.h> #include <stdint.h> #include <sys/types.h> @@ -27,6 +26,10 @@ // --------------------------------------------------------------------------- namespace android { +class TextOutput; +TextOutput& printStrongPointer(TextOutput& to, const void* val); +TextOutput& printWeakPointer(TextOutput& to, const void* val); + template<typename T> class wp; // --------------------------------------------------------------------------- @@ -427,8 +430,7 @@ sp<T>::sp(T* p, weakref_type* refs) template <typename T> inline TextOutput& operator<<(TextOutput& to, const sp<T>& val) { - to << "sp<>(" << val.get() << ")"; - return to; + return printStrongPointer(to, val.get()); } // --------------------------------------------------------------------------- @@ -585,8 +587,7 @@ void wp<T>::clear() template <typename T> inline TextOutput& operator<<(TextOutput& to, const wp<T>& val) { - to << "wp<>(" << val.unsafe_get() << ")"; - return to; + return printWeakPointer(to, val.unsafe_get()); } }; // namespace android diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp index 907f119b8174..e2883121e497 100644 --- a/libs/camera/Camera.cpp +++ b/libs/camera/Camera.cpp @@ -301,12 +301,12 @@ status_t Camera::cancelAutoFocus() } // take a picture -status_t Camera::takePicture() +status_t Camera::takePicture(int msgType) { - LOGV("takePicture"); + LOGV("takePicture: 0x%x", msgType); sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - return c->takePicture(); + return c->takePicture(msgType); } // set preview/capture parameters - key/value pairs diff --git a/libs/camera/ICamera.cpp b/libs/camera/ICamera.cpp index 0881d65ae3d2..931b57d048aa 100644 --- a/libs/camera/ICamera.cpp +++ b/libs/camera/ICamera.cpp @@ -223,11 +223,12 @@ public: } // take a picture - returns an IMemory (ref-counted mmap) - status_t takePicture() + status_t takePicture(int msgType) { - LOGV("takePicture"); + LOGV("takePicture: 0x%x", msgType); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(msgType); remote()->transact(TAKE_PICTURE, data, &reply); status_t ret = reply.readInt32(); return ret; @@ -401,7 +402,8 @@ status_t BnCamera::onTransact( case TAKE_PICTURE: { LOGV("TAKE_PICTURE"); CHECK_INTERFACE(ICamera, data, reply); - reply->writeInt32(takePicture()); + int msgType = data.readInt32(); + reply->writeInt32(takePicture(msgType)); return NO_ERROR; } break; case SET_PARAMETERS: { diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0bd1af4ebf18..f934eec80f80 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -23,6 +23,7 @@ #include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/threads.h> +#include <utils/TextOutput.h> #include <stdlib.h> #include <stdio.h> @@ -530,5 +531,20 @@ bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) void RefBase::onLastWeakRef(const void* /*id*/) { } - + +// --------------------------------------------------------------------------- + +TextOutput& printStrongPointer(TextOutput& to, const void* val) +{ + to << "sp<>(" << val << ")"; + return to; +} + +TextOutput& printWeakPointer(TextOutput& to, const void* val) +{ + to << "wp<>(" << val << ")"; + return to; +} + + }; // namespace android diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 98ddbe70ac90..9c92ace2f47e 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1235,6 +1235,14 @@ public class MediaPlayer private native final void native_setup(Object mediaplayer_this); private native final void native_finalize(); + /** + * @param reply Parcel with audio/video duration info for battery + tracking usage + * @return The status code. + * {@hide} + */ + public native static int native_pullBatteryData(Parcel reply); + @Override protected void finalize() { native_finalize(); } diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java index ad2bf954543e..e0df257eca55 100644 --- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java +++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java @@ -3807,7 +3807,6 @@ class MediaArtistNativeHelper { } catch (Throwable e) { // Allocating to new size with Fixed count try { - System.gc(); rgb888 = new int[thumbnailSize * MAX_THUMBNAIL_PERMITTED]; bitmaps = new Bitmap[MAX_THUMBNAIL_PERMITTED]; thumbnailCount = MAX_THUMBNAIL_PERMITTED; diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java index 69088edd32b7..4faa83a02fe3 100755 --- a/media/java/android/media/videoeditor/MediaImageItem.java +++ b/media/java/android/media/videoeditor/MediaImageItem.java @@ -197,7 +197,6 @@ public class MediaImageItem extends MediaItem { fl.close(); } imageBitmap.recycle(); - System.gc(); } /* diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 0884e350d769..ca544328952c 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -36,6 +36,8 @@ #include "android_util_Binder.h" #include <binder/Parcel.h> #include <surfaceflinger/Surface.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> // ---------------------------------------------------------------------------- @@ -723,6 +725,21 @@ static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL ); } +static jint +android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject java_reply) +{ + sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player")); + sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + if (service.get() == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "cannot get MediaPlayerService"); + return UNKNOWN_ERROR; + } + + Parcel *reply = parcelForJavaObject(env, java_reply); + + return service->pullBatteryData(reply); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -758,6 +775,7 @@ static JNINativeMethod gMethods[] = { {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, + {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, }; static const char* const kClassPathName = "android/media/MediaPlayer"; diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 77199e12e719..17a0362862e0 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -37,7 +37,9 @@ enum { DECODE_FD, CREATE_MEDIA_RECORDER, CREATE_METADATA_RETRIEVER, - GET_OMX + GET_OMX, + ADD_BATTERY_DATA, + PULL_BATTERY_DATA }; class BpMediaPlayerService: public BpInterface<IMediaPlayerService> @@ -156,6 +158,19 @@ public: remote()->transact(GET_OMX, data, &reply); return interface_cast<IOMX>(reply.readStrongBinder()); } + + virtual void addBatteryData(uint32_t params) { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(params); + remote()->transact(ADD_BATTERY_DATA, data, &reply); + } + + virtual status_t pullBatteryData(Parcel* reply) { + Parcel data; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + return remote()->transact(PULL_BATTERY_DATA, data, reply); + } }; IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService"); @@ -270,6 +285,17 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(omx->asBinder()); return NO_ERROR; } break; + case ADD_BATTERY_DATA: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + uint32_t params = data.readInt32(); + addBatteryData(params); + return NO_ERROR; + } break; + case PULL_BATTERY_DATA: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pullBatteryData(reply); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 60bdd6233b91..8c6f76b19996 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -23,6 +23,7 @@ #include <sys/types.h> #include <sys/stat.h> +#include <sys/time.h> #include <dirent.h> #include <unistd.h> @@ -51,6 +52,8 @@ #include <media/Metadata.h> #include <media/AudioTrack.h> +#include <private/android_filesystem_config.h> + #include "MediaRecorderClient.h" #include "MediaPlayerService.h" #include "MetadataRetrieverClient.h" @@ -1762,4 +1765,93 @@ int MediaPlayerService::AudioCache::getSessionId() return 0; } +void MediaPlayerService::addBatteryData(uint32_t params) +{ + Mutex::Autolock lock(mLock); + int uid = IPCThreadState::self()->getCallingUid(); + if (uid == AID_MEDIA) { + return; + } + int index = mBatteryData.indexOfKey(uid); + int32_t time = systemTime() / 1000000L; + + if (index < 0) { // create a new entry for this UID + BatteryUsageInfo info; + info.audioTotalTime = 0; + info.videoTotalTime = 0; + info.audioLastTime = 0; + info.videoLastTime = 0; + info.refCount = 0; + + mBatteryData.add(uid, info); + } + + BatteryUsageInfo &info = mBatteryData.editValueFor(uid); + + if (params & kBatteryDataCodecStarted) { + if (params & kBatteryDataTrackAudio) { + info.audioLastTime -= time; + info.refCount ++; + } + if (params & kBatteryDataTrackVideo) { + info.videoLastTime -= time; + info.refCount ++; + } + } else { + if (info.refCount == 0) { + LOGW("Battery track warning: refCount is already 0"); + return; + } else if (info.refCount < 0) { + LOGE("Battery track error: refCount < 0"); + mBatteryData.removeItem(uid); + return; + } + + if (params & kBatteryDataTrackAudio) { + info.audioLastTime += time; + info.refCount --; + } + if (params & kBatteryDataTrackVideo) { + info.videoLastTime += time; + info.refCount --; + } + + // no stream is being played by this UID + if (info.refCount == 0) { + info.audioTotalTime += info.audioLastTime; + info.audioLastTime = 0; + info.videoTotalTime += info.videoLastTime; + info.videoLastTime = 0; + } + } +} + +status_t MediaPlayerService::pullBatteryData(Parcel* reply) { + Mutex::Autolock lock(mLock); + BatteryUsageInfo info; + int size = mBatteryData.size(); + + reply->writeInt32(size); + int i = 0; + + while (i < size) { + info = mBatteryData.valueAt(i); + + reply->writeInt32(mBatteryData.keyAt(i)); //UID + reply->writeInt32(info.audioTotalTime); + reply->writeInt32(info.videoTotalTime); + + info.audioTotalTime = 0; + info.videoTotalTime = 0; + + // remove the UID entry where no stream is being played + if (info.refCount <= 0) { + mBatteryData.removeItemsAt(i); + size --; + i --; + } + i++; + } + return NO_ERROR; +} } // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 62f8ed67bf96..9f41db034028 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -204,7 +204,31 @@ public: void removeClient(wp<Client> client); + // For battery usage tracking purpose + struct BatteryUsageInfo { + // how many streams are being played by one UID + int refCount; + // a temp variable to store the duration(ms) of audio codecs + // when we start a audio codec, we minus the system time from audioLastTime + // when we pause it, we add the system time back to the audioLastTime + // so after the pause, audioLastTime = pause time - start time + // if multiple audio streams are played (or recorded), then audioLastTime + // = the total playing time of all the streams + int32_t audioLastTime; + // when all the audio streams are being paused, we assign audioLastTime to + // this variable, so this value could be provided to the battery app + // in the next pullBatteryData call + int32_t audioTotalTime; + + int32_t videoLastTime; + int32_t videoTotalTime; + }; + KeyedVector<int, BatteryUsageInfo> mBatteryData; + // Collect info of the codec usage from media player and media recorder + virtual void addBatteryData(uint32_t params); + // API for the Battery app to pull the data of codecs usage + virtual status_t pullBatteryData(Parcel* reply); private: class Client : public BnMediaPlayer { diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 87fdbf248c37..e3dfabb02493 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -20,6 +20,10 @@ #include "StagefrightRecorder.h" +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <media/IMediaPlayerService.h> #include <media/stagefright/AudioSource.h> #include <media/stagefright/AMRWriter.h> #include <media/stagefright/CameraSource.h> @@ -46,9 +50,23 @@ namespace android { +// To collect the encoder usage for the battery app +static void addBatteryData(uint32_t params) { + sp<IBinder> binder = + defaultServiceManager()->getService(String16("media.player")); + sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + CHECK(service.get() != NULL); + + service->addBatteryData(params); +} + + StagefrightRecorder::StagefrightRecorder() : mWriter(NULL), mWriterAux(NULL), - mOutputFd(-1), mOutputFdAux(-1) { + mOutputFd(-1), mOutputFdAux(-1), + mAudioSource(AUDIO_SOURCE_LIST_END), + mVideoSource(VIDEO_SOURCE_LIST_END), + mStarted(false) { LOGV("Constructor"); reset(); @@ -745,30 +763,54 @@ status_t StagefrightRecorder::start() { return UNKNOWN_ERROR; } + status_t status = OK; + switch (mOutputFormat) { case OUTPUT_FORMAT_DEFAULT: case OUTPUT_FORMAT_THREE_GPP: case OUTPUT_FORMAT_MPEG_4: - return startMPEG4Recording(); + status = startMPEG4Recording(); + break; case OUTPUT_FORMAT_AMR_NB: case OUTPUT_FORMAT_AMR_WB: - return startAMRRecording(); + status = startAMRRecording(); + break; case OUTPUT_FORMAT_AAC_ADIF: case OUTPUT_FORMAT_AAC_ADTS: - return startAACRecording(); + status = startAACRecording(); + break; case OUTPUT_FORMAT_RTP_AVP: - return startRTPRecording(); + status = startRTPRecording(); + break; case OUTPUT_FORMAT_MPEG2TS: - return startMPEG2TSRecording(); + status = startMPEG2TSRecording(); + break; default: LOGE("Unsupported output file format: %d", mOutputFormat); - return UNKNOWN_ERROR; + status = UNKNOWN_ERROR; + break; + } + + if ((status == OK) && (!mStarted)) { + mStarted = true; + + uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted; + if (mAudioSource != AUDIO_SOURCE_LIST_END) { + params |= IMediaPlayerService::kBatteryDataTrackAudio; + } + if (mVideoSource != VIDEO_SOURCE_LIST_END) { + params |= IMediaPlayerService::kBatteryDataTrackVideo; + } + + addBatteryData(params); } + + return status; } sp<MediaSource> StagefrightRecorder::createAudioSource() { @@ -1458,6 +1500,21 @@ status_t StagefrightRecorder::pause() { mWriterAux->pause(); } + if (mStarted) { + mStarted = false; + + uint32_t params = 0; + if (mAudioSource != AUDIO_SOURCE_LIST_END) { + params |= IMediaPlayerService::kBatteryDataTrackAudio; + } + if (mVideoSource != VIDEO_SOURCE_LIST_END) { + params |= IMediaPlayerService::kBatteryDataTrackVideo; + } + + addBatteryData(params); + } + + return OK; } @@ -1494,6 +1551,21 @@ status_t StagefrightRecorder::stop() { } } + if (mStarted) { + mStarted = false; + + uint32_t params = 0; + if (mAudioSource != AUDIO_SOURCE_LIST_END) { + params |= IMediaPlayerService::kBatteryDataTrackAudio; + } + if (mVideoSource != VIDEO_SOURCE_LIST_END) { + params |= IMediaPlayerService::kBatteryDataTrackVideo; + } + + addBatteryData(params); + } + + return err; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 72225dbdd78d..2c440c1a0dec 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -107,6 +107,8 @@ private: bool mIsMetaDataStoredInVideoBuffers; MediaProfiles *mEncoderProfiles; + bool mStarted; + status_t setupMPEG4Recording( bool useSplitCameraSource, int outputFd, diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b1d3630c67df..1b63ab2461c4 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -28,6 +28,8 @@ #include "include/MPEG2TSExtractor.h" #include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/AudioPlayer.h> @@ -155,8 +157,17 @@ private: const AwesomeNativeWindowRenderer &); }; -//////////////////////////////////////////////////////////////////////////////// +// To collect the decoder usage +void addBatteryData(uint32_t params) { + sp<IBinder> binder = + defaultServiceManager()->getService(String16("media.player")); + sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + CHECK(service.get() != NULL); + + service->addBatteryData(params); +} +//////////////////////////////////////////////////////////////////////////////// AwesomePlayer::AwesomePlayer() : mQueueStarted(false), mTimeSource(NULL), @@ -379,6 +390,17 @@ void AwesomePlayer::reset_l() { mDrmManagerClient = NULL; } + if (mFlags & PLAYING) { + uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder; + if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) { + params |= IMediaPlayerService::kBatteryDataTrackAudio; + } + if (mVideoSource != NULL) { + params |= IMediaPlayerService::kBatteryDataTrackVideo; + } + addBatteryData(params); + } + if (mFlags & PREPARING) { mFlags |= PREPARE_CANCELLED; if (mConnectingDataSource != NULL) { @@ -779,6 +801,16 @@ status_t AwesomePlayer::play_l() { seekTo_l(0); } + uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted + | IMediaPlayerService::kBatteryDataTrackDecoder; + if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) { + params |= IMediaPlayerService::kBatteryDataTrackAudio; + } + if (mVideoSource != NULL) { + params |= IMediaPlayerService::kBatteryDataTrackVideo; + } + addBatteryData(params); + return OK; } @@ -933,6 +965,16 @@ status_t AwesomePlayer::pause_l(bool at_eos) { Playback::PAUSE, 0); } + uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder; + if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) { + params |= IMediaPlayerService::kBatteryDataTrackAudio; + } + if (mVideoSource != NULL) { + params |= IMediaPlayerService::kBatteryDataTrackVideo; + } + + addBatteryData(params); + return OK; } diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index e6fe6186d45b..3689557bbf92 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -277,7 +277,7 @@ void CameraSourceTimeLapse::threadTimeLapseEntry() { // this thread as read() will make a copy of this last frame and keep // returning it in the quick stop mode. Mutex::Autolock autoLock(mQuickStopLock); - CHECK_EQ(OK, mCamera->takePicture()); + CHECK_EQ(OK, mCamera->takePicture(CAMERA_MSG_RAW_IMAGE)); if (mQuickStop) { LOGV("threadTimeLapseEntry: Exiting due to mQuickStop = true"); return; diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h index 589d8370e283..4a1c827453e1 100644 --- a/media/libstagefright/include/AMRExtractor.h +++ b/media/libstagefright/include/AMRExtractor.h @@ -18,6 +18,7 @@ #define AMR_EXTRACTOR_H_ +#include <utils/Errors.h> #include <media/stagefright/MediaExtractor.h> namespace android { diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h index 728980e5c281..ef71b8f21e0f 100644 --- a/media/libstagefright/include/MP3Extractor.h +++ b/media/libstagefright/include/MP3Extractor.h @@ -18,6 +18,7 @@ #define MP3_EXTRACTOR_H_ +#include <utils/Errors.h> #include <media/stagefright/MediaExtractor.h> namespace android { diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h index a41f681e7af2..e97c8cdef486 100644 --- a/media/libstagefright/include/OggExtractor.h +++ b/media/libstagefright/include/OggExtractor.h @@ -18,6 +18,7 @@ #define OGG_EXTRACTOR_H_ +#include <utils/Errors.h> #include <media/stagefright/MediaExtractor.h> namespace android { diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h index 9de197f67839..ce1f33ad9ff7 100644 --- a/media/libstagefright/include/WAVExtractor.h +++ b/media/libstagefright/include/WAVExtractor.h @@ -18,6 +18,7 @@ #define WAV_EXTRACTOR_H_ +#include <utils/Errors.h> #include <media/stagefright/MediaExtractor.h> namespace android { diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index 02187552723a..afff8242a49c 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -19,6 +19,7 @@ #define AVC_UTILS_H_ #include <media/stagefright/foundation/ABuffer.h> +#include <utils/Errors.h> namespace android { diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index d08199533188..153cfe67fe98 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -19,6 +19,7 @@ #define ES_QUEUE_H_ #include <media/stagefright/foundation/ABase.h> +#include <utils/Errors.h> #include <utils/List.h> #include <utils/RefBase.h> diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 3d8ca7a3da13..a09e16b3b412 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -727,17 +727,30 @@ status_t CameraService::Client::cancelAutoFocus() { } // take a picture - image is returned in callback -status_t CameraService::Client::takePicture() { - LOG1("takePicture (pid %d)", getCallingPid()); +status_t CameraService::Client::takePicture(int msgType) { + LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType); Mutex::Autolock lock(mLock); status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - enableMsgType(CAMERA_MSG_SHUTTER | - CAMERA_MSG_POSTVIEW_FRAME | - CAMERA_MSG_RAW_IMAGE | - CAMERA_MSG_COMPRESSED_IMAGE); + if ((msgType & CAMERA_MSG_RAW_IMAGE) && + (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) { + LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY" + " cannot be both enabled"); + return BAD_VALUE; + } + + // We only accept picture related message types + // and ignore other types of messages for takePicture(). + int picMsgType = msgType + & (CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_RAW_IMAGE_NOTIFY | + CAMERA_MSG_COMPRESSED_IMAGE); + + enableMsgType(picMsgType); return mHardware->takePicture(); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index ccb9cf7082cc..1c43b00f7ff1 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -108,7 +108,7 @@ private: virtual void releaseRecordingFrame(const sp<IMemory>& mem); virtual status_t autoFocus(); virtual status_t cancelAutoFocus(); - virtual status_t takePicture(); + virtual status_t takePicture(int msgType); virtual status_t setParameters(const String8& params); virtual String8 getParameters() const; virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index cc25e8d09996..f9b94a309f25 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -96,6 +96,9 @@ public class WifiService extends IWifiManager.Stub { private boolean mDeviceIdle; private int mPluggedType; + /* Chipset supports background scan */ + private final boolean mBackgroundScanSupported; + // true if the user enabled Wifi while in airplane mode private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false); @@ -369,6 +372,9 @@ public class WifiService extends IWifiManager.Stub { Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); mNotificationEnabledSettingObserver.register(); + + mBackgroundScanSupported = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wifi_background_scan_support); } /** @@ -900,6 +906,9 @@ public class WifiService extends IWifiManager.Stub { reportStartWorkSource(); evaluateTrafficStatsPolling(); mWifiStateMachine.enableRssiPolling(true); + if (mBackgroundScanSupported) { + mWifiStateMachine.enableBackgroundScan(false); + } mWifiStateMachine.enableAllNetworks(); updateWifiState(); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { @@ -909,6 +918,9 @@ public class WifiService extends IWifiManager.Stub { mScreenOff = true; evaluateTrafficStatsPolling(); mWifiStateMachine.enableRssiPolling(false); + if (mBackgroundScanSupported) { + mWifiStateMachine.enableBackgroundScan(true); + } /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off * AND the "stay on while plugged in" setting doesn't match the diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 0f7d6392e496..57af001fabc8 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -341,6 +341,45 @@ void Layer::onDraw(const Region& clip) const drawWithOpenGL(clip, tex); } +// As documented in libhardware header, formats in the range +// 0x100 - 0x1FF are specific to the HAL implementation, and +// are known to have no alpha channel +// TODO: move definition for device-specific range into +// hardware.h, instead of using hard-coded values here. +#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) + +bool Layer::needsBlending(const sp<GraphicBuffer>& buffer) const +{ + // If buffers where set with eOpaque flag, all buffers are known to + // be opaque without having to check their actual format + if (mNeedsBlending && buffer != NULL) { + PixelFormat format = buffer->getPixelFormat(); + + if (HARDWARE_IS_DEVICE_FORMAT(format)) { + return false; + } + + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (!err && info.h_alpha <= info.l_alpha) { + return false; + } + } + + // Return opacity as determined from flags and format options + // passed to setBuffers() + return mNeedsBlending; +} + +bool Layer::needsBlending() const +{ + if (mBufferManager.hasActiveBuffer()) { + return needsBlending(mBufferManager.getActiveBuffer()); + } + + return mNeedsBlending; +} + bool Layer::needsFiltering() const { if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { @@ -588,6 +627,9 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // we retired a buffer, which becomes the new front buffer const bool noActiveBuffer = !mBufferManager.hasActiveBuffer(); + const bool activeBlending = + noActiveBuffer ? true : needsBlending(mBufferManager.getActiveBuffer()); + if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); mPostedDirtyRegion.clear(); @@ -602,6 +644,12 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); if (newFrontBuffer != NULL) { + if (!noActiveBuffer && activeBlending != needsBlending(newFrontBuffer)) { + // new buffer has different opacity than previous active buffer, need + // to recompute visible regions accordingly + recomputeVisibleRegions = true; + } + // get the dirty region // compute the posted region const Region dirty(lcblk->getDirtyRegion(buf)); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2b3841466d70..bccc9004fd99 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -75,7 +75,8 @@ public: virtual uint32_t doTransaction(uint32_t transactionFlags); virtual void lockPageFlip(bool& recomputeVisibleRegions); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); - virtual bool needsBlending() const { return mNeedsBlending; } + virtual bool needsBlending(const sp<GraphicBuffer>& buffer) const; + virtual bool needsBlending() const; virtual bool needsDithering() const { return mNeedsDithering; } virtual bool needsFiltering() const; virtual bool isSecure() const { return mSecure; } diff --git a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml index 5211b0a9b6e0..d3c492eacf43 100644 --- a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml +++ b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml @@ -9,5 +9,7 @@ android:layout_width="wrap_content"/> <ImageView android:layout_height="wrap_content" - android:layout_width="wrap_content"/> + android:layout_width="wrap_content" + android:layout_marginLeft="3dip" + android:layout_marginRight="5dip"/> </merge> diff --git a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java index e1934774f672..a50a2bd039f5 100644 --- a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java @@ -63,7 +63,7 @@ public class AvoidXfermode_Delegate extends Xfermode_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(int opColor, int tolerance, int nativeMode) { AvoidXfermode_Delegate newDelegate = new AvoidXfermode_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index c6fde7be289b..9a8cf0462131 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -76,7 +76,7 @@ public class BitmapShader_Delegate extends Shader_Delegate { bitmap.getImage(), Shader_Delegate.getTileMode(shaderTileModeX), Shader_Delegate.getTileMode(shaderTileModeY)); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 0c8776628646..b6d5725ab07b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -245,12 +245,12 @@ public final class Bitmap_Delegate { @LayoutlibDelegate /*package*/ static void nativeDestructor(int nativeBitmap) { - sManager.removeDelegate(nativeBitmap); + sManager.removeJavaReferenceFor(nativeBitmap); } @LayoutlibDelegate /*package*/ static void nativeRecycle(int nativeBitmap) { - sManager.removeDelegate(nativeBitmap); + sManager.removeJavaReferenceFor(nativeBitmap); } @LayoutlibDelegate @@ -522,7 +522,7 @@ public final class Bitmap_Delegate { private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) { // get its native_int - int nativeInt = sManager.addDelegate(delegate); + int nativeInt = sManager.addNewDelegate(delegate); // and create/return a new Bitmap with it return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density); diff --git a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java index 92d0d0af5d3f..4becba130127 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java @@ -57,7 +57,7 @@ public class BlurMaskFilter_Delegate extends MaskFilter_Delegate { @LayoutlibDelegate /*package*/ static int nativeConstructor(float radius, int style) { BlurMaskFilter_Delegate newDelegate = new BlurMaskFilter_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index e8a99b5d6711..f0e727febe96 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -64,7 +64,7 @@ public final class Canvas_Delegate { private Bitmap_Delegate mBitmap; private GcSnapshot mSnapshot; - private int mDrawFilter = 0; + private DrawFilter_Delegate mDrawFilter = null; // ---- Public Helper methods ---- @@ -95,7 +95,7 @@ public final class Canvas_Delegate { * @return the delegate or null. */ public DrawFilter_Delegate getDrawFilter() { - return DrawFilter_Delegate.getDelegate(mDrawFilter); + return mDrawFilter; } // ---- native methods ---- @@ -313,12 +313,12 @@ public final class Canvas_Delegate { // create a new Canvas_Delegate with the given bitmap and return its new native int. Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } else { // create a new Canvas_Delegate and return its new native int. Canvas_Delegate newDelegate = new Canvas_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } } @@ -510,26 +510,18 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeSetDrawFilter(int nativeCanvas, - int nativeFilter) { + /*package*/ static void nativeSetDrawFilter(int nativeCanvas, int nativeFilter) { Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } - canvasDelegate.mDrawFilter = nativeFilter; + canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter); - // get the delegate only because we don't support them at all for the moment, so - // we can display the message now. - - DrawFilter_Delegate filterDelegate = DrawFilter_Delegate.getDelegate(nativeFilter); - if (canvasDelegate == null) { - return; - } - - if (filterDelegate.isSupported() == false) { + if (canvasDelegate.mDrawFilter != null && + canvasDelegate.mDrawFilter.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER, - filterDelegate.getSupportMessage(), null, null /*data*/); + canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/); } } @@ -956,7 +948,7 @@ public final class Canvas_Delegate { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { - // WARNING: the logic in this method is similar to Paint.measureText. + // WARNING: the logic in this method is similar to Paint_Delegate.measureText. // Any change to this method should be reflected in Paint.measureText // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. @@ -1139,7 +1131,7 @@ public final class Canvas_Delegate { canvasDelegate.dispose(); // remove it from the manager. - sManager.removeDelegate(nativeCanvas); + sManager.removeJavaReferenceFor(nativeCanvas); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java index 789c6e6904bf..e786eb587b1d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java @@ -57,7 +57,7 @@ public abstract class ColorFilter_Delegate { @LayoutlibDelegate /*package*/ static void finalizer(int native_instance, int nativeColorFilter) { - sManager.removeDelegate(native_instance); + sManager.removeJavaReferenceFor(native_instance); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java index 462b1e640725..2de344b41f8a 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java @@ -57,7 +57,7 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate { @LayoutlibDelegate /*package*/ static int nativeColorMatrixFilter(float[] array) { ColorMatrixColorFilter_Delegate newDelegate = new ColorMatrixColorFilter_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java index 2bdaa5bdbf5c..7c04a8709db2 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java @@ -64,7 +64,7 @@ public class ComposePathEffect_Delegate extends PathEffect_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(int outerpe, int innerpe) { ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java index a2ecb8f6a1a6..f6e1d0094925 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java @@ -67,7 +67,7 @@ public class ComposeShader_Delegate extends Shader_Delegate { int native_mode) { // FIXME not supported yet. ComposeShader_Delegate newDelegate = new ComposeShader_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -75,7 +75,7 @@ public class ComposeShader_Delegate extends Shader_Delegate { int porterDuffMode) { // FIXME not supported yet. ComposeShader_Delegate newDelegate = new ComposeShader_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java index c677de83ba7f..b0f8168aa3a0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java @@ -64,7 +64,7 @@ public class CornerPathEffect_Delegate extends PathEffect_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(float radius) { CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java index 12a4d4a348d9..d97c2eccd508 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java @@ -75,7 +75,7 @@ public final class DashPathEffect_Delegate extends PathEffect_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(float intervals[], float phase) { DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java index ac6971284583..ec4a810fbda5 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java @@ -64,7 +64,7 @@ public class DiscretePathEffect_Delegate extends PathEffect_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(float length, float deviation) { DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java index a98f0a941090..37c7359b5814 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java @@ -57,7 +57,7 @@ public abstract class DrawFilter_Delegate { @LayoutlibDelegate /*package*/ static void nativeDestructor(int nativeDrawFilter) { - sManager.removeDelegate(nativeDrawFilter); + sManager.removeJavaReferenceFor(nativeDrawFilter); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java index 31f8bbfb1f8e..ebc1c1d2eca4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java @@ -58,7 +58,7 @@ public class EmbossMaskFilter_Delegate extends MaskFilter_Delegate { /*package*/ static int nativeConstructor(float[] direction, float ambient, float specular, float blurRadius) { EmbossMaskFilter_Delegate newDelegate = new EmbossMaskFilter_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java index fcb62a8a9592..51e0576169c4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java @@ -57,7 +57,7 @@ public class LayerRasterizer_Delegate extends Rasterizer_Delegate { @LayoutlibDelegate /*package*/ static int nativeConstructor() { LayerRasterizer_Delegate newDelegate = new LayerRasterizer_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java index b2725343b791..0ee883dcd68c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java @@ -57,7 +57,7 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate { @LayoutlibDelegate /*package*/ static int native_CreateLightingFilter(int mul, int add) { LightingColorFilter_Delegate newDelegate = new LightingColorFilter_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java index 80605779fcf9..a2ba758a7e0c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java @@ -59,7 +59,7 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { int colors[], float positions[], int tileMode) { LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(x0, y0, x1, y1, colors, positions, Shader_Delegate.getTileMode(tileMode)); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java index 4adca276a7d8..5a6167dc6a3d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java @@ -57,7 +57,7 @@ public abstract class MaskFilter_Delegate { @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_filter) { - sManager.removeDelegate(native_filter); + sManager.removeJavaReferenceFor(native_filter); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 68a476f170ef..251aa16ba48b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -189,7 +189,7 @@ public final class Matrix_Delegate { } } - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -765,7 +765,7 @@ public final class Matrix_Delegate { @LayoutlibDelegate /*package*/ static void finalizer(int native_instance) { - sManager.removeDelegate(native_instance); + sManager.removeJavaReferenceFor(native_instance); } // ---- Private helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java index dfcb5916b8cc..71d346a93553 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java @@ -57,7 +57,7 @@ public class PaintFlagsDrawFilter_Delegate extends DrawFilter_Delegate { @LayoutlibDelegate /*package*/ static int nativeConstructor(int clearBits, int setBits) { PaintFlagsDrawFilter_Delegate newDelegate = new PaintFlagsDrawFilter_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index f5d2547799d8..51b3efe12cd7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -32,6 +32,7 @@ import java.awt.Stroke; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -75,19 +76,19 @@ public class Paint_Delegate { private int mCap; private int mJoin; private int mTextAlign; - private int mTypeface; + private Typeface_Delegate mTypeface; private float mStrokeWidth; private float mStrokeMiter; private float mTextSize; private float mTextScaleX; private float mTextSkewX; - private int mXfermode; - private int mColorFilter; - private int mShader; - private int mPathEffect; - private int mMaskFilter; - private int mRasterizer; + private Xfermode_Delegate mXfermode; + private ColorFilter_Delegate mColorFilter; + private Shader_Delegate mShader; + private PathEffect_Delegate mPathEffect; + private MaskFilter_Delegate mMaskFilter; + private Rasterizer_Delegate mRasterizer; // ---- Public Helper methods ---- @@ -172,17 +173,16 @@ public class Paint_Delegate { } public Stroke getJavaStroke() { - PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(mPathEffect); - if (effectDelegate != null) { - if (effectDelegate.isSupported()) { - Stroke stroke = effectDelegate.getStroke(this); + if (mPathEffect != null) { + if (mPathEffect.isSupported()) { + Stroke stroke = mPathEffect.getStroke(this); assert stroke != null; if (stroke != null) { return stroke; } } else { Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT, - effectDelegate.getSupportMessage(), + mPathEffect.getSupportMessage(), null, null /*data*/); } } @@ -201,7 +201,7 @@ public class Paint_Delegate { * @return the delegate or null. */ public Xfermode_Delegate getXfermode() { - return Xfermode_Delegate.getDelegate(mXfermode); + return mXfermode; } /** @@ -210,7 +210,7 @@ public class Paint_Delegate { * @return the delegate or null. */ public ColorFilter_Delegate getColorFilter() { - return ColorFilter_Delegate.getDelegate(mColorFilter); + return mColorFilter; } /** @@ -219,7 +219,7 @@ public class Paint_Delegate { * @return the delegate or null. */ public Shader_Delegate getShader() { - return Shader_Delegate.getDelegate(mShader); + return mShader; } /** @@ -228,7 +228,7 @@ public class Paint_Delegate { * @return the delegate or null. */ public MaskFilter_Delegate getMaskFilter() { - return MaskFilter_Delegate.getDelegate(mMaskFilter); + return mMaskFilter; } /** @@ -237,7 +237,7 @@ public class Paint_Delegate { * @return the delegate or null. */ public Rasterizer_Delegate getRasterizer() { - return Rasterizer_Delegate.getDelegate(mRasterizer); + return mRasterizer; } // ---- native methods ---- @@ -542,9 +542,6 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, int count) { - // WARNING: the logic in this method is similar to Canvas.drawText. - // Any change to this method should be reflected in Canvas.drawText - // get the delegate Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); if (delegate == null) { @@ -567,25 +564,57 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count, float maxWidth, float[] measuredWidth) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.native_breakText is not supported.", null, null /*data*/); - return 0; + + // get the delegate + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + return 0; + } + + int inc = count > 0 ? 1 : -1; + + int measureIndex = 0; + float measureAcc = 0; + for (int i = index; i != index + count; i += inc, measureIndex++) { + int start, end; + if (i < index) { + start = i; + end = index; + } else { + start = index; + end = i; + } + + // measure from start to end + float res = delegate.measureText(text, start, end - start + 1); + + if (measuredWidth != null) { + measuredWidth[measureIndex] = res; + } + + measureAcc += res; + if (res > maxWidth) { + // we should not return this char index, but since it's 0-based + // and we need to return a count, we simply return measureIndex; + return measureIndex; + } + + } + + return measureIndex; } @LayoutlibDelegate /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards, float maxWidth, float[] measuredWidth) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.native_breakText is not supported.", null, null /*data*/); - return 0; + return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth, + measuredWidth); } @LayoutlibDelegate /*package*/ static int native_init() { Paint_Delegate newDelegate = new Paint_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -597,7 +626,7 @@ public class Paint_Delegate { } Paint_Delegate newDelegate = new Paint_Delegate(delegate); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -728,7 +757,9 @@ public class Paint_Delegate { return shader; } - return delegate.mShader = shader; + delegate.mShader = Shader_Delegate.getDelegate(shader); + + return shader; } @LayoutlibDelegate @@ -739,13 +770,12 @@ public class Paint_Delegate { return filter; } - delegate.mColorFilter = filter; + delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);; // since none of those are supported, display a fidelity warning right away - ColorFilter_Delegate filterDelegate = delegate.getColorFilter(); - if (filterDelegate != null && filterDelegate.isSupported() == false) { + if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER, - filterDelegate.getSupportMessage(), null, null /*data*/); + delegate.mColorFilter.getSupportMessage(), null, null /*data*/); } return filter; @@ -759,7 +789,9 @@ public class Paint_Delegate { return xfermode; } - return delegate.mXfermode = xfermode; + delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode); + + return xfermode; } @LayoutlibDelegate @@ -770,7 +802,9 @@ public class Paint_Delegate { return effect; } - return delegate.mPathEffect = effect; + delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect); + + return effect; } @LayoutlibDelegate @@ -781,13 +815,12 @@ public class Paint_Delegate { return maskfilter; } - delegate.mMaskFilter = maskfilter; + delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter); // since none of those are supported, display a fidelity warning right away - MaskFilter_Delegate filterDelegate = delegate.getMaskFilter(); - if (filterDelegate != null && filterDelegate.isSupported() == false) { + if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, - filterDelegate.getSupportMessage(), null, null /*data*/); + delegate.mMaskFilter.getSupportMessage(), null, null /*data*/); } return maskfilter; @@ -801,9 +834,9 @@ public class Paint_Delegate { return 0; } - delegate.mTypeface = typeface; + delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); delegate.updateFontObject(); - return delegate.mTypeface; + return typeface; } @LayoutlibDelegate @@ -814,13 +847,12 @@ public class Paint_Delegate { return rasterizer; } - delegate.mRasterizer = rasterizer; + delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer); // since none of those are supported, display a fidelity warning right away - Rasterizer_Delegate rasterizerDelegate = delegate.getRasterizer(); - if (rasterizerDelegate != null && rasterizerDelegate.isSupported() == false) { + if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER, - rasterizerDelegate.getSupportMessage(), null, null /*data*/); + delegate.mRasterizer.getSupportMessage(), null, null /*data*/); } return rasterizer; @@ -862,19 +894,49 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int native_getTextWidths(int native_object, char[] text, int index, int count, float[] widths) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.getTextWidths is not supported.", null, null /*data*/); + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + return 0; + } + + if (delegate.mFonts.size() > 0) { + // FIXME: handle multi-char characters (see measureText) + float totalAdvance = 0; + for (int i = 0; i < count; i++) { + char c = text[i + index]; + boolean found = false; + for (FontInfo info : delegate.mFonts) { + if (info.mFont.canDisplay(c)) { + float adv = info.mMetrics.charWidth(c); + totalAdvance += adv; + if (widths != null) { + widths[i] = adv; + } + + found = true; + break; + } + } + + if (found == false) { + // no advance for this char. + if (widths != null) { + widths[i] = 0.f; + } + } + } + + return (int) totalAdvance; + } + return 0; } @LayoutlibDelegate /*package*/ static int native_getTextWidths(int native_object, String text, int start, int end, float[] widths) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.getTextWidths is not supported.", null, null /*data*/); - return 0; + return native_getTextWidths(native_object, text.toCharArray(), start, end - start, widths); } @LayoutlibDelegate @@ -971,22 +1033,33 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start, int end, Rect bounds) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.getStringBounds is not supported.", null, null /*data*/); + nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bounds); } @LayoutlibDelegate /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, int count, Rect bounds) { - // FIXME - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.getCharArrayBounds is not supported.", null, null /*data*/); + + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return; + } + + // FIXME should test if the main font can display all those characters. + // See MeasureText + if (delegate.mFonts.size() > 0) { + FontInfo mainInfo = delegate.mFonts.get(0); + + Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, + delegate.mFontContext); + bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight()); + } } @LayoutlibDelegate /*package*/ static void finalizer(int nativePaint) { - sManager.removeDelegate(nativePaint); + sManager.removeJavaReferenceFor(nativePaint); } // ---- Private delegate/helper methods ---- @@ -1028,18 +1101,18 @@ public class Paint_Delegate { mCap = Paint.Cap.BUTT.nativeInt; mJoin = Paint.Join.MITER.nativeInt; mTextAlign = 0; - mTypeface = Typeface.sDefaults[0].native_instance; + mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance); mStrokeWidth = 1.f; mStrokeMiter = 4.f; mTextSize = 20.f; mTextScaleX = 1.f; mTextSkewX = 0.f; - mXfermode = 0; - mColorFilter = 0; - mShader = 0; - mPathEffect = 0; - mMaskFilter = 0; - mRasterizer = 0; + mXfermode = null; + mColorFilter = null; + mShader = null; + mPathEffect = null; + mMaskFilter = null; + mRasterizer = null; updateFontObject(); } @@ -1048,9 +1121,9 @@ public class Paint_Delegate { */ @SuppressWarnings("deprecation") private void updateFontObject() { - if (mTypeface != 0) { + if (mTypeface != null) { // Get the fonts from the TypeFace object. - List<Font> fonts = Typeface_Delegate.getFonts(mTypeface); + List<Font> fonts = mTypeface.getFonts(); // create new font objects as well as FontMetrics, based on the current text size // and skew info. @@ -1073,6 +1146,10 @@ public class Paint_Delegate { } /*package*/ float measureText(char[] text, int index, int count) { + + // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText + // Any change to this method should be reflected there as well + if (mFonts.size() > 0) { FontInfo mainFont = mFonts.get(0); int i = index; @@ -1120,6 +1197,8 @@ public class Paint_Delegate { i += size; } } + + return total; } return 0; diff --git a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java index 98a5386067a7..c448f0ec0f20 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java @@ -65,7 +65,7 @@ public class PathDashPathEffect_Delegate extends PathEffect_Delegate { /*package*/ static int nativeCreate(int native_path, float advance, float phase, int native_style) { PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java index bbbebdd8df2f..4d5311af42ae 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java @@ -61,7 +61,7 @@ public abstract class PathEffect_Delegate { @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_patheffect) { - sManager.removeDelegate(native_patheffect); + sManager.removeJavaReferenceFor(native_patheffect); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 9510ce00e214..c29e9b6a0142 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -90,7 +90,7 @@ public final class Path_Delegate { // create the delegate Path_Delegate newDelegate = new Path_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -104,7 +104,7 @@ public final class Path_Delegate { newDelegate.set(pathDelegate); } - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -440,7 +440,7 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static void finalizer(int nPath) { - sManager.removeDelegate(nPath); + sManager.removeJavaReferenceFor(nPath); } diff --git a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java index bbb20e956163..4ab044bd107a 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java @@ -63,7 +63,7 @@ public class PixelXorXfermode_Delegate extends Xfermode_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(int opColor) { PixelXorXfermode_Delegate newDelegate = new PixelXorXfermode_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java index 33f6c4465527..c45dbaad344d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java @@ -57,7 +57,7 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate { @LayoutlibDelegate /*package*/ static int native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) { PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java index 116a773c9832..4301c1a2c5d7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java @@ -129,7 +129,7 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreateXfermode(int mode) { PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index 8723ed1a6584..9bf78b4c7249 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -58,7 +58,7 @@ public class RadialGradient_Delegate extends Gradient_Delegate { int colors[], float positions[], int tileMode) { RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(x, y, radius, colors, positions, Shader_Delegate.getTileMode(tileMode)); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java index 28262787f9ba..e388bd9fc1b0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java @@ -57,7 +57,7 @@ public abstract class Rasterizer_Delegate { @LayoutlibDelegate /*package*/ static void finalizer(int native_instance) { - sManager.removeDelegate(native_instance); + sManager.removeJavaReferenceFor(native_instance); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java index 7b912154930f..91f4347482ab 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java @@ -266,12 +266,12 @@ public class Region_Delegate { @LayoutlibDelegate /*package*/ static int nativeConstructor() { Region_Delegate newDelegate = new Region_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_region) { - sManager.removeDelegate(native_region); + sManager.removeJavaReferenceFor(native_region); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java index a1b8bdd12fdf..a008d151c6ae 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java @@ -45,7 +45,7 @@ public abstract class Shader_Delegate { // ---- delegate helper data ---- // ---- delegate data ---- - private int mLocalMatrix = 0; + private Matrix_Delegate mLocalMatrix = null; // ---- Public Helper methods ---- @@ -77,7 +77,7 @@ public abstract class Shader_Delegate { @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_shader, int native_skiaShader) { - sManager.removeDelegate(native_shader); + sManager.removeJavaReferenceFor(native_shader); } @LayoutlibDelegate @@ -89,15 +89,14 @@ public abstract class Shader_Delegate { return; } - shaderDelegate.mLocalMatrix = matrix_instance; + shaderDelegate.mLocalMatrix = Matrix_Delegate.getDelegate(matrix_instance); } // ---- Private delegate/helper methods ---- protected java.awt.geom.AffineTransform getLocalMatrix() { - Matrix_Delegate localMatrixDelegate = Matrix_Delegate.getDelegate(mLocalMatrix); - if (localMatrixDelegate != null) { - return localMatrixDelegate.getAffineTransform(); + if (mLocalMatrix != null) { + return mLocalMatrix.getAffineTransform(); } return new java.awt.geom.AffineTransform(); diff --git a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java index 0c9ee48ebd64..410df0cf72f5 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java @@ -64,7 +64,7 @@ public class SumPathEffect_Delegate extends PathEffect_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate(int first, int second) { SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate(); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java index 382e34c41ad4..966e06e4a8b2 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java @@ -54,7 +54,7 @@ public class SweepGradient_Delegate extends Gradient_Delegate { @LayoutlibDelegate /*package*/ static int nativeCreate1(float x, float y, int colors[], float positions[]) { SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(x, y, colors, positions); - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 1992341d1423..5af16aeb5fb3 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -74,6 +74,10 @@ public final class Typeface_Delegate { sPostInitDelegate.clear(); } + public static Typeface_Delegate getDelegate(int nativeTypeface) { + return sManager.getDelegate(nativeTypeface); + } + public static List<Font> getFonts(Typeface typeface) { return getFonts(typeface.native_instance); } @@ -84,7 +88,11 @@ public final class Typeface_Delegate { return null; } - return delegate.mFonts; + return delegate.getFonts(); + } + + public List<Font> getFonts() { + return mFonts; } // ---- native methods ---- @@ -105,7 +113,7 @@ public final class Typeface_Delegate { sPostInitDelegate.add(newDelegate); } - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -125,7 +133,7 @@ public final class Typeface_Delegate { sPostInitDelegate.add(newDelegate); } - return sManager.addDelegate(newDelegate); + return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate @@ -144,7 +152,7 @@ public final class Typeface_Delegate { @LayoutlibDelegate /*package*/ static void nativeUnref(int native_instance) { - sManager.removeDelegate(native_instance); + sManager.removeJavaReferenceFor(native_instance); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java index 88df02739c6e..f3401fcadb65 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java @@ -61,7 +61,7 @@ public abstract class Xfermode_Delegate { @LayoutlibDelegate /*package*/ static void finalizer(int native_instance) { - sManager.removeDelegate(native_instance); + sManager.removeJavaReferenceFor(native_instance); } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java index 04d06e4bb45e..9fab51a61092 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java @@ -30,7 +30,7 @@ import android.widget.TextView; public class PhoneSystemBar extends CustomBar { public PhoneSystemBar(Context context, Density density) throws XmlPullParserException { - super(context, density, "/bars/tablet_system_bar.xml"); + super(context, density, "/bars/phone_system_bar.xml"); setGravity(mGravity | Gravity.RIGHT); setBackgroundColor(0xFF000000); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index 05a258d56c66..295c98c3eafa 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -16,8 +16,14 @@ package com.android.layoutlib.bridge.impl; +import com.android.layoutlib.bridge.util.SparseWeakArray; + import android.util.SparseArray; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + /** * Manages native delegates. * @@ -44,13 +50,32 @@ import android.util.SparseArray; * will do is call {@link #getDelegate(int)} to get the Java object matching the int. * * Typical native init methods are returning a new int back to the Java class, so - * {@link #addDelegate(Object)} does the same. + * {@link #addNewDelegate(Object)} does the same. + * + * The JNI references are counted, so we do the same through a {@link WeakReference}. Because + * the Java object needs to count as a reference (even though it only holds an int), we use the + * following mechanism: + * + * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes + * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming + * the delegate. + * + * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a + * {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically + * when nothing references it. This means that any class that holds a delegate (except for the + * Java main class) must not use the int but the Delegate class instead. The integers must + * only be used in the API between the main Java class and the Delegate. * * @param <T> the delegate class to manage */ public final class DelegateManager<T> { - - private final SparseArray<T> mDelegates = new SparseArray<T>(); + private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>(); + /** list used to store delegates when their main object holds a reference to them. + * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed + * @see #addNewDelegate(Object) + * @see #removeJavaReferenceFor(int) + */ + private final List<T> mJavaReferences = new ArrayList<T>(); private int mDelegateCounter = 0; /** @@ -77,17 +102,20 @@ public final class DelegateManager<T> { * @param newDelegate the delegate to add * @return a unique native int to identify the delegate */ - public int addDelegate(T newDelegate) { + public int addNewDelegate(T newDelegate) { int native_object = ++mDelegateCounter; mDelegates.put(native_object, newDelegate); + assert !mJavaReferences.contains(newDelegate); + mJavaReferences.add(newDelegate); return native_object; } /** - * Removes the delegate matching the given native int. - * @param native_object the native int. + * Removes the main reference on the given delegate. + * @param native_object the native integer representing the delegate. */ - public void removeDelegate(int native_object) { - mDelegates.remove(native_object); + public void removeJavaReferenceFor(int native_object) { + T delegate = getDelegate(native_object); + mJavaReferences.remove(delegate); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java new file mode 100644 index 000000000000..22f1609e6dc6 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java @@ -0,0 +1,368 @@ +/* + * 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. + */ + +package com.android.layoutlib.bridge.util; + + +import com.android.internal.util.ArrayUtils; + +import android.util.SparseArray; + +import java.lang.ref.WeakReference; + +/** + * This is a custom {@link SparseArray} that uses {@link WeakReference} around the objects added + * to it. When the array is compacted, not only deleted indices but also empty references + * are removed, making the array efficient at removing references that were reclaimed. + * + * The code is taken from {@link SparseArray} directly and adapted to use weak references. + * + * Because our usage means that we never actually call {@link #remove(int)} or {@link #delete(int)}, + * we must manually check if there are reclaimed references to trigger an internal compact step + * (which is normally only triggered when an item is manually removed). + * + * SparseArrays map integers to Objects. Unlike a normal array of Objects, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Integers to Objects. + */ +@SuppressWarnings("unchecked") +public class SparseWeakArray<E> { + + private static final Object DELETED_REF = new Object(); + private static final WeakReference<?> DELETED = new WeakReference(DELETED_REF); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public SparseWeakArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public SparseWeakArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new int[initialCapacity]; + mValues = new WeakReference[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or <code>null</code> + * if no such mapping has been made. + */ + public E get(int key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(int key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED || mValues[i].get() == null) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i].get(); + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(int)}. + */ + public void remove(int key) { + delete(key); + } + + /** + * Removes the mapping at the specified index. + */ + public void removeAt(int index) { + if (mValues[index] != DELETED) { + mValues[index] = DELETED; + mGarbage = true; + } + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + int[] keys = mKeys; + WeakReference<?>[] values = mValues; + + for (int i = 0; i < n; i++) { + WeakReference<?> val = values[i]; + + // Don't keep any non DELETED values, but only the one that still have a valid + // reference. + if (val != DELETED && val.get() != null) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = new WeakReference(value); + } else { + i = ~i; + + if (i < mSize && (mValues[i] == DELETED || mValues[i].get() == null)) { + mKeys[i] = key; + mValues[i] = new WeakReference(value); + return; + } + + if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + WeakReference<?>[] nvalues = new WeakReference[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = new WeakReference(value); + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public int keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index].get(); + } + + /** + * Given an index in the range <code>0...size()-1</code>, sets a new + * value for the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = new WeakReference(value); + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i].get() == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + WeakReference<?>[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + WeakReference<?>[] nvalues = new WeakReference[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = new WeakReference(value); + mSize = pos + 1; + } + + private boolean hasReclaimedRefs() { + for (int i = 0 ; i < mSize ; i++) { + if (mValues[i].get() == null) { // DELETED.get() never returns null. + return true; + } + } + + return false; + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private int[] mKeys; + private WeakReference<?>[] mValues; + private int mSize; +} diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 39676b044001..909605dc55d4 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -170,4 +170,6 @@ public class WifiNative { * @return the event string sent by the supplicant. */ public native static String waitForEvent(); + + public native static void enableBackgroundScan(boolean enable); } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index fc42ab8bdad9..676218e9eb4c 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -112,9 +112,11 @@ public class WifiStateMachine extends HierarchicalStateMachine { private String mLastBssid; private int mLastNetworkId; private boolean mEnableRssiPolling = false; + private boolean mEnableBackgroundScan = false; private int mRssiPollToken = 0; private int mReconnectCount = 0; private boolean mIsScanMode = false; + private boolean mScanResultIsPending = false; private boolean mBluetoothConnectionActive = false; @@ -300,6 +302,8 @@ public class WifiStateMachine extends HierarchicalStateMachine { static final int CMD_START_WPS = 89; /* Set the frequency band */ static final int CMD_SET_FREQUENCY_BAND = 90; + /* Enable background scan for configured networks */ + static final int CMD_ENABLE_BACKGROUND_SCAN = 91; /* Commands from/to the SupplicantStateTracker */ /* Reset the supplicant state tracker */ @@ -823,6 +827,10 @@ public class WifiStateMachine extends HierarchicalStateMachine { sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0)); } + public void enableBackgroundScan(boolean enabled) { + sendMessage(obtainMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0)); + } + public void enableAllNetworks() { sendMessage(CMD_ENABLE_ALL_NETWORKS); } @@ -1538,6 +1546,9 @@ public class WifiStateMachine extends HierarchicalStateMachine { case CMD_ENABLE_RSSI_POLL: mEnableRssiPolling = (message.arg1 == 1); break; + case CMD_ENABLE_BACKGROUND_SCAN: + mEnableBackgroundScan = (message.arg1 == 1); + break; /* Discard */ case CMD_LOAD_DRIVER: case CMD_UNLOAD_DRIVER: @@ -1973,6 +1984,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { eventLoggingEnabled = false; setScanResults(WifiNative.scanResultsCommand()); sendScanResultsAvailableBroadcast(); + mScanResultIsPending = false; break; case CMD_PING_SUPPLICANT: boolean ok = WifiNative.pingCommand(); @@ -2180,6 +2192,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { case CMD_START_SCAN: eventLoggingEnabled = false; WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + mScanResultIsPending = true; break; case CMD_SET_HIGH_PERF_MODE: setHighPerfModeEnabledNative(message.arg1 == 1); @@ -2681,8 +2694,8 @@ public class WifiStateMachine extends HierarchicalStateMachine { * back to CONNECT_MODE. */ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); - WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); - break; + /* Have the parent state handle the rest */ + return NOT_HANDLED; /* Ignore connection to same network */ case CMD_CONNECT_NETWORK: int netId = message.arg1; @@ -2771,21 +2784,35 @@ public class WifiStateMachine extends HierarchicalStateMachine { } class DisconnectedState extends HierarchicalState { + private boolean mAlarmEnabled = false; @Override public void enter() { if (DBG) Log.d(TAG, getName() + "\n"); EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - /** - * In a disconnected state, an infrequent scan that wakes - * up the device is needed to ensure a user connects to - * an access point on the move + /* + * We initiate background scanning if it is enabled, otherwise we + * initiate an infrequent scan that wakes up the device to ensure + * a user connects to an access point on the move */ - long scanMs = Settings.Secure.getLong(mContext.getContentResolver(), + if (mEnableBackgroundScan) { + /* If a regular scan result is pending, do not initiate background + * scan until the scan results are returned. This is needed because + * initiating a background scan will cancel the regular scan and + * scan results will not be returned until background scanning is + * cleared + */ + if (!mScanResultIsPending) { + WifiNative.enableBackgroundScan(true); + } + } else { + long scanMs = Settings.Secure.getLong(mContext.getContentResolver(), Settings.Secure.WIFI_SCAN_INTERVAL_MS, DEFAULT_SCAN_INTERVAL_MS); - mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, + mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + scanMs, scanMs, mScanIntent); + mAlarmEnabled = true; + } } @Override public boolean processMessage(Message message) { @@ -2800,6 +2827,10 @@ public class WifiStateMachine extends HierarchicalStateMachine { transitionTo(mScanModeState); } break; + case CMD_ENABLE_BACKGROUND_SCAN: + mEnableBackgroundScan = (message.arg1 == 1); + WifiNative.enableBackgroundScan(mEnableBackgroundScan); + break; /* Ignore network disconnect */ case NETWORK_DISCONNECTION_EVENT: break; @@ -2808,6 +2839,20 @@ public class WifiStateMachine extends HierarchicalStateMachine { setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state)); /* DriverStartedState does the rest of the handling */ return NOT_HANDLED; + case CMD_START_SCAN: + /* Disable background scan temporarily during a regular scan */ + if (mEnableBackgroundScan) { + WifiNative.enableBackgroundScan(false); + } + /* Handled in parent state */ + return NOT_HANDLED; + case SCAN_RESULTS_EVENT: + /* Re-enable background scan when a pending scan result is received */ + if (mEnableBackgroundScan && mScanResultIsPending) { + WifiNative.enableBackgroundScan(true); + } + /* Handled in parent state */ + return NOT_HANDLED; default: return NOT_HANDLED; } @@ -2817,7 +2862,14 @@ public class WifiStateMachine extends HierarchicalStateMachine { @Override public void exit() { - mAlarmManager.cancel(mScanIntent); + /* No need for a background scan upon exit from a disconnected state */ + if (mEnableBackgroundScan) { + WifiNative.enableBackgroundScan(false); + } + if (mAlarmEnabled) { + mAlarmManager.cancel(mScanIntent); + mAlarmEnabled = false; + } } } |